The Source for Java Technology Collaboration
User: Password:



Kumar Jayanti

Kumar Jayanti's Blog

SSL and CRL Checking with GlassFish V2

Posted by kumarjayanti on November 19, 2007 at 04:14 AM | Comments (3)

SSL and CRL Checking with GlassFish V2

Introduction

This blog is dedicated to some of the less documented but important aspects of using SSL on GlassFish V2. The following topics would be covered :

  1. How to change the Keystore Password
  2. Steps to develop a  Skeletal  Web Application that uses SSL Mutual Authentication
  3. How to Enable CRL based Revocation Checking (Static CRL file approach)
  4. How to Enable CRL based Revocation Checking (Dynamic approach)
  5. Revocation Checking using OCSP (Online Certificate Status Protocol)

How to change the Keystore Password

We should not change the GlassFish Keystore password directly using Keytool, because if we did that then GlassFish would not know how to retrieve the keys from it anymore. The reason why one would want to change the keystore password is because the default  password "changeit" is not a secure password (everyone knows it).

So what would happen if we change the keystore password directly using the following command :

>keytool  -storepasswd -keystore keystore.jks -new newpassword -storepass changeit

Now when you start GlassFish it wouldn't know what the new password is so you would see the following exception

Caused by: java.lang.IllegalStateException: Keystore was tampered with, or password was incorrect
        at com.sun.enterprise.security.SecuritySupportImpl.loadStores(SecuritySupportImpl.java:114)
        at com.sun.enterprise.security.SecuritySupportImpl.initJKS(SecuritySupportImpl.java:82)
        at com.sun.enterprise.security.SecuritySupportImpl.<init>(SecuritySupportImpl.java:76)
        at com.sun.enterprise.security.SecuritySupportImpl.<init>(SecuritySupportImpl.java:71)

And GlassFish would Fail to start. So how does one change the Keystore password for GlassFish. When we see the GlassFish Admin Console we see the option to change the Administrator Password.

 Application Server --> Administrator Password

Changing this password also does not help, because it changes the administrator password. So the real password to be changed is the GlassFish Master Password.

>asadmin stop-domain

Stop the domain if it is running,  and then we can change the master password.

>asadmin change-master-password --savemasterpassword=true
Please enter the new master password>
Please enter the new master password again>
Master password changed for domain domain1

Now let us see what happens if we try to list the GlassFish Keystore using the old password

>keytool -list -keystore keystore.jks -storepass changeit
keytool error: java.io.IOException: Keystore was tampered with, or password was incorrect

So we see that it fails, now let us try with the changed masterpassword

>keytool -list -keystore keystore.jks -storepass newpassword

Keystore type: jks
Keystore provider: SUN

Your keystore contains 1 entries

s1as, Nov 11, 2007, keyEntry,
Certificate fingerprint (MD5): C0:41:05:12:5A:77:E8:5D:1F:DB:FD:EF:E4:23:E2:42

This confirms that the right way to change the keystore password is to change the master password. Also do not forget the --savemasterpassword=true option when changing the masterpassword if you wish to save the changed masterpassword. Without this option the masterpassword file if it exists will be deleted and hence you will be prompted for the masterpassword every time you try to start the domain. On the otherhand be aware that there is a risk associated in saving the master passsword in a file

Another Caveat When Changing Master Password

If you have added more keyentries into the GlassFish Keystore other than the default "s1as" then when you change the master password, you will have to manually change the KeyPassword of the KeyEntries that you have added into the GlassFish Keystore. Otherwise  GlassFish would again fail to start and you may see the following exception :

java.lang.reflect.InvocationTargetException
...........

Caused by: java.lang.IllegalStateException: java.security.UnrecoverableKeyExcept
ion: Cannot recover key
        at com.sun.enterprise.security.SSLUtils.<clinit>(SSLUtils.java:128)
        ... 10 more
Caused by: java.security.UnrecoverableKeyException: Cannot recover key
        at sun.security.provider.KeyProtector.recover(KeyProtector.java:301)
        at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:120
)
        at java.security.KeyStore.getKey(KeyStore.java:731)
        at com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl.<init>(SunX509KeyM
anagerImpl.java:111)
        at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl$SunX509.engineInit

Assuming my GlassFish Keystore had a KeyEntry "myserver" in addition to "s1as" then upon changing the master password i would need to run the following command to change the keypassword for "myserver" to be the same as the new master password

