When a client recently requested secure communication among multiple platform boxes distributed across three continents, I decided to leverage the 100% Java-based security available via Java Secure Socket Extension.
JSSE requires trusted certificates for authentication services, but my client had no Public Key Infrastructure (PKI) in place for certificate generation and distribution. So I built a PKI implementation where my client acted as the certificate authority and then integrated this PKI with J2EE systems via JSSE to provide secure communication services. In this article, I'll demonstrate how you can do the same. (The source code is available below.)
If you already have a PKI in place or have other experience with key-based cryptography, you may want to skip this section. For those new to the subject, the following brief overview provides some of the key terms and acronyms used throughout this article. There's a lot to absorb here, but the actual application of these concepts in the following sections should make matters more clear.
One of the additions to the upcoming release of J2SDK 1.4 will be Java Secure Socket Extension 1.0.2. Currently available as an optional add-on, JSSE is a set of Java packages that enables secure Internet communications via Secure Sockets Layer (SSL) v3 and Transport Layer Security (TLS) 1.0 protocols.
Netscape developed SSL in 1994 and subsequently transferred control of the protocol to the Internet Engineering Task Force. The IETF renamed SSL to Transport Layer Security (TLS), and released their first specification in January 1999. TLS 1.0 is a modest upgrade to the most recent version of SSL, version 3.0, and the differences between the two are minor.
SSL and TLS use public key cryptography to provide authentication, secret key cryptography to provide privacy, and a message authentication code to provide data integrity. While in this article I focus on the use of a key pair for authentication purposes, all these cryptographic processes require only one.
One key in the pair is made public and the other is held strictly private. For authentication purposes, the public key in a key pair is associated with a certificate. A certificate in a PKI implementation is an electronic document used to identify a communicating entity by its association with a public key. Since certificates are used to address the problem of impersonation, their distribution must be governed by a trustworthy entity. These entities are known as certificate authorities (CAs).
When you participate in a client- authenticated TLS or SSL conversation, you receive your counterparticipant's certificate. The certificate specifies the identity of your counterparticipant and guarantees it by providing a certificate authority's signature. The CA in this case acts much like a notary. If you trust the notary, you can trust the certificate. CAs for the uncontrolled communication encouraged by the World Wide Web are typically independent third parties who charge hundreds of dollars for the issuance of a single certificate. This expense is not due to the cost of producing a certificate, but to the costs involved in ensuring that an entity requesting a certificate is in fact who it claims to be.
In an enterprise system where multiple communicating parties are controlled by a single entity, it makes little sense to incur this expense. You can save time and money while maintaining internal control of your PKI by establishing yourself as the CA in your secure communications. So let's get to it.
Becoming a CA
JSSE was not included in Java Development Kits prior to version 1.4, so you may need to install JSSE as an optional extension to your Java platform. Luckily, the JSSE distribution includes explicit installation instructions, making this step a breeze. Next, you'll need an SSL toolkit capable of issuing X.509 certificates. I recommend OpenSSL because it's open source, well documented, and free. There are several other options available including managed services, but I'll be using OpenSSL for demonstration purposes.
Once you have the necessary software installed, the next step is to create your certificate authority private key and certificate. I'll create mine with the single-line command:
openssl req -new -x509 -newkey
The first option "req" signifies that we're performing an X.509 certificate management operation, followed by "new" and "x509" showing that we're creating a new certificate without a certificate request. The "newkey" parameter specifies the type of private key along with its size in bits. The "days" parameter specifies that this certificate will be valid for 10 years, and the "rand" parameter points to a couple of files used to help randomize my key generation.
rsa:2048 -config openssl.cnf -keyout rootCAKey
-out rootCACert -days 3650 -rand
Once you execute this command, you'll be prompted for a pass phrase that will be used to encrypt and decrypt your new private key. When choosing a pass phrase and restricting access to your CA private key, keep in mind that your private key is a cornerstone of your PKI and that its safekeeping is vital. After confirming your pass phrase, you'll be prompted for the attributes of your CA certificate as shown in Listing 1.
OpenSSL generates base64 encoded certificates between "-----BEGIN-----" and "-----END-----" lines by default. This format is commonly but mistakenly referred to as PEM format. True Privacy Enhanced Mail format is actually used by some SSL servers like old Lotus Domino and 4D WebSTAR Server, so I'll refer to the OpenSSL format as OSSL. The JSSE integration tools expect OSSL-formatted certificates. Regardless of which SSL toolkit you used to complete this step, your final product should be an OSSL certificate and a private key. These two components, along with your SSL toolkit, provide all you need. Congratulations, you've successfully become a certificate authority.
Your next step is to tell your JVM that you're a trusted CA so it will inherently trust any certificates you issue. This is accomplished by adding your CA certificate to your JVM's CA certificates keystore. The default CA certificates keystore is found in your runtime environment's security directory, found underneath the lib directory. It's intuitively named "cacerts". We'll be modifying the cacerts file, so make a backup copy before continuing.
The keytool utility distributed with J2SDK allows us to perform operations on any JVM keystores, so let's use it to examine the default cacerts.
keytool -list -keystore cacerts
When you execute this command, you'll be prompted for the keystore password. The default cacerts password is "changeit".
As you can see in Listing 2, the default cacerts contains certificates from VeriSign and Thawte, which together issue the vast majority of Web site certificates. Our JVM recognizes most Web-based e-commerce certificates already. Now let's add our CA certificate to the keystore.
keytool -import -alias newCA -file
rootCACert -keystore cacerts
When asked if you wish to trust this certificate, answer "yes". If you examine the cacerts keystore again, you'll find that your CA certificate is listed underneath the alias you provided. You're now registered as a CA in your JVM. Let's use our new power as a CA to facilitate an SSL conversation between two parties.
To demonstrate that your JVM will trust certificates that you issue, we'll
set up an SSL-enabled conversation between Alice and Bob. Alice and Bob represent two servers in an enterprise system that need to communicate securely. For this demonstration you can use, but do not need, two separate boxes. To keep things simple, we can just launch two JVMs on the same box and share the same local cacerts file (see Figure 1). In this figure, configuration A shows the setup for the demo running on separate boxes. Configuration B shows the setup for the demo running on one machine.
Regardless of your hardware setup, both Alice and Bob will need access to a cacerts that we inserted our CA certificate in. Each communicating entity also needs a personal keystore. Let's make two copies of our cacerts file and place them in a working directory. Name the first copy aliceStore and the second bobStore. Next, we'll generate a public and private key pair for Alice and store the pair in her personal keystore. A screenshot of this process is shown in Listing 3.
Alice now has a 1024-bit RSA key, which will be valid for 365 days. This key pair allows Alice to participate as an unauthenticated client in an SSL conversation. However, in our demonstration system, we want to guarantee the identity of all conversation participants. Since Alice needs a certificate to participate as an authenticated client, she'll need to generate a certificate request to send to her certificate authority.
keytool -certreq -alias alice -sigalg
Note that my keytool utility balks on this step if the key generated in the last step has spaces in any Distinguished Name attributes. So, I used "Tallán" for my organization name since "Tallán, Inc." threw an exception. If you complete this step successfully, you'll have a certificate request for Alice. Now we can put our CA hat back on and generate a certificate according to Alice's request.
MD5withRSA -file aliceCSR -keystore
Enter keystore password: changeit
openssl x509 -req -CAcreateserial -
The resulting aliceReply is a certificate that's valid for 365 days. We return this certificate to Alice, which needs to store this certificate alongside her key pair in her personal keystore.
CAkey rootCAKey -days 365 -CA
rootCACert -in aliceCSR -out aliceReply
Loading 'screen' into random state - done
Getting CA Private Key
Enter PEM pass phrase: [passphrase]
keytool -import -alias Alice -trust
Great, Alice is all set. Now just repeat the same procedure for Bob. Once you have Bob's certificate stored in bobStore, it's time to work with some code.
cacerts -file aliceReply -keystore
Enter keystore password: changeit
Certificate reply was installed in keystore
There are four classes that we'll use in this demonstration. The first, SSLDemoException, is a simple wrapper around the Exception class. There's nothing to it. The other common class is the abstract base class SSLDemo, which contains some common features of the client and the server. The first item of interest in SSLDemo is the static initializer, where I dynamically add the Cryptographic Service Provider "SunJSSE" to my list of security providers, as described in the JSSE installation instructions. I also override the default keystore used by the TrustManager. This step designates the keystore that holds all my trusted CA certificates.
To improve performance for multiple connection conversations, my constructor preseeds a pseudorandom number generator that will be used to generate each SSLContext. I also load my personal keystore upon construction. My personal keystore holds the certificate chain needed to validate my own certificate. As an example, Alice's personal keystore must hold Alice's certificate and our CA certificate.
Since a good portion of the JSSE-specific logic is encapsulated in my base class, my client and server classes are kept relatively clean of JSSE-specific code. Each must define its personal keystore and use SSL-specific socket factories. In addition, the BobServer conditionally sets client authentication on its SSL-enabled server socket.
Let's put the code into action and step through an SSL conversation. First start the BobServer with client authentication enabled.
java BobServer true
The result seems a bit anticlimactic from the outside, but Figure 2 details the more impressive internal workings.
Bob is listening on port: 4242
Then invoke AliceClient
Java AliceClient "Hello Bob, this is Alice"
Sending message to Bob: Hello Bob
Bob's Reply: RE: Hello Bob - Hello Alice, this is Bob.
As you can see, client authentication simply requires that the client in the client/server conversation also presents a valid certificate. Since Alice has a valid certificate, AliceClient can talk to BobServer with client authentication enabled or disabled. However, if Alice only had a public/private key pair in aliceStore with no certificate, then Alice would only be able to converse with Bob with client authentication disabled. That demonstration is left as an exercise for the reader. So Alice accepted Bob's certificate and Bob accepted Alice's. They both trusted certificates that we issued as the CA in our PKI.
We've provided you with the ability to act as a CA in a minimalist PKI implementation, and demonstrated how you can integrate your self-certified certificates with J2EE systems via JSSE. Before you rush off to develop SSL-enabled enterprise systems, be aware that I've only exposed you to some fairly complex material. There's a lot more to developing enterprise PKI than simple certificate generation and distribution. I've detailed enough to make you dangerous, but security is only as good as its weakest link. Careful consideration must be given to all areas of a system's security to avoid negating any that SSL might provide.
Java Secure Socket Extension:
The OpenSSL Project:
Netscape's Secure Sockets Layer:
Eric Simmerman is a senior consultant in the development division of Tallán, Inc., where he designs and implements numerous enterprise systems in the U.S. and abroad. He's a Java 2 Sun Certified programmer and a Cisco Certified Network Associate and has been
developing professionally for seven years.. He holds a BS in computer engineering from Virginia Tech. [email protected]
Listing 1: Screen capture of the Certificate Authority key and certificate generation process
openssl req -new -x509 -newkey rsa:2048 -config openssl.cnf -keyout rootCAKey
-out rootCACert -days 3650 -rand "install.log:sunnyday.gif"
Loading 'screen' into random state - done
Generating a 2048 bit RSA private key
writing new private key to 'rootCAKey'
Enter PEM pass phrase: [I entered a pass phrase here]
Verifying password - Enter PEM pass phrase: [I confirmed my pass phrase here]
You are about to be asked to enter information that will be incorporated into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [US]:
State or Province Name (full name) [Connecticut]:
Locality Name (eg, city) [Glastonbury]:
Organization Name (eg, company) [Tallán, Inc.]:
Organizational Unit Name (eg, section) [Development]:
Common Name (eg, YOUR name) [Eric Simmerman]:
Email Address [[email protected]]:
Listing 2: Screen capture of a listing of the default cacerts keystore
Enter keystore password: changeit
Keystore type: jks
Keystore provider: SUN
Your keystore contains 10 entries:
thawtepersonalfreemailca, Fri Feb 12 15:12:16 EST 1999, trustedCertEntry,
Certificate fingerprint (MD5): 1E:74:C3:86:3C:0C:35:C5:3E:C2:7F:EF:3C:AA:3C:D9
thawtepersonalbasicca, Fri Feb 12 15:11:01 EST 1999, trustedCertEntry,
Certificate fingerprint (MD5): E6:0B:D2:C9:CA:2D:88:DB:1A:71:0E:4B:78:EB:02:41
verisignclass3ca, Mon Jun 29 13:05:51 EDT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 78:2A:02:DF:DB:2E:14:D5:A7:5F:0A:DF:B6:8E:9C:5D
thawteserverca, Fri Feb 12 15:14:33 EST 1999, trustedCertEntry,
Certificate fingerprint (MD5): C5:70:C4:A2:ED:53:78:0C:C8:10:53:81:64:CB:D0:1D
thawtepersonalpremiumca, Fri Feb 12 15:13:21 EST 1999, trustedCertEntry,
Certificate fingerprint (MD5): 3A:B2:DE:22:9A:20:93:49:F9:ED:C8:D2:8A:E7:68:0D
verisignclass4ca, Mon Jun 29 13:06:57 EDT 1998, trusstedCertEntry,
Certificate fingerprint (MD5): 1B:D1:AD:17:8B:7F:22:13:24:F5:26:E2:5D:4E:B9:10
verisignclass1ca, Mon Jun 29 13:06:17 EDT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 51:86:E8:1F:BC:B1:C3:71:B5:18:10:DB:5F:DC:F6:20
verisignserverca, Mon Jun 29 13:07:34 EDT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 74:7B:82:03:43:F0:00:9E:6B:B3:EC:47:BF:85:A5:93
thawtepremiumserverca, Fri Feb 12 15:15:26 EST 1999, trustedCertEntry,
Certificate fingerprint (MD5): 06:9F:69:79:16:66:90:02:1B:8C:8C:A2:C3:07:6F:3A
verisignclass2ca, Mon Jun 29 13:06:39 EDT 1998, trustedCertEntry,
Certificate fingerprint (MD5): EC:40:7D:2B:76:52:67:05:2C:EA:F2:3A:4F:65:F0:D8
Listing 3: Screen capture of a personal key pair generation with the JDK's keytool utility
keytool -genkey -alias Alice -keyalg RSA -keysize 1024 -keystore aliceStore
-validity 365 -storepass changeit
What is your first and last name?
What is the name of your organizational unit?
What is the name of your organization?
What is the name of your City or Locality?
What is the name of your State or Province?
What is the two-letter country code for this unit?
Is <CN=Alice, OU=Client, O=Tallán , L=Glastonbury, ST= Connecticut, C=US>
Enter key password for <Alice>
(RETURN if same as keystore password): [Hit RETURN to keep things simple]
Source Code For this article (~ 6.33 KB ~Zip File Format)