August 3, 2004

Securing Web services: PKI basics

Author: Jeffrey L. Vagle

Developers of Web services and enterprise applications must make their software secure. While cryptography alone does not a secure system make, it's important
for developers to have a firm grasp of the basic theory
and technologies behind encryption, so they can make informed
decisions about whether to apply cryptographic
technology, and if so, how.

In this article we begin the exploration of applied cryptography foundations by
looking at public key infrastructure (PKI), and specifically keys, certificates, and trust, along with some practical examples of key generation and certificate management tasks. The full descriptions of many of these topics are often beyond the scope of this article.

Crypto basics

Encryption transforms information so that it becomes unreadable by anyone who is not privy
to a particular secret, and decryption is this operation's
inverse. The actual method of encryption is called the
cryptographic algorithm, and the secret is called the key. Two
general classifications of cryptographic algorithms are
symmetric encryption and asymmetric encryption (often called
public-key encryption).

Symmetric encryption

Most symmetric encryption algorithms use a single key for both encryption and decryption. The secret must be shared somehow, in other words, which can pose a problem. Relative
to public-key encryption, symmetric encryption is quite efficient -- by orders
of magnitude, in fact -- which makes symmetric encryption
algorithms attractive. However, if the symmetric key is compromised, the secret
is out, and all bets are off with respect to the benefits of encrypting your
data.

Public-key encryption

Public-key encryption uses a key pair made up of a private key and a public key. The
public key component can be published (hence the name) without revealing
anything about the private key component. Data encrypted with a
public key can only be decrypted with the recipient's private key. The reverse is also
true: data encrypted with the private key can only be decrypted with the
public key. Since anyone could be in possession of your public key,
this is not a great approach for hiding information, but it's perfect for
digital signatures. If someone successfully decrypts a message using your
public key, they are assured of two things: you encrypted the message (or at
least the holder of your private key did), and the message hasn't been tampered
with since it was encrypted (else the decryption would
fail).

Since public-key encryption is slow compared with symmetric encryption, its use is usually
limited to digital signatures and the sharing of symmetric keys.

Public-key infrastructure (PKI)

Public-key infrastructure (PKI) is the name for a system of cryptographic algorithms --
software and infrastructure used in the implementation of security services,
such as confidentiality, identification, authorization, and non-repudiation. At
the core of PKI is the use of public-key certificates.

Certificates

A certificate, sometimes known as an X.509 certificate or
public-key certificate, is a file that binds a public key (from
an asymmetric key pair) to an identity (usually a person,
computer, or corporate entity). These certificates contain the
public key in question in addition to information that can uniquely identify
the subject of the certificate (e.g. name, organization,
location), the validity period of the certificate (think of it
as an enforceable freshness date), the issuer (certification
authority), and details regarding the issuance of the certificate, such as the digital signature of the issuer.

Certificate authorities

Certificate authorities (CAs) are trusted organizations that
offer certificate creation, issuance, and revocation services.
There are basically two classifications of CA: subordinate CAs,
whose own signing certificate has, in turn, been signed by
another CA; and root CAs, which have issued and signed
their own signing certificate.

Trust

One of the key requirements behind the motivation for PKI is
the notion of distributed trust. For instance, how do I, the
user, trust the assertion that the Web site to which I am about to
submit my credit card information does indeed belong to the
company it claims to? A solution to this problem
lies with the use of certificates.

If, in the course of loading the Web site in question, the
site's server first sends its public-key certificate to me for inspection,
it could then encrypt all subsequent data it sent to me using the private
key corresponding to the public key in its certificate. I could
use the server's certificate to verify that the Web page
information loaded did indeed come from the certificate's owner,
since only that certificate's public key could decrypt the data
originally encrypted with the server's private key.

However, we still have the problem of identity. Although I now
know that the data I'm receiving did indeed originate from the
server that sent me the certificate, I know nothing at all about
that server, except that it's sending me encrypted data. One
possible solution to this problem would be to drive to the
vendor's offices, satisfy myself that the business is legit, and
personally meet with a corporate representative, who would give
me the company's server certificate file right then and there.
I could then take that file home and add it to a collection of
certificates I trust, since I have personally verified its
provenance. This solution doesn't scale very well, however.

Therefore, in order for a PKI to be at all useful, trust solutions must accommodate hundreds, thousands, or potentially millions of certificates, but should do so in a way that does not require the user to personally vet each and every one. The PKI approach to this problem is through transitive trust relationships.

Transitive trust works as follows. Let's say there are two
individuals, Alice and Bob, who know and trust one another.
Further, Bob has a friend, unknown to Alice, named Cassandra,
whom he trusts.

Since Alice trusts Bob, which presumably can be
extended to trusting Bob's choice of friends, Alice can then
(transitively) trust Cassandra.

This is how CAs are used in PKI. Rather than having to make explicit trust decisions regarding every certificate that one encounters, one can trust just a few CAs. Then, if a certificate you encounter has been issued (or signed) by one of your trusted CAs, you can trust
that certificate transitively. This is how PKI can scale.

Unfortunately, this is also how PKI can fail, since there is so much riding on trusting CAs. This, however, is a topic for another article or two.

Next: Create your own certificates

Although most CAs will create for you a keypair along with its signed public-key certificate, you can create your own keypairs and (self-signed) certificates. Let's walk through the steps necessary to request that a trusted CA sign your certificate.

Keys and certificates generated by these examples will be
usable by any application that recognizes such standards as RSA, DSA, and X.509. And even though we will limit our usage scenarios to Apache's Tomcatservlet container, you can use these techniques with other Web service applications via their respective configuration frameworks.

Two readily available tools for key/certificate management are
OpenSSL, a library of
cryptographic functionality which includes a command line tool
for performing specific functions; and Keytool, a key and certificate management utility that comes with Sun's Java Development Kit (JDK).

OpenSSL

Although the OpenSSL distribution includes other functions, such as an SSL/TLS implementation, we will only touch on its keypair/certificate generation and management tools here.

The following command will generate an RSA private key, from which a public key can be derived:

openssl genrsa -out private_key.pem 2048

If you wish to protect your private key with a password --
generally a good idea -- use
either the -des, -des3, or -idea
switch, depending on which password encryption algorithm you
wish to use. The size of the key (in
bits) here is 2,048, which is the current generally accepted minimum for RSA key
sizes.

If you wish to generate a DSA keypair, there are two steps
to follow. First, create a DSA parameter file:

openssl dsaparam -out dsa_param.pem 2048

Once this step is complete, you can generate the actual DSA
key:

openssl gendsa -out private_key.pem dsa_param.pem

As with the RSA example above, if you wish to protect your
private key with a password, use either the -des,
-des3, or -idea switch.

Both of the above examples use PEM as their output format, which
wraps Base64 ASN.1 DER
data with ASCII headers. In order to convert PEM files to PKCS#12 format (for use with Tomcat or many Web browsers), use the following command:

openssl pkcs12 -export -in pem_formatted_file -out pkcs12-formatted-file

We're not done yet, however. We still need a public-key certificate to go with our
corresponding private key. We'll start by creating our own self-signed X.509 certificate:

openssl req -new -x509 -key private_key.pem -out certificate.der

This command interactively asks the user for information
about the entity for which the certificate is being generated
(e.g., a Web server or a user), all of which is used to
identify the subject of the public-key certificate; that is, to bind
the entity to a public key.

If you're generating the certificate for test purposes, this may suffice. However, for most real applications, it's necessary for the certificate to be signed by a trusted CA. The details behind this process vary by CA, but most will ask for and accept a certificate signing request (CSR). To generate one, use the following command:

openssl req -new -key private_key.pem -out certificate_request.csr

Notice that this command differs from the last one only by
the omission of the -x509 switch (which self-signs the
certificate). The output from this command -- a CSR -- can now be sent to a CA via
whatever method it requires. Once the CA has been satisified
as to the identity of the subject (and again, these steps will vary from CA
to CA), the CA will issue a signed certificate.

