ITU-T X.509 certificates have many uses in computing, and are of special interest in integration applications, where they are useful in helping to authenticate partners in a data exchange, to preserve the confidentiality, and to certify the authenticity of the data being exchanged. Most notably, the TLS transport layer protocol makes use of public key infrastructures based on X.509, but so does WS-Security; both are fundamental components of data flow security in common integration scenarios.
X.509 is focused on public-key cryptography, where a public and a private keypair define two transformations, one of which is the inverse of the other. The power of the cryptographic transformations relies heavily on the difficulty of computing the private key from the public key and vice versa; however, apart from the potential differences in how the keys themselves are distributed, there is no conceptual difference between the transformations that they represent. In a public key infrastructure it is normally assumed that the private key is not redistributed while the public key may be; for convenience, private keys are generally stored together with their respective public key counterparts. In this article, the term ‘private key’ will often refer to not only the private key itself, but rather a corresponding public/private keypair that is kept together in an appropriate digital container such as PKCS #12.
The transformations defined by the keys can be used for various purposes as mentioned above; for example, TLS uses public-key cryptography for authentication and as part of the Diffie-Hellman Key Agreement Method that is used to establish a shared secret for encrypting and decrypting the transport payload using symmetric-key cryptography.
Figure 1. shows some of the most important components of how a certification scenario involving trusted parties may be implemented using an X.509 public key infrastructure and JKS key and trust stores. The JKS format is a standard container used in Java. The X.509 certificates involved are essentially representations of a public key stored together with the Distinguished Name of the entity to whom the key belongs to (the subject), plus the verifiable signature of a trusted third party (the issuer) certifying the fact that the DN in the certificate is an accurate representation of the entity holding the private key.
|Fig. 1: Certificate chaining in JKS containers|
Note that key stores and trust stores may contain multiple entries, but for simplicity, we are only concerned with one in each. Specifically,
entry 1of the trust store in Figure 1 contains a single certificate, whereas
entry kof the key store contains a private key, its corresponding certificate signed by Carol, and two other certificates necessary for establishing a certification path to Alice — all under a single key store entry. The three certificates have been chained together in the correct order.
The example illustrates a server-authentication scenario, where a server operated by Dan is challenged by a client. Alice, a “trust anchor”, has been issued a certificate by an unknown entity (possibly Alice herself), and this certificate has been placed in a specially designated location called a “trust store” in the client, representing the fact that the client trusts Alice and all certificates issued by her. The server has a key store, and in response to the client’s challenge, it may present some of the certificates stored in there as evidence of being a trusted server. In this case, it can send to the client the certificate issued by Alice to Bob, the one issued by Bob to Carol, and the one issued by Carol to Dan, who is operating the server. The demonstrated existence of the Alice-Bob-Carol-Dan certification path proves through the implicit transitivity of the trust relationship that the client can trust Dan (see also X.509 Section 7.7, RFC 5280 Section 6, and Understanding Certification Path Construction). The only fact that still needs to be established at this point is whether the server is indeed operated by Dan, but this is easy: the client can challenge the server to prove that it has Dan’s private key, and after the establishment of this fact, the client-server interaction can proceed as planned.
In this post, we describe a way to create public key infrastructures based on X.509 for testing integration applications during development. Note that commercial certificate authorities are subject to strict requirements that these methods, as described, may not fulfill; therefore, we must emphasize that certificates created this way are generally not suitable for production environments. However, they are based on the same technology and can be used as substitutes for their commercial equivalents in isolated test environments.
We use OpenSSL, the latest version of which is 1.0.2g at the time of writing, to perform most of the functions described here. The same version of OpenSSL was used to test the examples given here. Some additional Java-specific functionality is achieved using the
keytoolexecutable that ships with IBM® Java Developer Kits and which is also bundled with a number of IBM products, such as IBM® Integration Bus.
Creating a Root Certificate Authority
We start off by creating everything that is needed to operate a Root Certificate Authority (CA) that we can later use to sign certificate signing requests (CSRs). By definition, a root CA uses a self-signed certificate to certify other entities. Note that this does not exclude (mutual) cross-certification, where other certificate authorities can also validate the same root certificate, or other complex certification scenarios; however, the certificate chain typically terminate in a root CA.
The very first step is to create a public/private keypair to form the basis of the Root CA. The private key of this keypair is the ultimate secret, the exposure of which results in the compromise of all certificates signed by the root CA based on this key. To create an RSA private key with a length of 2048 bits, then store it in an unencrypted file called
rootkey.key(which will also include the corresponding public key), issue the following command:
openssl genpkey -algorithm RSA -out rootkey.key -pkeyopt rsa_keygen_bits:2048
Please refer to the genpkey page of the OpenSSL manual for further information on the
openssl genpkeycommand. To produce the root certificate, we need this key to be certified by an authority. The certification will be done by the same entity that issued the key. That the resulting certificate is self-signed will be evident from the fact that the issuer and the signer are the same.
As the next step, let us create a Certificate Signing Request from our
rootkey.keykey file using the OpenSSL req command, and store it in the
rootreq.csrfile in the PEM format. A similar request, which essentially contains our public key augmented with an X.500 Distinguished Name, but not the private key, could be sent to a commercial certificate authority for signing:
openssl req -new -key rootkey.key -out rootreq.csr -config rootcert.cnf
The Distinguished Name is taken from the
[req] distinguished_name = req_distinguished_name prompt = no [req_distinguished_name] C = GB ST = Hampshire L = Hursley O = IBM UK Ltd. OU = MQESB CN = Test Root CA emailAddress = email@example.com
The final step of creating a Root CA is to comply with the request, and sign the CSR to produce a certificate. This can be done with the OpenSSL x509 command as follows:
openssl x509 -req -days 7305 -in rootreq.csr -signkey rootkey.key -out rootcert.crt
Here we sign the CSR with our own private key to produce a certificate with a validity period of 7305 days, which we store in the
rootcert.crtfile in the PEM format. Note that in this special case, as we are creating a self-signed certificate, the signing key happens to be the same key as the one that issued the CSR. The
signkeyargument is only applicable to self-signed certificates; other types are created differently as we will see below. The period of 7305 days corresponds to 20 years, assuming that 5 leap days occur during this time.
We now have everything that is needed to sign certficates: the
rootkey.keypublic/private keypair, and the
rootcert.crtcertificate, which contains the public key of our Root CA as well as its Distinguished Name. One may display a human-readable extract of the certificate using the OpenSSL x509 command as follows. Note that the issuer and the subject are the same.
openssl x509 -in rootcert.crt -noout -text -nameopt multiline
Signing certificates with our new Root CA
The first step of creating a certificate is no different from creating the Root CA itself; we need a public/private keypair that we can use for encryption and decryption, and that can be certified by a CA. We are using the OpenSSL genpkey command in the same way as we did when creating the Root CA, except that we are saving the keypair in a file called
openssl genpkey -algorithm RSA -out mykey.key -pkeyopt rsa_keygen_bits:2048
Likewise, the second step of creating a CSR is rather similar; however, we are using a different configuration file this time:
openssl req -new -key mykey.key -out myreq.csr -config mycert.cnf
mycert.cnffile specifies the following Distinguished Name:
[req] distinguished_name = req_distinguished_name prompt = no [req_distinguished_name] C = GB ST = Hampshire L = Hursley O = IBM UK Ltd. OU = MQESB CN = earth.hursley.ibm.com emailAddress = firstname.lastname@example.org
Signing the certificate, however, happens differently from the earlier case. We use the x509 command with different parameters to emulate the behavior of a CA. We must supply our CSR, along with the signing certificate (
rootcert.crt) and its corresponding private key (
rootkey.key). The CA also keeps track of a serial number that we keep in
-CAcreateserialargument tells OpenSSL to create this file if it does not already exist. The resulting signed certificate is then exported to the
mycert.crtfile, in the PEM format.
openssl x509 -req -days 7305 -in myreq.csr -CA rootcert.crt -CAkey rootkey.key -CAserial rootcert.srl -CAcreateserial -out mycert.crt
As an unencrypted file is not an appropriate container for storing a public/private keypair, also, the unencrypted storage method that we have been using so far does not include the DN of the entity represented by the keypair, so we need to export the key to a PKCS #12 container for further use as follows:
openssl pkcs12 -export -in mycert.crt -inkey mykey.key -out mycert.p12 -password pass:P4s5w0rd -name mykey
At this point, we have a certificate signed by out Root CA. The certificate is stored on its own in the
mycert.crtfile, while the password-protected
mycert.p12file includes the corresponding private key as well.
In theory, we could use the private key of this newly signed certificate to sign further certificates, i.e., we could act as a non-root CA and create chains of certificates similar to the one shown in Figure 1. In practice, however, non-root CA certificates created this way can only be used as CAs if the certificate itself contains an X.509 Version 3 field called
basicConstraints(see X.509 Section 126.96.36.199) set to a value expressly allowing such use. To create certificates that have this extension, the OpenSSL x509 -req commands that are used for signing the certificates need to be instructed to import the extension from a file:
openssl x509 -req -days 7305 -in myreq.csr -CA rootcert.crt -CAkey rootkey.key -CAserial rootcert.srl -CAcreateserial -out mycert.crt -extensions v3_ca -extfile ca.cnf
ca.cnffile should define the above referenced
v3_caextension in a manner similar to the following:
[ v3_ca ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always basicConstraints=CA:true
Non-root CA certificates issued this way can also be used to sign public keys and allow the creation of longer certificate chains that can be of particular interest to testing integration applications.
Packaging keys and certificates in JKS key stores
To allow the keys and certificates created above to be used with a wide range of Java-based products, including IBM Integration Bus, it can be useful to package them in the Java native key store format, JKS. The keytool command is included in IBM Java implementations for this purpose.
A key entry along with its certificate counterpart can be exported from a PKCS #12 container to a JKS file entry using
keytool -importkeystore -srckeystore mycert4.p12 -srcstorepass P4s5w0rd -srcstoretype PKCS12 -srcalias mykey4 -destkeystore keystore.jks -deststorepass P4s5w0rd -deststoretype JKS -destalias localhostcert
Note that the X.509 certificate corresponding to the private key is automatically imported from the PKCS #12 container, so if the only certificate required in the key store is the one belonging to the private key, no further action is necessary.
To build a key store entry that contains several chained certificates in addition to a private key corresponding to the first certificate of the chain, similar to the one shown in Figure 1, first concatenate the PEM representations of the certificates to form a certificate chain (Windows-specific example):
copy /b mycert.crt localhostcert.arm copy /b localhostcert.arm+mycert2.crt localhostcert.arm copy /b localhostcert.arm+mycert3.crt localhostcert.arm copy /b localhostcert.arm+mycert4.crt localhostcert.arm
The resulting certificate chain can be appended to a private key entry already in the key store by specifying the alias of the existing key (see Importing the Certificate Reply from the CA)
keytool -importcert -noprompt -keystore keystore.jks -file localhostcert.arm -alias localhostcert -storepass P4s5w0rd -rfc
In the above example,
mycertwas signed by
mycert3, enabling Java to verify the authenticity of the
mykey4key stored in the key store using the
To insert a single certificate as a separate entry in a JKS container (see also Importing a Certificate for the CA), issue
keytool -importcert -noprompt -keystore truststore.jks -file rootcert.crt -alias rootcert -storepass P4s5w0rd -rfc
Finally, to list the entries in a JKS container (key store or trust store), use the list command:
keytool -list -keystore keystore.jks -storepass P4s5w0rd
The resulting key store and trust store can be imported into IBM Integration Bus using the mqsichangeproperties command in the manner described under Setting up a public key infrastructure in the IBM Integration Bus Version 10.0 Knowledge Center.