3
votes

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) Here are the detailed steps:

  1. Creating keystore, private key and certificate

    keytool -genkey -alias ssl_key -keyalg RSA -keypass passwd123 -keystore keystore.jks -storepass passwd123
    
  2. Exporting certificate out from keystore to .cer file

    keytool -export -alias ssl_key -file ssl_key.cer -keystore keystore.jks -storepass passwd123
    
  3. Exporting certificate from keystore to truststore

    keytool -import -v -trustcacerts -alias ssl_key -keypass passwd123 -file ssl_key.cer -keystore truststore.jks -storepass passwd123
    
  4. Exported keystore from keytool's proprietary format to standard format

    keytool -importkeystore -srckeystore keystore.jks -destkeystore stdkeystore.p12 -deststoretype PKCS12 -srcalias ssl_key -deststorepass passwd123 -destkeypass passwd123
    
  5. Exported certificate from standard format keystore using openssl

    openssl pkcs12 -in stdkeystore.p12 -nokeys -out cert.pem
    
  6. Exported unencrypted private key from standard format keystore using openssl as follows:

    openssl pkcs12 -in stdkeystore.p12  -nodes -nocerts -out key.pem
    

My small python server and java client are as follows:

server.py

import socket
import ssl

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)

    while True:
        try:
            print("Awaiting connection...")
            data_socket, client_address = socketObj.accept()
            ssl_socket = ssl.wrap_socket(socketObj,
                            server_side = True,
                            certfile="cert.pem",
                            keyfile="key.pem")        

            reading_status = True
            data = bytearray()
            data_decoded = ""
            while(reading_status):
                message_chunks = data_socket.recv(90000)
                data.extend(message_chunks)

                print(data)          
                data_decoded = str(data.decode("utf-8"))
                #data_decoded = str(data.decode())

                if(data_decoded.startswith("<<startMessage>>")):
                    if(data_decoded.endswith("<<endMessage>>")):
                        reading_status = False
                        print(data_decoded);

        except socket.error as msg:
            print (msg)

    ssl_socket.shutdown(socket.SHUT_RDWR)
    ssl_socket.close()

if __name__ == '__main__':
    start_server_ssl()

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 the client is as follows:

Awaiting connection...
bytearray(b'\x16\x03\x03\x00\xc1\x01\x00\x00\xbd\x03\x03["}o\x12\x01\xb2\xd4E\xa1\x1fy\xa8/d\x11\xd2\x00)\\t\x9a:\xb6\n\xcd\x03\x05\xbe\xe58\xd6\x00\x00:\xc0#\xc0\'\x00<\xc0%\xc0)\x00g\x00@\xc0\t\xc0\x13\x00/\xc0\x04\xc0\x0e\x003\x002\xc0+\xc0/\x00\x9c\xc0-\xc01\x00\x9e\x00\xa2\xc0\x08\xc0\x12\x00\n\xc0\x03\xc0\r\x00\x16\x00\x13\x00\xff\x01\x00\x00Z\x00\n\x004\x002\x00\x17\x00\x01\x00\x03\x00\x13\x00\x15\x00\x06\x00\x07\x00\t\x00\n\x00\x18\x00\x0b\x00\x0c\x00\x19\x00\r\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x02\x00\x12\x00\x04\x00\x05\x00\x14\x00\x08\x00\x16\x00\x0b\x00\x02\x01\x00\x00\r\x00\x18\x00\x16\x06\x03\x06\x01\x05\x03\x05\x01\x04\x03\x04\x01\x03\x03\x03\x01\x02\x03\x02\x01\x02\x02')
Traceback (most recent call last):
  File "D:\Mahesh\workspaces\workspace6\PythonServer\server.py", line 44, in <module>
    start_server_ssl()
  File "D:\Mahesh\workspaces\workspace6\PythonServer\server.py", line 29, in start_server_ssl
    data_decoded = str(data.decode("utf-8"))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 4: invalid start byte

I uncommented utf-8 encoding-decoding lines in client and server code and ended up getting following output on server's console:

Awaiting connection...
bytearray(b'\x16\x03\x03\x00\xc1\x01\x00\x00\xbd\x03\x03["\x7f>s\x04\x15S\x14\xb0\xa2\xa0\x7f\x90}@\xe8\xa3:\x15\x04z\xeb\x986\\b\xe9\xe0v=\x1f\x00\x00:\xc0#\xc0\'\x00<\xc0%\xc0)\x00g\x00@\xc0\t\xc0\x13\x00/\xc0\x04\xc0\x0e\x003\x002\xc0+\xc0/\x00\x9c\xc0-\xc01\x00\x9e\x00\xa2\xc0\x08\xc0\x12\x00\n\xc0\x03\xc0\r\x00\x16\x00\x13\x00\xff\x01\x00\x00Z\x00\n\x004\x002\x00\x17\x00\x01\x00\x03\x00\x13\x00\x15\x00\x06\x00\x07\x00\t\x00\n\x00\x18\x00\x0b\x00\x0c\x00\x19\x00\r\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x02\x00\x12\x00\x04\x00\x05\x00\x14\x00\x08\x00\x16\x00\x0b\x00\x02\x01\x00\x00\r\x00\x18\x00\x16\x06\x03\x06\x01\x05\x03\x05\x01\x04\x03\x04\x01\x03\x03\x03\x01\x02\x03\x02\x01\x02\x02')
Traceback (most recent call last):
  File "D:\Mahesh\workspaces\workspace6\PythonServer\server.py", line 44, in <module>
    start_server_ssl()
  File "D:\Mahesh\workspaces\workspace6\PythonServer\server.py", line 30, in start_server_ssl
    data_decoded = str(data.decode())
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 4: invalid start byte

I feel I am missing something. Either some basics of SSL or encoding. Can I use pem file generated from java keystore as explained above directly with python SSLSocket? Or do I have to ensure something more like ciphers used to generate keys and certificate?

Any links/resources to dispel my ignorance are welcome...

1

1 Answers

0
votes

I replaced your server.py with this example. There is nothing wrong with your server about keys and certificates.

server.py

import socket
from socket import AF_INET, SOCK_STREAM, SO_REUSEADDR, SOL_SOCKET, SHUT_RDWR
import ssl

KEYFILE = 'key.pem'
CERTFILE = 'cert.pem'

def print_data(s):
    while True:
        data = s.recv(8192)
        print(data.decode("utf-8"))
        if data == b'':
            break
        s.send(b'This is a response.')
        print('Connection closed')
    s.close()

def start_server_ssl(address):
    s = socket.socket(AF_INET, SOCK_STREAM)
    s.bind(address)
    s.listen(1)
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

    ssl_socket = ssl.wrap_socket(s, keyfile=KEYFILE, certfile=CERTFILE, server_side=True)
    print("Awaiting connection...") 

    while True:
        try:
            (c,a) = ssl_socket.accept()
            print('Got connection', c, a)
            print_data(c)
        except socket.error as e:
            print('Error: {0}'.format(e))

start_server_ssl((socket.gethostbyname('localhost'), 6000))