>keytool -keypasswd -alias myserver -keystore keystore.jks -storepass <new master password>

This comes from the limitation of the  JSSE API. The keypassword and the keystore password cannot be different. The authentication process will fail if the keystore and the certificate's private key password are not the same.

Steps to develop a  Skeletal  Web Application that uses SSL Mutual Authentication

Assuming you are all set with your correct Server Certificates in place, here are the steps to create a Skeletal  WebApplication that makes use of SSL Mutual Authentication.  I made of  NetBeans when developing the Application because it provides Visual Editing of the Security Settings described in this section and makes things very easy.

The WebApplication demonstrated in this section would just have a Welcome JSP and a  Secure Hello.html page which  is Secured by specifying a Security Constraint requiring SSL Mutual Authentication.  You can access the complete WAR file for the Application here.


So here is the simple  Welcome JSP page.



<%@page contentType="text/html"%>
......
   <html>
       <head>
           <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
           <title>JSP Page</title>
       </head>
      
       <body bgcolor="#FFFFFF">
           Welcome to the  SSL Mutual Authentication Test Page
           <br />
           <p>Request a secure page <a href="secure/Hello.html">here!</a></p>
           <br />
           <p>It will use SSL Mutual Authentication</p>
       </body>
   </html>



In the web.xml we will add a Security Constraint for the URL  pattern "/secure/*"  which is where our  Secure Hello.html page is located.   We add a user-data-constraint with transport-guarantee CONFIDENTIAL indicating the need to use SSL. Then we add a  login-config element with auth-method  CLIENT-CERT to indicate the need for Client Certificate Authentication (making it an SSL Mutual Authentication Scenario).

In addition we would need to define the role which will be allowed to access the secure resources.  Followed by a mapping of  the role to groups/principals in sun-web.xml.  Here is how the security portion of web.xml would look



 <security-constraint>
        <display-name>Constraint1</display-name>
        <web-resource-collection>
            <web-resource-name>secure resource</web-resource-name>
            <description/>
            <url-pattern>/secure/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>HEAD</http-method>
            <http-method>PUT</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>authorized</role-name>
            </auth-constraint>
        <user-data-constraint>
            <description/>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
        </security-constraint>
    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
    </login-config>
    <security-role>
        <description/>
        <role-name>authorized</role-name>
    </security-role>

  
And here is how the role mapping in sun-web.xml is defined as

<security-role-mapping>
    <role-name>authorized</role-name>
    <group-name>authorized</group-name>
 </security-role-mapping>

 Now the last thing we would need to do is add the assign-groups property for the Certificate Realm in Glassfish  Domain.xml. This would make sure that all Client's with Valid Client Certificates get assigned a group named "authorized".  Here is how the Cerificate Realm configuration in GlassFish would look like


 <auth-realm classname="com.sun.enterprise.security.auth.realm.certificate.CertificateRealm" name="certificate">
          <property name="assign-groups" value="authorized"/>
 </auth-realm>

How to Enable CRL based Revocation Checking (Static CRL file approach)

Certificates may be revoked by a Certification Authority for Various reasons.  The most common proposed method for distributing revocation information requires an issuing authority to publish a signed list of revoked certificates (called CRL, acronym for Certificate Revocation List). The reasons for revocation and a whole lot of other details and issues with Revocation can be found elsewhere on the world wide web. In this section of the  blog we will discuss how  one can  use such a  CRL file to enforce certificate revocation checking. 

Ofcourse a Static CRL file is no good because the revocation lists issued by the Certificate Authority are bound to change overtime and so any site/server depending on such a CRL file will need to deal with issues of  timely updates to the CRL file  inorder to ensure robust revocation information. A complete discussion of this topic is out of the scope of this  blog.

The GlassFish   http-listener element supports a Property called "crlFile" whose value is a CRL file to be consulted during SSL  client Authentication. This can be an absolute or relative file path. If relative it is resolved against the domain-dir. If the property is not specified then CRL checking is disabled.

For this blog  i  created a  sample CA (Certificate Authority) and  generated a  Client Certificate signed by the CA. I later revoked the Client Certificate and the CA generated a CRL(crl.pem)  file containing the revocation information.  Here are the steps to simulate an SSL Client Authentication Failure using the revoked certificate.