The private key and public-key certificate combination can now
be used for PKI-enabled applications, e.g., to enable SSL
connections with your Tomcat servlet container, as we'll see in a moment.

Keytool

Keypair and certificate management using keytool is somewhat
simpler than using OpenSSL. For instance, rather than having to
create a PKCS#12 keystore separately, keytool creates a Java key
store (JKS) as part of the keypair/certificate
generation process. On the other hand, keytool doesn't provide nearly as much
functionality as OpenSSL. For example, keytool provides no
functionality for importing or exporting private keys to or from a keystore.

We begin by generating an RSA keypair (along with its corresponding self-signed certificate) using keytool:

keytool -genkey -keyalg RSA -keysize 2048 -keystore my_keystore.jks -alias me

This command will ask you to complete the necessary
certificate entries, as with the OpenSSL example above. The
-alias switch lets you assign a name to this
key/certificate entry in your keystore. If you wish to generate a
DSA keypair, use the following command:

keytool -genkey -keyalg DSA -keysize 2048 -keystore my_keystore.jks -alias me

Once again, if you are interested only in generating a test certificate, you can stop here. If, however, you want a trusted CA to sign your certificate, you need to generate a CSR, with a command like:

keytool -certreq -alias me -keystore my_keystore.jks -file certificate_request.csr

You can then send the resulting CSR file to your CA using whatever methods they require. Following its vetting process, the CA will issue a signed certificate.

A Tomcat example

Now that we've generated our public/private keypairs and have
CA-issued public-key certificates to go with them, we're ready
to put them to good use.

Our first step is to ensure our keystore is formatted properly
for use by Tomcat. Tomcat 5 currently supports only keystores formatted by JKS or
PKCS#12. If you used OpenSSL to create your keypair
and certificate request, you need to put your private key and
the corresponding public-key certificate together in a PKCS#12
keystore:

openssl pkcs12 -export -in certificate_file -inkey private_key.pem -chain -name tomcat -outfile my_keystore.p12

This command takes the file containing the public-key
certificate issued by the CA (certificate_file) and
pairs it with its corresponding private key
(private_key.pem) in a PKCS#12-formatted keystore,
giving it the alias "tomcat," the default alias for use within
Tomcat.

If you generated your keypair/certificate using keytool, you don't need to reformat any files -- keytool creates JKS-formatted keystores. Make sure, however, that once you receive
a signed certificate from a CA (in response to your CSR above),
you import that certificate into your keystore:

keytool -import -alias tomcat -trustcacerts -file certificate_file -keystore my_keystore.jks

This command pairs the CA-signed certificate (in the file
certificate_file) with its corresponding private
key in the keystore created above, giving it the default alias
of "tomcat."

We're almost there. Our last step is to configure Tomcat to
offer an SSL-enabled socket by editing the
$TOMCAT_HOME/conf/server.xml file, adding the
following Connector element:

<Connector port="443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="/path/to/keystore_file" keystorePass="keystorePassword" keystoreType="JKS">

This enables Tomcat to listen for SSL connections on port
443. Note that you should change the keystoreType
parameter to "PKCS12" if your keystore is PKCS#12 formatted.
Also, if you wish to require that clients also identify
themselves with valid certificates (mutual authentication),
change the clientAuth parameter to "true."

It's also important to verify that the redirectPort parameter on the non-SSL connector is set to match the SSL port's connector. For information on this and other Tomcat server configuration options, see this page.

Once you've edited the server configuration file, you can
restart Tomcat and verify your new SSL connection by pointing
your browser at your new Tomcat SSL port, https://localhost:443.

This assumes, of course, that your browser is running on the
same machine as your Tomcat installation. You should see the
Tomcat splash page (or whatever you've configured your root Web
application to be), and your browser should indicate that SSL
has been enabled.

Category:

  • Security
Click Here!