GoogleSearchBox

Custom Search

Friday, May 31, 2013

On CentOS (Linux), Configuring SSL (https) on tomcat 6

This post is to Configuring / Installing SSL (https) on tomcat 6 on CentOS (RedHat Linux) Machine / box.
I will update this post as I get to know more issues myself.

Basically, I am needing my server (tomcat in this case) to be SSL configured, because I am configuring CAS server on it for SSO functionality. So I will be talking the paths related to CAS, but you can have your directories/paths accordingly as per your requirement. This post should serve as a generic post for Configuring SSL on tomcat.

We need Openssl tool/app to generate and certify our certificates. Openssl is analogous to Certifying authorities like Verisign, Godaddy, Thwat,  etc who are private organizations to sign and certify our ssl certificates for a server.
First install Openssl on your Linux machine.

To know the location of Openssl conf :
# openssl version -d
Result :  OPENSSLDIR: "/etc/pki/tls"

Below are our objectives:
1. We will create a root CA (Certification Authority), self sign it.
2. Create Server certificate and Sign it using root CA.
3. Create Client certificate and Sign it using root CA.
4. Use PKCS#12, to bundle server and client certificates with their respective private keys.
5. Configuring the Tomcat to enable the SSL and to use a JKS (JavaKeyStore) file.
6. Configure the server to require client authentication.


Now go to the OPENSSLDIR directory and create a directory under it, which will hold our project related certificates in it (in my case I have named it "tomcat6-CAS-certs").
# cd /etc/pki/tls
#  mkdir tomcat6-CAS-certs

#  cd tomcat6-CAS-certs/

1. Create a root CA (Certification Authority), self sign it:
1.(a). Lets create a public-private key pair (a file named as: casRootCAkey.key), which will be used to self sign our root CA (will be named as: ):
# openssl genrsa -out casRootCAkey.key 1024

1.(b). Lets create our Root CA's certificate and self sign this certificate (if you know your server's dns name/ hostname or skip to next)
#  openssl req -new -x509 -days 3650 -key casRootCAkey.key -out casRootCAcert.crt

Basically above command will generate the certificate (I named it, "casRootCAcert.crt") first , and then sign it using the key ("casRootCAkey.key") previously created):
-x509 used to self sign the certificate.
-days 3650    will sign the certificate for next 10 years.

Result will be something like this:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:NewJersey
Locality Name (eg, city) [Default City]:Edison
Organization Name (eg, company) [Default Company Ltd]:YourCompanyName
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:yourcompanysite.com
Email Address []:myemail@anymail.com

Above in Bold are entered by me related to me, you have to change related to your data.

OR

Create our Root CA's certificate and self sign this certificate (if you don't know your server's dns name/ hostname, use the IP address):
If you are using an IP address basically as a CN, then use below command :
# openssl req -new -x509 -days 3650 -key casRootCAkey.key -out casRootCAcert.crt -config openssl.cnf

Edit your "openssl.cnf"  file to update below section:
uncomment below if commented (by taking out # sign before req_extensions):
req_extensions = v3_req # The extensions to add to a certificate request

also under [ v3_req ]
add and entry:
subjectAltName=IP:210.10.0.1

Above command's Result will be something like this:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:NewJersey
Locality Name (eg, city) [Default City]:Edison
Organization Name (eg, company) [Default Company Ltd]:YourCompanyName
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:210.10.0.1
Email Address []:myemail@anymail.com

1.(c). Lets create some directories to hold some helper files like (index.txt and serial files) for our CA file, and we will configure these directories later in "openssl.cnf" file to be used while creating and signing certificates:
# mkdir  CAhelperFiles
# mkdir newcertsPEMfiles
# ls -l
Result:
total 28
drwxr-xr-x 2 root root  4096 Jun 17 02:46 CAhelperFiles
drwxr-xr-x 2 root root  4096 Jun 17 03:05 newcertsPEMfiles
-rw-r--r-- 1 root root 11026 Jun 14 05:17 openssl.cnf
-rw-r--r-- 1 root root  1123 Jun 14 08:05 casRootCAcert.crt
-rw-r--r-- 1 root root   891 Jun 14 07:17 casRootCAkey.key

1.(d). Lets create an index and a serial file to be used by our CA as per the openssl requirement:
# touch CAhelperFiles/index.txt
# echo '01' > CAhelperFiles/serial

Now lets see what we have got so far in each directory (-R is for recursively list all subdirectories too):
[root@www tomcat6-CAS-certs] #  ls -R
Result:
CAhelperFiles  newcertsPEMfiles  openssl.cnf  casRootCAcert.crt  casRootCAkey.key

./CAhelperFiles:
index.txt  serial

./newcertsPEMfiles:
[root@www tomcat6-CAS-certs]#

2. Create Server certificate:
2.(a). Lets create a key file (containing key-pair) to be used for creating server certificate:
tomcat6-CAS-certs] # openssl genrsa -out localhostServerKey.key 2048           (if you are using the localhost server for development env)
OR
tomcat6-CAS-certs] # openssl genrsa -out mydomainNameServerKey.key 2048       (where mydomainNameServerKey can be anyname but for clarity, lets use the server domain name or the server's IP as the key file name)

