So, I've been trying to set up an SSL connection between a python client and python server in which both have separate certificates to validate each-other, and both certificates are signed by one CA (which also happens to be the root CA). This should make them both valid to each-other, correct?
My methodology thus far has been to create a bash script that does it all:
#!/bin/bash
BOLD=$(tput bold)
CLEAR=$(tput sgr0)
echo -e "${BOLD}Generating RSA AES-256 Private Key for Root Certificate Authority${CLEAR}"
openssl genrsa -aes256 -out Root.CA.example.llc.key 4096
echo -e "${BOLD}Generating Certificate for Root Certificate Authority${CLEAR}"
openssl req -x509 -new -nodes -key Root.CA.example.llc.key -sha256 -days 1825 -out Root.CA.example.llc.pem
echo -e "${BOLD}Generating RSA Private Key for Server Certificate${CLEAR}"
openssl genrsa -out server/example.llc.server.key 4096
echo -e "${BOLD}Generating Certificate Signing Request for Server Certificate${CLEAR}"
openssl req -new -key server/example.llc.server.key -out server/example.llc.server.csr
echo -e "${BOLD}Generating Certificate for Server Certificate${CLEAR}"
openssl x509 -req -in server/example.llc.server.csr -CA Root.CA.example.llc.pem -CAkey Root.CA.example.llc.key -CAcreateserial -out server/example.llc.server.crt -days 1825 -sha256 -extfile server/example.llc.server.ext
echo -e "${BOLD}Generating RSA Private Key for Client Certificate${CLEAR}"
openssl genrsa -out client/example.llc.client.key 4096
echo -e "${BOLD}Generating Certificate Signing Request for Client Certificate${CLEAR}"
openssl req -new -key client/example.llc.client.key -out client/example.llc.client.csr
echo -e "${BOLD}Generating Certificate for Client Certificate${CLEAR}"
openssl x509 -req -days 1825 -in client/example.llc.client.csr -CA Root.CA.example.llc.pem -CAkey Root.CA.example.llc.key -set_serial 01 -out client/example.llc.client.crt
echo "Done!"
The server loads these credentials:
cntx = SSL.Context(...)
# ...
cntx.use_privatekey_file('example.llc.server.key')
cntx.use_certificate_file('example.llc.server.cert')
cntx.load_verify_locations('../Root.CA.example.llc.pem')
The client loads these credentials:
cntx = SSL.Context(...)
# ...
cntx.use_privatekey_file('example.llc.client.key')
cntx.use_certificate_file('example.llc.client.cert')
cntx.load_verify_locations('../Root.CA.example.llc.pem')
The server launches like a rocket
server = SSL.Connection(cntx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
server.bind(('localhost', 44000))
server.listen(1)
server.setblocking(1)
cli, addr = server.accept()
and the client attempts to connect with
SSL.Connection(cntx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sock.connect(('localhost', 44000))
then I get hit with this madness:
OpenSSL.SSL.Error: [('SSL routines', 'ssl3_read_bytes', 'tlsv1 alert unknown ca')]
At this point, I'm not sure if the client and server certs should be signed by the root CA, if the client cert should be signed by the server cert, or if I'm going insane with all this certificate trust chain SSL/TLS magic wizard voodoo craziness. Please help.
It's technically possible for a TLS certificate to be used as both a server certificate and a client certificate. The TLS certificate for this very site has its key usage set that way, for instance. But the server which requires a client certificate does so to authenticate the client.
To combine them, simply copy the contents inside of the root certificate and paste it into a new line at the bottom of the intermediate certificate file. Once this is done, click File -> Save As and save this new bundle file and ensure to add '. crt' without the quotes at the end of the new filename.
And that's all she wrote!
Steve E. pointed out that the certs needed to be verified and so the culprit was found to be the self-signed client cert.
openssl verify -verbose -CAfile Root.CA.example.llc.pem server/example.llc.server.crt
openssl verify -verbose -CAfile Root.CA.example.llc.pem client/example.llc.client.crt
Here's the new autogen code:
#!/bin/bash
BOLD=$(tput bold)
CLEAR=$(tput sgr0)
iterate=(server/ client/)
for dir in "${iterate[@]}"; do
[[ ! -d "$dir" ]] && mkdir -p "$dir" \
&& echo -e "${BOLD}directory '$dir' was created ${CLEAR}"
done
echo -e "${BOLD}Generating RSA AES-256 Private Key for Root Certificate Authority${CLEAR}"
openssl genrsa -aes256 -out Root.CA.example.llc.key 4096
echo -e "${BOLD}Generating Certificate for Root Certificate Authority${CLEAR}"
openssl req -x509 -new -nodes -key Root.CA.example.llc.key -sha256 -days 1825 -out Root.CA.example.llc.pem
echo -e "${BOLD}Generating RSA Private Key for Server Certificate${CLEAR}"
openssl genrsa -out server/example.llc.server.key 4096
echo -e "${BOLD}Generating Certificate Signing Request for Server Certificate${CLEAR}"
openssl req -new -key server/example.llc.server.key -out server/example.llc.server.csr
echo -e "${BOLD}Generating Certificate for Server Certificate${CLEAR}"
openssl x509 -req -in server/example.llc.server.csr -CA Root.CA.example.llc.pem -CAkey Root.CA.example.llc.key -CAcreateserial -out server/example.llc.server.crt -days 1825 -sha256 -extfile server/example.llc.server.ext
echo -e "${BOLD}Generating RSA Private Key for Client Certificate${CLEAR}"
openssl genrsa -out client/example.llc.client.key 4096
echo -e "${BOLD}Generating Certificate Signing Request for Client Certificate${CLEAR}"
openssl req -new -key client/example.llc.client.key -out client/example.llc.client.csr
echo -e "${BOLD}Generating Certificate for Client Certificate${CLEAR}"
openssl x509 -req -in client/example.llc.client.csr -CA Root.CA.example.llc.pem -CAkey Root.CA.example.llc.key -CAcreateserial -out client/example.llc.client.crt -days 1825 -sha256
echo "Done!"
Good luck and godspeed to anyone who is trying to create a self-signed root CA with double-sided authentication for client/server systems!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With