1. Install the revoked ceritificate in your Browser as the Client Certificate
2. Copy the crl.pem file into domains/domain1/config/ directory
3. Specify the "crlFile" property in domain.xml under the http-listener meant for SSL (port 8181)

<http-listener acceptor-threads="1" address="0.0.0.0" blocking-enabled="false" default-virtual-server="server" enabled="true" family="inet" id="http-listener-2" port="8181" security-enabled="true" server-name="" xpowered-by="true">
          <ssl cert-nickname="s1as" client-auth-enabled="false" ssl2-enabled="false" ssl3-enabled="true" tls-enabled="true" tls-rollback-enabled="true"/>
          <property name="crlFile" value="${com.sun.aas.instanceRoot}/config/crl.pem"/>
</http-listener>

Notice that the Property should come below the ssl child element of http-listener.

 4.  Install the CA certificate in GlassFish Truststore cacerts.jks using Keytool, or using NSS tools if you are running in the enterprise profile.

Now run the SSL Mutual Authentication Sample and you will see that the Client Authentication Failed, the following Message can be seen in the GlassFish server Logs :

[#|2007-11-12T17:32:54.113+0530|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=17;_ThreadName=httpSSLWorkerThread-8181-0;|
httpSSLWorkerThread-8181-0, fatal error: 46: General SSLEngine problem
sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: Certificate has been revoked, reason: unspecified|#]

How to Enable CRL based Revocation Checking (Dynamic approach)

In the previous section we discussed static CRL file approach to revocation checking. But the JSSE supports Http URL based Revocation Checking wherein the Revocation List will be dynamically downloaded from the Ceritificate Authority.  Since the SSL implementation in GlassFish is essentially layered upon the JSSE support  so this feature of  Dynamic CRL based revocation checking is supported by GlassFish.  The information about the revocation list URL  is encoded  inside the Ceritificate  itself as Extension elements .  For example i created a  certificate using the Verisign Test CA and the certificate it issued to me contains the following extension elements :



#2: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [accessMethod: 1.3.6.1.5.5.7.48.1
   accessLocation: URIName: http://ocsp.verisign.com, accessMethod: 1.3.6.1.5.5.7.48.2
   accessLocation: URIName: http://SVRSecure-aia.verisign.com/SVRTrial2005-aia.cer]
]
#3: ObjectId: 1.3.6.1.5.5.7.1.12 Criticality=false
#4: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
  [DistributionPoint:
     [URIName: http://SVRSecure-crl.verisign.com/SVRTrial2005.crl]
]]


Notice the CRLDistributionPoints extension  which specifies the URL of the dynamicall downloadable CRL file from the CA.

The tradeoff between a Static CRL File and a Dynamic CRL download would be that a Dynamic CRL would be more  robust and correct but the size of the CRL file may impact the performance of the  revocation checking logic.

In GlassFish the following two system properties (understood by the underlying JSSE implementation)  can be specified as jvm-options in domain.xml to enable  Dynamic CRL download based Revocation Checking.

<jvm-options>-Dcom.sun.net.ssl.checkRevocation=true</jvm-options>
<jvm-options>-Dcom.sun.security.enableCRLDP=true</jvm-options>

This is because the way in which GlassFish uses the JSSE API's causes these two options remain false by default.

This approach ofcourse makes an assumption that the Certificate being used contains a CRL DistributionPoint Extension element. Otherwise enabling this option may cause failure.  You may  also  need to set the http.proxyHost and http.proxyPort  properties for this approach to work correctly. If for some reason the CRL file could not be fetched from the specified URL at runtime you may see an exception in the server logs of the following form :