2. (b). Use above server key to generate a server certificate signing request (CSR) I named it "mydomainNameServerCSR.csr":
tomcat6-CAS-certs] #  openssl req -new -key mydomainNameServerKey.key -out mydomainNameServerCSR.csr -config openssl.cnf
Result:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:NewJersey
Locality Name (eg, city) [Default City]:Cranbury
Organization Name (eg, company) [Default Company Ltd]:YourCompanyName
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:localhost [or server's IP address or your company/project web site address if you have configured the “subjectAltName” for the IP in "openssl.cnf " file]
Email Address []:admin@myadminmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:apassword
An optional company name []:mycomp

Still you are in directory path:
# pwd
/etc/pki/tls/tomcat6-CAS-certs

2.(c). Now lets create a server certificate (mydomainNameServerCert.crt) and sign it using 
this server CSR file (mydomainNameServerCSR.csr)
along with rootCA key file (casRootCAkey.key) to be certified from the root CA (casRootCAcert.crt) :
If you are getting some error like below please see the link here:
Using configuration from /etc/pki/tls/openssl.cnf
/etc/pki/CA/index.txt: No such file or directory
unable to open '/etc/pki/CA/index.txt'
3078076140:error:02001002:system library:fopen:No such file or directory:bss_file.c:355:fopen('/etc/pki/CA/index.txt','r')
3078076140:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:357:

Otherwise please proceed as below:
Correct way/location of using the -config option is show below:
#  openssl ca -config openssl.cnf  -keyfile casRootCAkey.key -cert casRootCAcert.crt  -out mydomainNameServerCert.crt  -infiles mydomainNameServerCSR.csr



Result :
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
The organizationName field needed to be the same in the
CA certificate (YourCompanyName) and the request (YourCompanyName)

If you are lost, don't be confused about the directory structure. My directory structure is below:







And I am firing the "openssl" command from  "/etc/pki/tls/tomcat6-CAS-certs" directory.
And inside this directory only, I have my version of "openssl.cnf" file exist.

Now back to, where we left.  Lets list our files:
# ls -l
Result:


But still seems like our cert file has not been Signed! As you can see its a zero sized file.
Now we, have another problem, due to the policy setting in the openssl.cnf file set to use as : policy          = policy_match
which caused above warning that, "The organizationName field needed to be the same".
While creating CSR some fields like Country, State, Locality etc. on the server certificate may
 not match the CA certificate in practical, that means in real time, the CA and the Cert applying companies can be different.

So, we have to use a different policy while signing the server csr.
Actually we have another policy is readily available to be used in the openssl.cnf, the policy is named as "policy_anything"

So, use this other policy in the command as below to correctly sign the server certificate:
# openssl  ca  -config  openssl.cnf  -keyfile  casRootCAkey.key  -cert  casRootCAcert.crt -out mydomainNameServerCert.crt -policy policy_anything  -infiles mydomainNameServerCSR.csr

Now Result looks like below:
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: May 31 08:07:08 2013 GMT
            Not After : May 31 08:07:08 2014 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = NewJersey
            localityName              = Edison
            organizationName          = YourCompanyName
            organizationalUnitName    = IT
            commonName                = localhost [or a IP address or your company/project web site address]
            emailAddress              = admin@myadminmail.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                0C:C6:43:F9:D2:D3:A2:0B:4E:0B:7A:68:12:CD:7B:A3:22:8E:C9:0C
            X509v3 Authority Key Identifier:
                keyid:31:12:87:CF:22:12:06:CE:C9:AF:CB:A3:32:0E:EB:21:84:59:BC:D3

Certificate is to be certified until May 31 08:07:08 2014 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

Pictorial /screenshot result of above is show below:





















Now all your directory structures should look like below:












Also you can see below, the file generated this time has some size:



Now we are done with Successfully creating and Signing a Server certificate.


3. Lets create a Client certificate first and then we will sign it using similar procedure as above.
I am still in "/etc/pki/tls/tomcat6-CAS-certs" directory.

3.(a). Create a key (key-pair) file for the Client:
# openssl genrsa -out client.key 2048
Result:
Generating RSA private key, 2048 bit long modulus
............................+++
............................+++
e is 65537 (0x10001)

3.(b). Now lets Create a certificate signing request (CSR) for the Client this time:
# openssl req -new -key client.key -out client.csr -config openssl.cnf

Result:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:Texas
Locality Name (eg, city) [Default City]:Dallas
Organization Name (eg, company) [Default Company Ltd]:AName
Organizational Unit Name (eg, section) []:Dev
Common Name (eg, your name or your server's hostname) []:Mr. ABC XYZ
Email Address []:abc@gmail.com

Please enter the following 'extra' attributes

to be sent with your certificate request
A challenge password []:changeit
An optional company name []:AName

3.(c). Now lets get this csr signed from the CA and will generate in turn the client certificate for us:

# openssl ca -config openssl.cnf -keyfile  casRootCAkey.key  -cert casRootCAcert.crt -out client.crt -policy policy_anything  -infiles client.csr

Result is shown below:





















Now listing directory, should return us like below:










4. Now Lets Use PKCS#12, to bundle certificates with its private key:
PKCS#12 files will be used to import certificates into Java keystores and also to import into client browsers.

4.(a). Bundling server cert file (mydomainNameServerCert.crt) and Exporting as pkcs12 file (mydomainNameServerCertAsPK12.p12) format for Server:
# openssl pkcs12 -export -in mydomainNameServerCert.crt  -inkey  mydomainNameServerKey.key  -out mydomainNameServerCertAsPK12.p12  -name servercertificate
Result :
Enter Export Password: [abc123]
Verifying - Enter Export Password: [abc123]

4.(b). Bundling client cert file (client.crt) and Exporting as pkcs12 file (client.p12) format for Client:
# openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name clientcertificate

Where "servercertificate" and "clientcertificate"  are alias names for server and client pk12 files respectively.


5. Configuring the Tomcat to enable the SSL and to use a JKS (JavaKeyStore) file:

Use the "keytool" command to generate the JKS file for you. keytool is found inside $JAVA_HOME/bin  where Java version should be 1.6 or greater and assuming that you have the PATH variable configured to include $JAVA_HOME/bin

5.(a). Lets generate a JKS (myServerKeystore.jks) file and use it to import the pk12 server file:
# keytool  -importkeystore  -deststorepass changeit  -destkeypass  changeit  -destkeystore myServerKeystore.jks  -srckeystore  /etc/pki/tls/tomcat6-CAS-certs/mydomainNameServerCertAsPK12.p12   -srcstoretype PKCS12  -alias  servercertificate

Result:
Enter source keystore password:
If you are getting an error like below, please follow this link (otherwise proceed to configuring server):
Error:
keytool error: java.io.IOException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded

5.(b). Copy the JKS (myServerKeystore.jks) file from /etc/pki/tls/tomcat6-CAS-certs/  into  $CATALINA_HOME/conf :

tomcat6-CAS-certs]#  cp  myServerKeystore.jks  /usr/local/apache-tomcat-6.0.37/conf

5.(c). Configuring the Server (Tomcat in our case) to use the JKS file created above:
Now edit the "server.xml" file located at $CATALINA_HOME/conf  (or any as per your Tomcat server's configuration):

Look for the section starting with :  <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
This might be commented out. Please Uncomment this block and edit it to include the JKS file location something as below (change the path of JKS file as per your file location in the system):

   <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               keystoreFile="/usr/local/apache-tomcat-6.0.37/conf/myServerKeystore.jks"   
               keystorePass="changeit" clientAuth="false" sslProtocol="TLS" />

Change the keystoreFile's location accordingly depending on your system (for example if you are in Windows, you can use something like  keystoreFile="d:/apache-tomcat-6.0.37/conf/myServerKeystore.jks")

Use the alias and password that you provided when you created PKCS#12 file above.

Now Restart the Tomcat, check for any errors, and there should be none, if some please follow my other posts for related errors.
Type  https://localhost:8443   or  https://< your server's/computer's IP address running tomcat >:8443

Now you should get a page like below showing an warning that, This connection is Untrusted:














Now if you click, I understand the Risks (in Firefox browser), and click Add Exception.
And in next window make sure to uncheck the field "Permanently store this exception"  as shown below and then click Confirm Security Exception:

















Now you should see the Apache tomcat's default page running with HTTPs (SSL) successfully.

5.(d). Configuring / Making Browsers trust the server's root CA certificate as not to get above exception page each time:
Please follow this post: http://java-with-shiva.blogspot.in/2013/06/making-our-browsers-trust-servers-root.html

6. Configure the server to require client authentication:

6.(a).  Create a client JKS file (named as cacerts.jks) using the root CA file (casRootCAcert.crt):
# keytool -import -keystore cacerts.jks -storepass changeit -alias casclient_ca  -file  casRootCAcert.crt

6.(b).  Copy the client jks file from /etc/pki/tls/tomcat6-CAS-certs/  into  $CATALINA_HOME/conf:
#  cp  cacerts.jks  /usr/local/apache-tomcat-6.0.37/conf

6.(c).  Configure the server's config file (server.xml) to have the cacerts.jks file as below:

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150"  scheme="https"  secure="true"
               truststoreFile="/usr/local/apache-tomcat-6.0.37/conf/cacerts.jks"  truststorePass="changeit"
               keystoreFile="/usr/local/apache-tomcat-6.0.37/conf/myServerKeystore.jks"  
               keystorePass="changeit"  clientAuth="false"  sslProtocol="TLS" />


Test your SSL connection by restarting application.
Now either you may get Success getting your application running under SSL configuration or you might get below exception:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provid er.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

So, please follow this post, to get a solution.


References I followed:
http://virgo47.wordpress.com/2010/08/23/tomcat-web-application-with-ssl-client-certificates/
http://www.crsr.net/Notes/SSL.html
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html
http://stackoverflow.com/questions/17167691/javax-net-ssl-sslhandshakeexception-java-security-cert-certificateexception-no
https://wiki.jasig.org/display/CASUM/SSL+Troubleshooting+and+Reference+Guide
http://stackoverflow.com/questions/9619030/resolving-javax-net-ssl-sslhandshakeexception-sun-security-validator-validatore?rq=1