Thursday, June 19, 2014

Authentication with Client Certificate over HTTPS/SSL using Java – Handshake

Authentication with Client Certificate over
HTTPS/SSL using Java – Handshake

To save somebody some time in the future, a step by step
instruction is provided below:
I assume you have a valid certificate or a chain of certificates,
whose root is acceptable by the server. The valid certificate
contains its private key. Run the following command to verify:
keytool -list -v -keystore "your certificate file"

Entry type: PrivateKeyEntry
Import your certificate and intermediate certificates into a
browser like IE or Firefox and test out the https URL. This step will
validate the certificates and save you a lot of troubles down the
road. Java version of the SSL implementation is not as simple/mature
as the browsers'. Please make sure all the certificates have not
expired.
Backup your keystore located at /your_home_directory/.keystore by
default and the truststore located at somewhere similar to
\Java\jre6\lib\security\cacerts
Use not-yet-commons-ssl utility to import your certificates into
the Java keystore format. Sample command is:
java -cp not-yet-commons-ssl-0.3.9.jar
org.apache.commons.ssl.KeyStoreBuilder
Customize the following java code, replace the static final
Strings to fit in your needs. Note that this implementation
forcefully use a specific alias to present the corresponding
certificate/certificate chain to the server. Somehow the default
KeyManager simply disqualifies my certificate to be presented to the
server.

Try to set
-Dsun.security.ssl.allowUnsafeRenegotiation=true
if you get the error message like:
javax.net.ssl.SSLException: HelloRequest followed by an unexpected
handshake message

Enabling
SSL for AXIS2 service and client

We
often encounter the satuation where requirement is to consume
webservice exposed on https. In this article we will investigate how
to consume webservice exposed over https using axis2. First lets see
how to enable SSL for AXIS2 services:

Enabling SSL on server
side for AXIS2 in tomcat:

You really don't need to do much
enable SSL for services deplyed in AXIS2. Just follow how to enable
SSL in tamcat.Add following in Server.xml of
tamcat.

Use axis2 1.5.3 in which
axis2.xml has https transportReceiver enabled by default, listening
on 8443 port so you don't need any configuration change in
axis2.xml.<transportReceiver
name="https"class="org.apache.axis2.transport.http.AxisServletListener"><parameter
name="port">8443</parameter></transportReceiver>

Prior
to this version of axis2 was using
org.apache.axis2.transport.nhttp.HttpCoreNIOSSLListener as
transportReceive which was having issues and not generating https
endpoint correctly.

Ideally
if we just provide end point URL starting with https, SSL connection
will be started and we don’t need any additional configuration.
Creation of secure connection will be taken care by JSSE. But then
trust store and keystore used by the JSSE would be default keystores
shipped with JDK.

In the practical/production scenarios user
should have capability to choose his truststore/keystore.user
may decide to trust a self signed certificate and keep it his local
truststore or different applications may use different
keystore/truststore.Above can be achieved by two ways:Approach
1:We can set truststore, password etc in system properties. This
will be picked by JSSE in SSL
handshake.Ex.System.setProperty("javax.net.ssl.trustStore","Your
truststore
path");System.setProperty("javax.net.ssl.trustStorePassword","your
trust store password");This approach will not be appropriate
for tooling since it sets keystore on JVM level. We should have
flexibility where we could attach different keystore/truststore for
different axis2 clint running in same JVM.

Approach 2:Apache
commons provide facility which allows us to customize the SSL socket
factory responsible for creation of secure socket. By customization I
mean ability to use user truststore/keystore in SSL handshake. To
achieve it we need to extend SecureProtocolSocketFactory interface.
In our custom socket factory implementation user refer its
Keystore/Truststore against default keystores.Apache commons
provide a reference implementation class named
AuthSSLProtocolSocketFactory for this purpose.

This class
takes Truststore/Keystore as argument to constructor which will be
referred later while initiating SSLContext. SSLContext is used to
create SSL Socket Factory.In your axis2 client code you need to
add following:Protocol authhttps = new Protocol ("https",
new AuthSSLProtocolSocketFactory (new url("keystore URL"),
"pwd", newURL("truststore URL"), "pwd"),
443);Protocol.registerProtocol("https", authhttps);

return
sslctx;}catch (Exception e){ e.printStackTrace();throw
new Exception("Error creating context for SSLSocket.",
e);}}}And
in the main call of a WS from Client I set the new SSLSocketFactory
Class:AxisProperties.setProperty("axis.socketSecureFactory","bo.socket.MyCustomSSLSocketFactory");

openssl

generate
a new private key and matching Certificate Signing Request (eg to
send to a commercial CA)

openssl
req -out MYCSR.csr -pubkey -new
-keyout MYKEY.key

add -nodes to
create an unencrypted private keyadd -config <openssl.cnf> if
your config file has not been set in the environment

decrypt
private key

openssl
rsa -in MYKEY.key
>> MYKEY-NOCRYPT.key

generate
a certificate siging request for an existing private key

openssl
req -out MYCSR.csr -key MYKEY.key
-new

generate
a certificate signing request based on an existing x509 certificate

keytool

keytool does
not support management of private keys inside a keystore. You need to
use another tool for that. If you are using the JKS format, that
means you need another java-based tool. extkeytool from
the Shibboleth distribution can do this.

If
the JVM truststore contains your certificate or the certificate of
the root CA that signed your certificate, then the JVM will trust
and thus might accept your certificate. The default truststore
already contains the root certificates of most commonly used
sommercial CA's. Use this command to add another certificate for
trust:

the
default password of the Java truststore is "changeit".if
$JAVA_HOME is set to the root of the JDK, then the truststore is it
$JAVA_HOME/jre/lib/security/cacertskeytool
does NOT support adding trust certificates to a PKCS12 keystore
(which is very unfortunate but probably a good move to promote JKS)

delete
a public certificate from a JAVA keystore (JKS; eg JVM truststore)