[#|2007-11-12T16:54:20.877+0530|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=20;_ThreadName=httpSSLWorkerThread-8181-1;|
httpSSLWorkerThread-8181-1, fatal error: 46: General SSLEngine problem
sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: revocation status check failed: no CRL found|#]

Make sure you do not mix the static approach mentioned in previous section with this one, because although the static approach may work even with certificates that do not contain a CRL DP extension, enabling the dynamic CRL checking will cause failures if the Client certificate does not contain a CRL DP Extension.

To debug issues with CertPath API in JDK you can set the following JVM Option in GlassFish domain.xml :  -Djava.security.debug=certpath

When the  dynamic CRL checking succeeds you can see debug prints of the following form after enabling certpath debugging using the above option.


certpath: CrlRevocationChecker.verifyRevocationStatus() ---checking revocation status...|#]  
certpath: Checking CRLDPs for CN=ABC, OU=Terms of use at www.verisign.com/cps/testca (c)05, OU=Foo, O=BAR, L=BLR, ST=KN, C=IN|#]  
certpath: Trying to fetch CRL from DP http://SVRSecure-crl.verisign.com/SVRTrial2005.crl|#]
certpath: Downloading new CRL...|#]
certpath: Returning 1 CRLs|#]
certpath: CrlRevocationChecker.verifyRevocationStatus() crls.size() = 1|#]  
certpath: starting the final sweep...|#]
certpath: CrlRevocationChecker.verifyRevocationStatus cert SN: 92665084177020392276407153602684386406|#]  
certpath: -checker5 validation succeeded|#]




Revocation Checking Using OCSP

The Online Certificate Status Protocol (OCSP) is an Internet protocol used for obtaining the revocation status of an X.509 digital certificate. It is described in RFC 2560 and is on the Internet standards track. It was created as an alternative to certificate revocation lists (CRL), specifically addressing certain problems associated with using CRLs in a public key infrastructure (PKI).

The JSSE in JDK 5 and Later releases supports OCSP based revocation checking.  The following link describes some of the PKI Enhancements in Java SE 5
http://java.sun.com/j2se/1.5.0/docs/guide/security/pki-tiger.html.

To enable OCSP revocation Checking we need to set "ocsp.enable" property to true. This property may be set either statically in the Java runtime's $JAVA_HOME/jre/lib/security/java.security file, or dynamically using the java.security.Security.setProperty() method.  Incase of GlassFish the Static Approach of setting inside  java.security file is what would be possible.  This is because GlassFish does not set this property by default.

The JSSE documentation indicates that one can possibly enable both OCSP and Dynamic CRL DP approaches. It says,  OCSP checking works in conjunction with Certificate Revocation Lists (CRLs) during revocation checking. Below is a summary of the interaction of OCSP and CRLs. Failover to CRLs occurs only if an OCSP problem is encountered. Failover does not occur if the OCSP responder confirms either that the certificate has been revoked or that it has not been revoked.

PKIXParameters RevocationEnabled (default=true) ocsp.enabled (default=false) Behavior
true true Revocation checking using OCSP,
failover to using CRLs
true false Revocation checking using CRLs only
false true No revocation checking
false false No revocation checking

For revocation checking based on OCSP to work the certificate would need to have the  URL of an online certificate status protocol (OCSP) server in the Authority Info Access (AIA) extension of the certificate (as shown in the example below)



#2: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [accessMethod: 1.3.6.1.5.5.7.48.1
   accessLocation: URIName: http://ocsp.verisign.com, accessMethod: 1.3.6.1.5.5.7.48.2
   accessLocation: URIName: http://SVRSecure-aia.verisign.com/SVRTrial2005-aia.cer]



However the JSSE layer allows specifying a  ocsp.responderURL property.  By default, the location of the OCSP responder is determined implicitly from the certificate being validated. The property is used when the Authority Information Access extension (defined in RFC 3280) is absent from the certificate or when it requires overriding.

By enabling Certpath Debugging you should see the debugging info as shown below when you set the ocsp.enable property to true.


 
certpath: connecting to OCSP service at: http://ocsp.verisign.com|#]
certpath: OCSP response: Successful|#]
certpath: OCSP response type: basic|#]
certpath: OCSP Responder name: CN=VeriSign Trial Secure Server OCSP Responder, OU=Terms of use at www.verisign.com/cps/testca (c)05, OU="For Test Purposes Only.  No assurances.", O="VeriSign, Inc.", C=US|#]
certpath: Verified signature of OCSP Responder|#]
certpath: Status of certificate (with serial number 92665084177020392276407153602684386406) is: Good|#]
certpath: -checker5 validation succeeded|#]
certpath: checking for unresolvedCritExts|#]
certpath:
cert1 validation succeeded.
certpath: Cert path validation succeeded. (PKIX validation algorithm)|#]




Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Thanks Kumar. It was very helpful in solving that exception.

    Posted by: vmiharia on February 15, 2008 at 07:59 AM

  • It's very interesting. Thanks Kumar! :D

    Posted by: ernestojpg on November 19, 2007 at 02:37 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds