I am trying to use keys and certificate generated using java keytool
in python server and java client. I have created key & keystore, exported certificate, added certificate to truststore, converted keystore to standard pkcs format and then extracted key and certificate from pkcs to use in python server. (first three steps from here and last three steps from here). This question is somewhat follow up of this question and detailed steps for generating key and certificate can be found in that question.
My SSL server looks like this
server.py
import socket
import multiprocessing as mup
import ssl
def worker_ssl(data_socket, client_address):
print("Inside worker")
#some processing
def start_server_ssl():
socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 6000)
socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
socketObj.bind(server_address)
socketObj.listen(10)
ssl_socket = ssl.wrap_socket(socketObj,
server_side = True,
certfile="cert.pem",
keyfile="key.pem")
while True:
print("Waiting For Connections .........\n")
try:
data_socket, client_address = ssl_socket.accept()
process = mup.Process(target=worker_ssl, args=(data_socket, client_address))
process.daemon = True
process.start()
except socket.error as msg:
print (" [ERROR] : %s " % msg)
continue
socketObj.close()
ssl_socket.shutdown(socket.SHUT_RDWR)
ssl_socket.close()
if __name__ == '__main__':
start_server_ssl()
SSL Client looks like this:
Client4Py.java
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class Client4Py {
static KeyStore ks;
static KeyManagerFactory kmf;
static TrustManagerFactory tmf;
static SSLContext sc;
static TrustManager[] trustManagers;
static {
try {
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("D:\\javasslstores\\truststore.jks"), "passwd123".toCharArray());
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "passwd123".toCharArray());
tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sc = SSLContext.getInstance("TLS");
sc.init(null, tmf.getTrustManagers(), null);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getStackTrace());
}
}
public static void main(String[] args) throws IOException {
SSLSocketFactory ssf = sc.getSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket("127.0.0.1", 6000);
socket.startHandshake();
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8)));
//PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
out.println("<<startMessage>>");
out.println("Message from Client4Py");
out.println("<<endMessage>>");
out.flush();
if (out.checkError())
System.out.println(
"SSLSocketClient: java.io.PrintWriter error");
out.close();
socket.close();
}
}
The output on server's console after first running server and then the client is as follows:
1 Waiting For Connections .........
2
3 Traceback (most recent call last):
4 File "D:\workspaces\workspace6\PythonServer\server.py", line 40, in <module>
5 start_server_ssl()
6 File "D:\workspaces\workspace6\PythonServer\server.py", line 29, in start_server_ssl
7 process.start()
8 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\process.py", line 105, in start
9 self._popen = self._Popen(self)
10 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 223, in _Popen
11 return _default_context.get_context().Process._Popen(process_obj)
12 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 322, in _Popen
13 return Popen(process_obj)
14 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
15 reduction.dump(process_obj, to_child)
16 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 60, in dump
17 ForkingPickler(file, protocol).dump(obj)
18 File "D:\Programs\python\python-3.6.6-amd64\lib\socket.py", line 185, in __getstate__
19 raise TypeError("Cannot serialize socket object")
20 TypeError: Cannot serialize socket object
21 Traceback (most recent call last):
22 File "<string>", line 1, in <module>
23 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\spawn.py", line 99, in spawn_main
24 new_handle = reduction.steal_handle(parent_pid, pipe_handle)
25 File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 87, in steal_handle
26 _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
27 PermissionError: [WinError 5] Access is denied
You can see on line 20, there is TypeError: Cannot serialize socket object
.
Code starts working after removing all SSL stuffs
When I comment call to wrap_socket()
, replace ssl_socket.accept()
with socketObject.accept()
and comment ssl_socket.shutdown()
and close()
, the code starts working. It creates new process from worker()
as desired and ends printing Inside worker
on console. Here is the modified non SSL server:
import socket
import multiprocessing as mup
# import ssl
def worker_ssl(data_socket, client_address):
print("Inside worker")
#some processing
def start_server_ssl():
socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 6000)
socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
socketObj.bind(server_address)
socketObj.listen(10)
# ssl_socket = ssl.wrap_socket(socketObj,
# server_side = True,
# certfile="cert.pem",
# keyfile="key.pem")
while True:
print("Waiting For Connections .........\n")
try:
data_socket, client_address = socketObj.accept()
process = mup.Process(target=worker_ssl, args=(data_socket, client_address))
process.daemon = True
process.start()
except socket.error as msg:
print (" [ERROR] : %s " % msg)
continue
socketObj.close()
# ssl_socket.shutdown(socket.SHUT_RDWR)
# ssl_socket.close()
if __name__ == '__main__':
start_server_ssl()
I dont know whether I understood the exact reason, but I am stating it here.
Looking at following stacktrace lines:
it seems that
multiprocessing
module'sProcess.start()
method serializes the parameters passed to it for passing them to new process. And it further seems thatSSLSocket
objects cannot be serialized. However, it seems thatSocket
objects can be serialized. This issue states the same and thats why theSocket
based server works. However, I dont get why is this so (SSLSocket
is not serializable butSocket
objects can be serialized). I mean is there any method that is implemented bySocket
objects which are not implemented bySSLSocket
. Also note that the error occurs atsocket.py, line 185, in __getstate__
, that is inSocket
class, but not inSSLSocket
class.I will like if someone confirms above reason (that is
SSLSocket
objects are not serializable andSocket
objects are serializable), explains exactly why is this the case and provides solution for usingSSLSocket
withmultiprocessing
module.Workaround
I ended up forking new process using
os.fork
. (Examples ofos.fork
can be found here). Its supported on linux only. So, I installed python on cygwin and then used it.