Skip to main content

SSL and CRL Checking with GlassFish V2

Posted by kumarjayanti on November 19, 2007 at 4:14 AM PST

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 href="http://weblogs.java.net/blog/kumarjayanti/archive/SSLMutualAuth.war">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 href="http://weblogs.java.net/blog/kumarjayanti/archive/revoked.pfx">revoked
ceritificate in your
Browser as the Client Certificate

2. Copy the href="http://weblogs.java.net/blog/kumarjayanti/archive/crl.pem">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 href="http://weblogs.java.net/blog/kumarjayanti/archive/cacert.der">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: href="http://SVRSecure-crl.verisign.com/SVRTrial2005.crl">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 class="moz-txt-link-abbreviated"
href="http://www.verisign.com/cps/testca">www.verisign.com/cps/testca
(c)05, OU=Foo, O=BAR, L=BLR, ST=KN, C=IN|#]
 

certpath: Trying to fetch CRL from DP href="http://svrsecure-crl.verisign.com/SVRTrial2005.crl">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

href="http://java.sun.com/j2se/1.5.0/docs/guide/security/pki-tiger.html">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: class="moz-txt-link-freetext" href="http://ocsp.verisign.com/">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 href="http://www.verisign.com/cps/testca">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)|#]



href="http://java.sun.com/developer/EJTechTips/2006/tt0527.html">


Related Topics >>

Comments

SSL and CRL Checking with GlassFish V3

Kumar,

This is a great posting for GF v2. Do you have a similar posting for GF v3?

Thanks in advance,
Eric

CRL check on LDAP using Glassfish

Hi,

First of all, thank you very much for this fantastic blog full of useful info.

I'm facing the following problem with Glassfish v2.1:

A customer that we are working for has 2 Certificate Authorities that are used to create user certificates in order to be used in a smartcard solution. So, some users have smartcards signed by the CA 1, some other users have smartcards signed by CA 2.
  • The CA 1 maintains a CRL list published on a LDAP server AND on a HTTP server (available in the "CRL Distribution Point" field of the certificates)
  • The CA 2 maintains a CRL list published only on a LDAP server (also available in the "CRL Distribution Point" of the certificates)

    We developed a web application for this customer where we MUST have a CRL check at login time.

    My questions are:
    1) Is it possible to configure Glassfish to use the dynamic CRL check, dynamically delivering the CRL list from the LDAP server using the "CRL Distribution Point" of the certificate ? If yes, how to do it ? do you have any pointer ?
    2) Is it possible to use the static way with the 2 CRLs ? (both CRL files will be updated periodically on GF filesystem). From my understanding, and as I already experienced, I did not manage to setup such static check with 2 CRLs (using the "crlFile" property, it doesn't support multivalues).

    Thanks in advance for your help!
    Nicolas