A while back I was setting up client certificates for a server and ran into some issues, apparently caused by cURL not playing nicely with OS X. Like most software developers, I think, my experience with cryptography is mainly limited to things like installing certificates, so when things didn’t work as expected, I didn’t know where to start looking.
In the process of figuring out what was wrong, I ended up skimming through Implementing SSL / TLS Using Cryptography and PKI by Joshua Davies. I may be a layperson when it comes to cryptography, but to me it looks like a great book for those who want to understand the concepts and implementation of modern cryptography. It is however also a fairly long book, so I thought I’d summarize the main concepts. Hopefully someone finds this useful.
The question that seems like the starting point of cryptography seems to be…
How can we transmit messages that may be intercepted by third parties, so that only the recipient can read them?
The initial answer seems simple.
We can encrypt messages by scrambling the characters in a reversible way (symmetric cryptography). For instance, if we right-shift with the key
ifmmp, and so on. This isn’t a good algorithm, but it illustrates the general idea.
But now that we’ve encrypted the message…
…how do we transmit the key securely?
Encrypting the message isn’t useful if the key is intercepted. In ancient times the key could have been exchanged in person, but that’s not practical when it comes to the internet.We also can’t simply encrypt the key using the same method used to encrypt the message, since that would simply result in a new key that would have to be exchanged.
The current solution is to use two keys referred to as the private and public keys, composed of numbers that are related in such a way that a message encrypted with either key, can be decrypted by the other. The key referred to as the public key is made available to the public, while the private key is kept secret.
To transmit a message securely, such as a key to be used in symmetric cryptography (or any communication), one party can use the public key of another party to encrypt and transmit a message (such as a key) to that party, who can then decrypt the message using their private key.
But now that the communication is encrypted…
…how do we know that messages we receive are from the corresponding private key holder?
Let’s say an army unit receives an order to lay down their arms, encrypted with the unit’s public key and therefore (probably) not intercepted by a third party. What if the message wasn’t intercepted by a third party, but was sent by third party? The unit may be able to transmit messages securely using the expected recipients public key, but how do they know the incoming messages are from the holder of the corresponding private key?
The solution is for the private key holder to calculate a value called a message digest for any message they are about to send, encrypt that value using their private key and send it along with the message.
Remember that messages encrypted with either key can be decrypted by the other.
Since the receiver has the corresponding public key, they can calculate the message digest using the same algorithm as the sender and decrypt the message digest received from the sender using the public key of the sender. If the received and calculated values match, they know the message was sent by someone with access to the private key and that it was not modified in transit.
But even if the message was sent by the private key holder…
…what if the malicious third party is actually the private key holder and not whoever we think they are?
For instance, a request from a client to a bank is intercepted by a third party who sends the client their public key instead of the public key of the bank, while relaying requests to the bank.
The solution is to give the public key an identity in the form of a certificate, verifiable by a trusted party called an intermediary, or certificate authority (CA). The certificate is created by combining the public key with identifying information such as a DNS name. This certificate is signed using the process described above, where the trusted party who knows the identity of the party we are communicating with, calculates a message digest from the certificate and signs it by encrypting it with their private key, which we in turn can decrypt using the their public key.
So let’s say that we’ve generated or received the necessary certificates…
…what do we actually do with the certificates once we have them?
Well there are lots of ways of handling certificates. In my case, my client had a Spring project deployed to Tomcat on Debian and three PEM-files that were sent to me that were supposed to be used directly by the Tomcat server: client-key.pem, client-cert.pem, server-ca.pem.
The standard that describes the certificates used by TLS/SSL is called X.509 and is stored in a structure described in ASN.1, functionally comparable to XML or JSON.
The binary representation is encoded using Distinguished Encoding Rules (DER).
These PEM-files are base64-encoded DER-files with the addition of a header, footer and label. E.g.: “BEGIN / END + CERTIFICATE / CERTIFICATE REQUEST / PRIVATE KEY.
The private key can be combined with its public key certificates (in this case, client-key.pem and client-cert.pem) into a PKCS #12 file (Public Key Cryptography Standards), which can be imported into a Java keystore, which will contain the servlet containers certificate.
openssl pkcs12 -export -out new-file.pkcs12 -in client-cert.pem -inkey client-key.pem keytool -importkeystore -srckeystore new-file.pkcs12 -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype jks -deststorepass <password>
The servers certificate (server-ca.pem) can be converted to DER-format and then imported into an existing or new truststore containing the servlet container’s trusted certificates.
openssl x509 -in server-ca.pem -out server-ca.der -outform der keytool -importcert -file server-ca.pem -alias SomeAlias -keystore path/to/cacerts -storepass <password>
Now that we have the files we need…
…how do we make our Tomcat installation actually use the certificates?
While developing, I run the embedded Tomcat server from IntelliJ, where the following command line options can be added under “Run/Debug Configurations” > “VM options”:
-Djavax.net.ssl.keyStore=path/to/keystore.jks -Djavax.net.ssl.keyStorePassword=<password> -Djavax.net.ssl.keyStoreType=JKS -Djavax.net.ssl.keyStoreProvider=SUN -Djavax.net.ssl.trustStore=path/to/cacerts.jks -Djavax.net.ssl.trustStorePassword=<password> -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStoreProvider=SUN
For this particular project, the same options were added to the file bin/setenv.sh in the the Tomcat installation directory.
export CATALINA_OPTS=" -Djavax.net.ssl.keyStore=path/to/keystore.jks -Djavax.net.ssl.keyStorePassword=<password> -Djavax.net.ssl.keyStoreType=JKS -Djavax.net.ssl.keyStoreProvider=SUN -Djavax.net.ssl.trustStore=path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=<password> -Djavax.net.ssl.trustStoreType=JKS