Tuesday, February 7, 2012

Enabling certificate based authentication for web application in GlassFish Server

Authentication is a process which allows application servers to identify the users through various modes like asking for userid-password, or user certificates etc. In this article I will discuss the steps to follow to enable Certificate based client authentication for a web application. It is one of the most complicated way of authentication therefore I have split the whole process of enabling it into several steps and discussed each of them in detail.

CLIENT-CERT authentication transmits the login information in an extremely secure form, using Public Key Certificates(PKC). A certificate is a digital document that at a minimum includes a Distinguished Name (DN) and an associated public key. The certificate is digitally signed by a trusted third party known as Certificate Authority (CA). CA vouches for authenticity of the certificate holder. Each principal in the transaction presents a certificate as its credentials. The recipient then validates the certificate's signature against its cache of known and trusted CA certificates. Two kinds of certificates associated in mutual authentication, a "personal certificate" identifies an end user in transaction and a "server certificate" identifies the service provider ie. server.

In this article, I will discuss the steps to create digital certificate for client, storing it in server truststore, and storing it in browser certificates list. Also will discuss the steps require to enable certificate based client authentication in IBM Websphere Application Server and deploying the sample web application on server.

Creating Self-Signed Certificates

We can create self-signed certificates with little effort using the keytool utility included with the Java Development Kit. Generating a self-signed certificate can be accomplished by typing the following command in the command line:

keytool -genkey -v -alias {aliasname} -keyalg RSA -storetype PKCS12 -keystore client_keystore.p12 -storepass {anypassword} -keypass {anypassword}


The above command assumes that the keytool utility is in system PATH. This tool can be found under the bin directory, under the directory where Java Development Kit is installed.

Substitute the values for the {anypassword} with your own password; both of these passwords must be same in order to successfully use the certificate to authenticate the client. You may choose any value for the {aliasname} parameter. You may also choose any value for the -keystore parameter; however, the value must end with .p12 , as this command generates a file that needs to be imported into web browser, and this file won't be recognized unless it has the .p12 extension.

After entering this command from the command line, keytool will prompt for some information.

What is your first and last name?
[Unknown]: Mohit
What is the name of your organizational unit?
[Unknown]: ISL
What is the name of your organization?
[Unknown]: IBM
What is the name of your City or Locality?
[Unknown]: Pune
What is the name of your State or Province?
[Unknown]: Mah
What is the two-letter country code for this unit?
[Unknown]: IN
Is CN=Mohit Agrawal, OU=ISL, O=IBM, L=Pune, ST=Mah, C=IN correct? (type "yes" or "no")
[no]: yes

Generating 1,024 bit RSA key pair and self-signed certificate (MD5WithRSA)
for: CN=Mohit Agrawal, OU=ISL, O=IBM, L=Pune, ST=Mah, C=IN
[Storing client_keystore.p12]

After you enter the data for each prompt, keytool will generate the certificate; it will be stored in the current directory. The name of the file will be value we used for the -keystore parameter (client_keystore.p12 in this example).

To be able to use this certificate to authenticate ourselves, we need to import it into the browser. The procedure, although similar, varies from browser to browser. In Firefox, this can be accomplished by going to Tools->Options->Encryption->View Certificates , click import and give the path of the PKCS12 keystore and corresponding password.


Importing Certificate in GlassFish trust store

The certificate we created in the previous step needs to be exported into a format that GlassFish can understand:

keytool -export -alias myselfsigned -keystore client_keystore.p12 -storetype PKCS12 -storepass changeit -rfc -file selfsigned.cer

The values for the -alias,-keystore, and -storepass parameters must match the values used in the previous command. You may choose any value for the -file parameter, but it is recommended for the value to end in the .cer extension.

As our certificate was not issued by a certificate authority, GlassFish by default will not recognize it as a valid certificate. GlassFish knows what certificates to trust based on the certificate authority that created them. The way this is implemented is that certificates for these various authorities are stored in a keystore names cacerts.jks. The keystore can be found in the following location:

[glassfish installation directory]/glassfish/domains/domain1/config/cacerts.jks

In order for GlassFish to accept our certificate, we need to import it into the cacerts keytsore. This can be accomplished by issuing the following command from the command line:

keytool -import -file selfsigned.cer -keystore "C:\Program Files\glassfish-3.1.1\glassfish\domains\domain1\config\cacerts.jks" -alias myselfsigned -storepass changeit

At this point, keytool will display the certificate information in the command line and ask us if we want to trust it:

Owner: CN=Mohit, OU=IBM, O=IBM India, L=Pune, ST=Mah, C=IN
Issuer: CN=Mohit, OU=IBM, O=IBM India, L=Pune, ST=Mah, C=IN
Serial number: 4f2a86ca
Valid from: 2/2/12 6:21 PM until: 5/2/12 6:21 PM
Certificate fingerprints:
MD5: 3B:83:AF:7F:58:EC:D9:89:03:F7:FE:38:EF:C6:3A:73
SHA1: 6C:CD:02:78:0B:78:CB:0B:DD:C2:D3:DF:CA:85:49:79:52:0C:2A:38
Trust this certificate? [no]: yes
Certificate was added to keystore

Once we add the certificate to the cacerts.jks keystore, we need to restart the domain for the change to take effect.

Configuring Applications to Use the Certificate Realm

As we are taking advantage of Java EE 5 security features, we don't need to modify any code at all in order to use the certificate realm. All we need to do is modify the application's configuration in its deployment descriptors, web.xml and sun-web.xml .

<security-constraint>
   <display-name>Constraint1</display-name>
   <web-resource-collection>
     <web-resource-name>ClientCertWebApp</web-resource-name>
     <url-pattern>/*</url-pattern>
   </web-resource-collection>
   <auth-constraint>
     <role-name>master</role-name>
   </auth-constraint>
   <user-data-constraint>
     <transport-guarantee>CONFIDENTIAL</transport-guarantee>
   </user-data-constraint>
</security-constraint>
<login-config>
   <auth-method>CLIENT-CERT</auth-method>
   <realm-name>certificate</realm-name>
</login-config>
<security-role>
   <role-name>master</role-name>
</security-role>



In this case, we declared CLIENT-CERT as the authorization method and certificate as the realm to use to authenticate. This will have effect of GlassFish asking the browser for a client certificate before allowing user into the application.

When using client-certificate authentication, the request must always be done via HTTPS, therefore; it is a good idea to add the element with a value of CONFIDENTIAL to the web.xml deployment descriptor.

Notice that we declared that only users in the role of master can access any page in the system; we did this by adding the role of master to the element nested inside the element of the element in web.xml deployment descriptor. In order to allow access to authorized users, we need to add them to this role. This is done in the sun-web.xml deployment descriptor.

<sun-web-app>
   <context-root>/ClientCertWebApp</context-root>
   <security-role-mapping>
     <role-name>master</role-name>
     <principal-name>CN=Mohit,OU=ISL,O=IBM,L=Pune,ST=Mah,C=IN</principal-name>
   </security-role-mapping>
</sun-web-app>

We are now ready to test our application; after we deploy it on GlassFish server and point the browser to any page in the application we should see a pop-up screen which will ask to select certificate from the available certificates in browser.

Friday, April 17, 2009

Configuring Log4j in Junit TestCases

As a developer we often have to test our business codes through different kinds of test cases. For my java objects I prefer Apache's JUnit Test cases. At many points in our code we have to log messages on output consoles or files. There are many logging API's are available but Apache's Log4j is widely used these days. One of the most important feature of Lo4j is that we can even log our messages to different files based on the java objects i.e based on there fully qualified package names.

Well, but the topic I am going to discuss here is how to configure our test cases to show our log messages on our standard output? Following are the steps we should keep in mind for successful configuration.
  1. If our test-cases has to be executed through Ant target then first let us create a target in our build.xml
  2. Log4j.properties file at specified location contains all the configurations required for log4j loggers and apenders.
  3. In our test cases we have to create and initialize our logger before we can use it to log our information. Here I have created a test case named SampleTest in package com.tests.project. The most essential point to remember that in constructor or inside init() method we have to call configure() method of BasicConfigurator object else it won't read the properties of Log4j.properties file.
  4. We can add any method named test***() which will get execute when we call ant target from command line as folows:
    D:\Workspace\Blog>ant _unittest -Dtest.class=com.tests.project.SampleTest

Following all the mentioned steps give your logger informations into standard console. Note: Don't forget to call BasicConfigurator.configure() mentioned in step 3.

Saturday, February 23, 2008

Htm Truncator Lucene Analyzer


On my recent SpellChecker project I had a module, in which I required spelling dictionary (i.e. Lucene Index) based on lucene NGramAnalyzers, reading from 1000's of .htm files we had in our repository. For this I have taken an advantage of lucene's extension capabilities by creating my own Analyzer to truncate the html tags since it was not required to store them in spelling dictionary. An Analyzer is an encapsulation of the analysis process.



Creating lucene Analyzer is very simple and easy task as we just have to extend Analyzer class which also have have only one method to override i.e.


public TokenStream tokenStream( String fieldName, Reader reader){}



TokenStream comes in two flavour i.e. Tokenizer and TokenFilter . Distinction is that Tokenizer deals with individual characters whereas TokenFilter deals with words. One important aspect is that we can create chain of these tokenstreams so that output of one object can be used as input for another object. In Tokenizer, the input should be in the form of Reader class. So what I have done that created a analyzer similar to StandardAnalyzer and supplied a new Reader object, which will truncate any words between '<' '>' characters from .htm files.

Following is the code of my HtmTagTruncatorAnalyzer class


public class MyStandardAnalyzer extends Analyzer{
private Set stopWords;
public MyStandardAnalyzer(){
stopWords = StopFilter.makeStopSet(StopAnalyzer.ENGLISH_STOP_WORDS);
}

public MyStandardAnalyzer(String[] stopWords){
this.stopWords = StopFilter.makeStopSet(StopAnalyzer.ENGLISH_STOP_WORDS);
this.stopWords.addAll(StopFilter.makeStopSet(stopWords));
}

public TokenStream tokenStream(String fieldName,Reader reader) {
return new StopFilter(new LowerCaseFilter(new StandardFilter
(new StandardTokenizer(getNewReader(reader)))),stopWords);
}

private Reader getNewReader(Reader reader){
StringBuffer tempBuffer = new StringBuffer();
try{
boolean result = true;
int length = 0;
loop1:while(true){
int character = reader.read();
char c = (char)character;
if(character == -1) {
break;
}
if(c == '<'){
result = false;
}
if(result == true){
if(c == '.' ){
tempBuffer.append(' ');
continue loop1;
}
if(c == '0' ||c == '1' ||c == '2' ||c == '3'||c == '4' ||
c == '5'||c == '6'||c == '7'||c == '8'||c == '9'||c == '&'||
c == ','||c == '-'||c == '_'||c == '@'){
tempBuffer.append(' ');
continue loop1;
}
tempBuffer.append(c);
}else if(result == false && c == '>'){
result = true;
tempBuffer.append(' ');
}
}
}catch(Exception e){
e.printStackTrace();
}

String text = tempBuffer.toString();
return new StringReader(text);
}

}

Hibernate's Solution for org.hibernate.hql.ast.QuerySyntaxException.

It take a more then 2 hours to me to solve this exception. I am sharing it with you guys so that new comers won't face it.

Suppose I have persistent object named 'User' mapped to Database table 'USER_DETAILS' in my User.hbm.xml file. When I tried to get object through HQL query ie
User user = session.createQuery("from User_Details where User_Id=1 ).uniqueResult();

I got " org.hibernate.hql.ast.QuerySyntaxExcepti
on: User_Details is not mapped [from User_Details where USER_ID=1".

Solution : I found that in HQL we have to supply persistent Object class name instead of DB table name in queries to retrieve object. i.e.
User user = session.createQuery("from User where User_Id=1 ).uniqueResult();

Regards

Mohit Agrawal

Tuesday, January 1, 2008

Introduction

Hi,

This is my first acquaintance with writing blogs. Still much not aware what to write but writing to see something in my blogs. One of the reason for writing blog is to get used to working with for Blog Server technology. At same time i am also going through reading a book on Blog sever technology i.e RSS and Atom in Action.