Saturday, October 8. 2011
Certificate Security in JavaEE - Demo Setup


Some days ago I was discussing with a colleague what would be the best way to get certificate security in a JavaEE application. We did not reach any final conclusion so I decided to start a demo environment to test some simple cases. In this post the environment is going to be setup, the CA and the Application Server.
In order to get a complete infrastructure a CA (Certificate Authority) with CRL (Certificate Revokation List) publication and/or OCSP (Online Certificate Status Protocol) checking is needed. As I explained in the first post of the signing applet series a CA is a trusted company which issues certificates. The other two concepts are mechanisms for validating certificates or, more precisely, to know if a certificate has been revoked. A CRL is a list of revoked certificates and this list is usually accessed via internet. OCSP is a standard protocol to check certificates online. It is clear if some application is going to trust in user certificates, there should be someway to deal with revocations.
I decided to use openssl as a simple but complete CA infrastructure. The famous security library also has some CA capabilities which are perfect for a demo environment. As the JavaEE container I setup Glassfish 3.1.1 (but I think the concepts are valid for any other app server). Of course Debian Squeeze is the chosen OS (installed as a KVM virtual box). The steps for creating the complete infrastructure are the following.
-
Creation and configuration of the OpenSSL CA. For this point I followed this helpful web page.
First of all, openssl package is installed:
# apt-get install openssl
After that the file /etc/ssl/openssl.cnf has to be modified. Here it is my modified file but basically only two options were added to [usr_cert] section:
authorityInfoAccess = OCSP;URI:http://debian.demo.kvm:3456/ crlDistributionPoints = URI:http://debian.demo.kvm/my.crl
This options add the OCSP and CRL information to the certificate. Both techniques have to be explicitly added to the generated certificates, this way, when something (a browser, app server or whatever) reads the certificate it can check its validity against the specified URLs. As you see my debian demo box is called debian.demo.kvm.
Some directories needed by the CA configuration are created now (CA was located inside /etc/ssl/demoCA directory which is also specified in openssl.cnf):
# cd /etc/ssl # mkdir -p demoCA/newcerts
Some files are also initialized:
# touch /etc/ssl/demoCA/index.txt # echo 01 > /etc/ssl/demoCA/serial # echo 01 > /etc/ssl/demoCA/crlnumber
-
Creation of the CA certificate. In this second step the CA certificate is generated (directory is always /etc/ssl if not specified). The certificate is requested:
# cd /etc/ssl # openssl req -subj "/C=ES/O=demo.kvm/CN=ca.demo.kvm" -new -newkey rsa:2048 \ -keyout private/cakey.pem -out careq.pem Generating a 2048 bit RSA private key ...............................................................+++ .........................+++ writing new private key to 'private/cakey.pem' Enter PEM pass phrase: Verifying - Enter PEM pass phrase:
Now the certificate is self-signed against the CA. Please check the extensions v3_ca option, this is the CA cert and it should not contain OCSP or CRL info (this option makes the command to use the other v3_ca section which does not contain OCSP or CRL info).
# openssl ca -out cacert.pem -days 365 -keyfile private/cakey.pem -selfsign \ -extensions v3_ca -infiles careq.pem Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for private/cakey.pem: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 1 (0x1) Validity Not Before: Sep 16 16:38:41 2011 GMT Not After : Sep 15 16:38:41 2012 GMT Subject: countryName = ES organizationName = demo.kvm commonName = ca.demo.kvm X509v3 extensions: X509v3 Subject Key Identifier: BC:39:85:FF:DB:6E:46:FF:08:52:32:6F:97:65:91:F6:F4:A9:24:E7 X509v3 Authority Key Identifier: keyid:BC:39:85:FF:DB:6E:46:FF:08:52:32:6F:97:65:91:F6:F4:A9:24:E7 DirName:/C=ES/O=demo.kvm/CN=ca.demo.kvm serial:01 X509v3 Basic Constraints: CA:TRUE Certificate is to be certified until Sep 15 16:38:41 2012 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
Finally the CA cert is exported in DER format (DER format will be better for JKS stores in glassfish) and in PKCS#12 format (a p12 file contains the keys and the certificate, so it can be considered as a backup).
# openssl x509 -in cacert.pem -outform DER -out cacert.der # openssl pkcs12 -export -out cacert.p12 -in cacert.pem -inkey private/cakey.pem Enter pass phrase for private/cakey.pem: Enter Export Password: Verifying - Enter Export Password:
The first certificate (the CA one) is ready. This certificate will be used to sign all the others (users and glassfish server).
-
Creation of two client certificates. Once the CA cert is generated two client certs are going to be issued, the procedure is very similar but, when signed, the CA certificate is used (instead of self-signing) and now no extensions option is specified (now user_cert section will be used, with OCSP and CRL info). Again in this point I followed another tutorial, in this case the rsa keys were generated first and then the certificate was requested, created and transformed to DER and PKCS#12.
# openssl genrsa -out private/client1.key 1024 # openssl req -subj "/C=ES/O=demo.kvm/CN=SampleClient1" -key private/client1.key \ -new -out client1.req # openssl ca -in client1.req -cert cacert.pem -keyfile private/cakey.pem -out client1.pem Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for private/cakey.pem: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 2 (0x2) Validity Not Before: Sep 16 16:42:31 2011 GMT Not After : Sep 15 16:42:31 2012 GMT Subject: countryName = ES organizationName = demo.kvm commonName = SampleClient1 X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 36:24:6E:0E:9B:A4:30:FA:85:7B:59:0D:35:DE:09:F0:C1:89:4B:D6 X509v3 Authority Key Identifier: keyid:BC:39:85:FF:DB:6E:46:FF:08:52:32:6F:97:65:91:F6:F4:A9:24:E7 Authority Information Access: OCSP - URI:http://debian.demo.kvm:3456/ X509v3 CRL Distribution Points: URI:http://debian.demo.kvm/my.crl Certificate is to be certified until Sep 15 16:42:31 2012 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated # openssl x509 -in client1.pem -outform DER -out client1.der # openssl pkcs12 -export -out client1.p12 -in client1.pem -inkey private/client1.key Enter pass phrase for private/client1.pem: Enter Export Password: Verifying - Enter Export Password:
Doing the same steps a second certificate (client2) was generated. But, in this case, the certificate was then revoked (this way we have a valid and an invalid user certificate for testing).
# openssl ca -revoke client2.pem -keyfile private/cakey.pem -cert cacert.pem Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for private/cakey.pem: Revoking Certificate 3. Data Base Updated
-
Installing a certificate for glassfish. Another necessary certificate is the one for the application server ssl socket, working with user certificates always needs a server-side https connection. New version v3 of glassfish only uses JKS java store (so forget about the NSS certificate store which I commented in a previous entry about 2.1.1 version).
The first thing to do is installing java JDK. In debian you need to add the non-free debian repository in the /etc/apt/sources.list and install the oracle (closed-source) package:
# apt-get install sun-java6-jdk
If you prefer OpenJDK (open-source) implementation you need to install another package instead (I did not test it but I am sure it would also work smoothly):
# apt-get install openjdk-6-jdk
Glassfish 3.1.1 was downloaded, the full platform ZIP file, and installed (uncompressing the file is the only step). In the virtual debian box glassfish was installed in /opt directory:
# cd /opt # unzip ${DOWNLOAD_DIR}/glassfish-3.1.1.zip # cd /opt/glassfish3/bin # ./asadmin start-domain
As you see ZIP installation contains a local domain1 domain already configured. The certificate stores are placed in the glassfish/domains/domain1/config directory. The keystore.jks file is the store for server certificates and cacerts.jks the store for trusted CA certificates. Now it is the time to request a new certificate for the https port (glassfish always configures a secure port, port 8181 by default, with a self-signed s1as certificate, which is going to be replaced).
The certificate (called server) is created without password (RETURN was pressed):
# keytool -genkey -alias server -keyalg RSA -keysize 1024 -keystore keystore.jks \ -dname "CN=debian.demo.kvm,OU=GlassFish,O=demo.kvm,C=ES" Enter keystore password: Enter key password for <server> (RETURN if same as keystore password):
Then the request file is generated:
# keytool -certreq -alias server -keyalg RSA -file /etc/ssl/server.req -keystore keystore.jks Enter keystore password: Enter key password for <server>
Now it is time to sign the request with our CA. Again user_cert section is used, so OCSP and CRL information will be also added to this certificate (this is not necessary but it is quite nice). The server has to be transformed to DER format to be used in JKS.
# cd /etc/ssl # openssl ca -in server.req -cert cacert.pem -keyfile private/cakey.pem -out server.pem Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for private/cakey.pem: Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4 (0x4) Validity Not Before: Sep 16 16:56:14 2011 GMT Not After : Sep 15 16:56:14 2012 GMT Subject: countryName = ES organizationName = demo.kvm organizationalUnitName = GlassFish commonName = debian.demo.kvm X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 36:33:F0:39:B6:3C:71:29:E3:0D:25:76:6F:70:46:EE:E5:73:97:C6 X509v3 Authority Key Identifier: keyid:BC:39:85:FF:DB:6E:46:FF:08:52:32:6F:97:65:91:F6:F4:A9:24:E7 Authority Information Access: OCSP - URI:http://debian.demo.kvm:3456/ X509v3 CRL Distribution Points: URI:http://debian.demo.kvm/my.crl Certificate is to be certified until Sep 15 16:56:14 2012 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated # openssl x509 -in server.pem -outform DER -out server.der
And finally the certificate is imported into the keystore.jks glassfish store. Cos the certificate is signed by an unknown CA the cacert is also imported in both (user and trusted CA files). So now three commands are executed: import CA file in cacerts.jks, import CA file into keystore.jks and finally import the new server certificate in keystore.jks.
# cd /opt/glassfish3/glassfish/domains/domain1/config # keytool -import -trustcacerts -alias myca -file /etc/ssl/cacert.der -keystore cacerts.jks Enter keystore password: Owner: CN=ca.demo.kvm,O=demo.kvm,C=ES Issuer: CN=ca.demo.kvm,O=demo.kvm,C=ES ... Trust this certificate? [no]: yes Certificate was added to keystore # keytool -import -trustcacerts -alias myca -file /etc/ssl/cacert.der -keystore keystore.jks Enter keystore password: Owner: CN=ca.demo.kvm,O=demo.kvm,C=ES Issuer: CN=ca.demo.kvm,O=demo.kvm,C=ES ... Trust this certificate? [no]: yes Certificate was added to keystore # keytool -import -alias server -file /etc/ssl/server.der -keystore keystore.jks Enter keystore password: Certificate reply was installed in keystore
At this point we have a valid certificate with the alias server ready to be used in the https listener. In the glassfish console (http://debian.demo.kvm:4848) we select Configuration → server config → Network Config → http-listener-2. Here we see that security is enable in this listener, clicking the SSL tab the Client Authentication checkbox is selected and in the Certificate Nickname current auto-generated certificate s1as is changed for the new server alias.
This change does not need a reboot of the server. At this moment the 8181 port is using the new certificate (server alias) but it is also demanding a user certificate (so you only access the welcome page if a user certificate is installed in the browser, it will be done in the last step).
-
Publication of the CRL and setup of the OCSP server. The main idea of this demo is that the Application Server (glassfish) was able to validate user certficates. To do that both OCSP and CRL techniques are going to be available.
Openssl has a minimal (only recommended for testing) OCSP server, we can start the process directly from the command line. As we have specified port 3456 in the certificate URI the daemon is started as follows (the -text option dumps communication to the terminal):
# cd /etc/ssl # openssl ocsp -index demoCA/index.txt -CA cacert.pem -rsigner cacert.pem \ -rkey private/cakey.pem -port 3456 -text Enter pass phrase for private/cakey.pem: Waiting for OCSP client connections...
This way the client certificates can now be checked. The validation process uses the serial number of the certificate (a kind of identifier the CA assigns for every certificate). In the first step a serial file was initialized to 0x1, this way the CA has been assigning a consecutive number to each generated certificate (0x1 for cacert, 0x2 for client1, 0x3 for client2 and 0x4 for server). The client1 certificate (0x2) is a good one:
# openssl ocsp -issuer cacert.pem -nonce -CAfile cacert.pem \ -url http://localhost:3456 -serial "0x2" Response verify OK 0x2: good This Update: Sep 16 16:51:49 2011 GMT
But the client2 certificate (0x3) is revoked:
# openssl ocsp -issuer cacert.pem -nonce -CAfile cacert.pem \ -url http://localhost:3456 -serial "0x3" Response verify OK 0x3: revoked This Update: Sep 16 16:51:51 2011 GMT Revocation Time: Sep 16 16:50:19 2011 GMT
CRL is a complementary way of validating certificates, it can be intended as a secondary method to use in case of an OCSP error. The CRL (revocation certificate list) is downloaded using standard http by the checkers (app server, browser,...). This way I decided to install an apache server in the box and then copy the generated file inside document root directory.
# apt-get install apache2
After that the CRL list is generated using openssl:
# cd /etc/ssl # openssl ca -gencrl -keyfile private/cakey.pem -cert cacert.pem -out my.crl
And it is copied to the apache2 root directory in debian:
# cp /etc/ssl/my.crl /var/www/my.crl
If you check the previous URL set in the certificate was http://debian.demo.kvm/my.crl which is exactly where my CRL is now.
-
CRL and OCSP checking in the glassfish server. By default glassfish server only checks the CA, it must be a trusted one (that is why cacert was imported to cacerts.jks store) and dates, but it does not validate revocations (neither OCSP nor CRL). This entry explains how to do it in glassfish v2 but it also works in v3 (the solution consists in activating the checking in JSSE implementation which I suppose works in any server that uses this ssl socket layer).
In the glassfish console Configurations → server-config → JVM Settings → JVM Options tab two new options have to be set (-Dcom.sun.net.ssl.checkRevocation=true and -Dcom.sun.security.enableCRLDP=true). Restart is required when JVM options are modified.
But this options only activate CRL checking and not OCSP. In order to finally have all the validation methods the /usr/lib/jvm/java-6-sun/jre/lib/security/java.security has to be modified uncommenting the following line (please think that this path varies depending where your JDK is installed, this is the default path for debian Oracle JDK):
ocsp.enable=true
If you check in this file there are more options to customize ocsp validation (I did not spend more time on this).
Finally glassfish is ready to receive and validate client certificates using both techniques. Java implementation first tries OCSP protocol and, if it fails, the CRL is downloaded (check commented post for further info).
-
Setting up client certificates. Now all the server side is ready but our browser needs some certificates, client1 and client2. If you remember both were exported in PKCS#12 format and both p12 files can be directly imported to firefox. Of course remember to also import the cacert.der as a trusted CA.
In firefox (iceweasel) 5.0 Edit → Preferences → Advanced Tab → Encryption subtab → View Certificates. In Your Certificates tab click the Import button and select client1.p12 and client2.p12 (the password that you inserted when the certificates were exported is requested at this point).
The cacert.der is imported in the same window but in Authorities tab. There click again Import. Check all the trust settings (web sites, email users and software) when requested.
Finally all the environment (server and client) is ready to access the glassfish secure port. In the following video I first show that my two certificates are imported and the CA is trusted. Then I access the 8181 port of my debian box, as client certificate was configured the browser asks me which one to use, but I select the revoked certificate (see it has the 03 serial). So the glassfish server does not permit the access (it is revoked!). Trying again I select the other certificate (02 serial) and, this time, welcome glassfish page is shown.
If you recheck this entry I have not done anything yet. Only the environment is ready and no application is deployed inside glassfish. At first moment I was not going to post the setup but it needed so many steps and particular options that I finally decided to publish this entry. Entries in this blog are many times posted just for me, because I want to have a clear guide of what I did. This is one of these times, but of course it is also here for anyone who finds it useful. I have two more entries in mind. In the first one I will try to deploy a custom security application (not using JavaEE security but programmatic). And in the second JavaEE security will be incorporated. But you know, it will depend on my mood and my time.
Don't change the channel!
Comments