There are times when you need to secure access to a web application or service and you would like to use client certificates for authentication. This is a common way to secure web service calls and safely authenticate the client. This is not really difficult to do, but there is a lot of steps involved
I recently did this for a project and I was amazed at how confusing it was and the fact there was no website that went thru all the steps. I would find a little documentation here and there from a lot of different websites. I am putting the full instructions here for other as well as myself in the event I have to do this again. This example uses openssl, keytool, and jetty.jar
.jar to your ssl working directory
6.) Execute jetty tool to import key
# java -classpath ./jetty-6.0.2.jar org.mortbay.jetty.security.PKCS12Import server.p12 server.keystore
Copy the server.keystore to a place for tomcat, I usually copy it to $TOMCAT_HOME/conf directory
Configure Tomcat
Uncomment the SSL adaptor configuration in server.xml and change clientAuth="true" and add entries for the keystore and truststore. Your adaptor should look something like this
<Connector port="8443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="/usr/local/apache-tomcat-5.5.27/conf/server.keystore"
keystorePass="yourpassword"
truststoreFile="/usr/local/apache-tomcat-5.5.27/conf/server.truststore"
truststorePass="yourpassword" />
You have to change clientAuth="true" or the server will not use the client certificate to authenticate
Use the p12 certificate in Java (Using commons http client)
EasySSLProtocolSocketFactory easySSL = new EasySSLProtocolSocketFactory();
KeyMaterial keyMaterial = new KeyMaterial("c:xmlclient.p12", password.toCharArray());
easySSL.setKeyMaterial(keyMaterial);
Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easySSL, 443);
Protocol.registerProtocol("https", easyhttps);
MultiThreadedHttpConnectionManager connectionManager = new
MultiThreadedHttpConnectionManager();
connectionManager.getParams().setDefaultMaxConnectionsPerHost(500);
connectionManager.getParams().setMaxTotalConnections(2000);
HttpClient httpClient = new HttpClient(connectionManager);
PostMethod postMethod = new PostMethod();
try {
postMethod.setURI(new URI("https", null, host, 443, url));
postMethod.setRequestEntity(new ByteArrayRequestEntity(payload.toString().getBytes()));
postMethod.setRequestHeader("Content-Type", "text/xml");
httpClient.executeMethod(new HostConfiguration(), postMethod, new HttpState());
String responseXml = postMethod.getResponseAsString();
}
finally {
postMethod.releaseConnection();
}
Use the .p12 file in C#
private string SendXml(string url, string data)
{
X509Certificate certificate = new X509Certificate2(
@"c:xmlclient.p12,"password");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ClientCertificates.Add(certificate);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";
byte[] postBytes = Encoding.ASCII.GetBytes(data);
request.ContentType = "text/xml";
request.ContentLength = postBytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();
// Pick up the response:
string result = null;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
StreamReader reader = new StreamReader(response.GetResponseStream());
result = reader.ReadToEnd();
}
log.Debug(result);
return result;
}
How to use a real certificate when you get it
Now what to do when you get the real certificate? You need to create a new keystore and truststore
You would do step 3.1 and 3.2 as usual but step 3.3 would not be necessary because this is the certificate you will get from verisign or where ever you order your SSL cert.
Step 3.4 gets replaced with this:
You need to convert the private key and certificate to DER format first
# openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER
# openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER
Now import the existing private key and certificate DER files into a java keystore. keytool does not support this so you have to use java to do this. This site has good instructions
To sum it up you would run the code like this
# java ImportKey key.der cert.der
Using keystore-file : /home/user
/keystore.ImportKey
One certificate, no chain.
Key and certificate stored.
Alias:importkey Password:importkey
Downlaod ImportKey.class
Downlaod ImportKey.java
After you have imported the key you need to change the alias to tomcat
# keytool -changealias -alias importkey -destalias tomcat -keystore keystore.ImportKey
Change the keystore password to something you want
# keytool -storepasswd -new -keystore keystore.ImportKey
Now convert the root ca public key into DER format and import into keystore
# openssl x509 -in trustca.pem -inform PEM -outform DER -out trustca.crt
# keytool -import -alias trustca -keystore keystore.ImportKey -file trustca.crt
Now your keystore is set to work with tomcat, rename the keystore to whatever you had before and replace the file you were pointing to in tomcat config.
Create the new truststore by importing the certificate you received and the ca public cert into the truststore
# keytool -import -v -keystore server.truststore -storepass yourpassword -file trustca.pem
# keytool -import -v -keystore server.truststore -storepass yourpassword -file servercert.pem
Now copy your new truststore to the file your tomcat config is pointing to
Now you need to create new client certificates that are signed with the server certificate like this:
Steps 4.1, 4.2, and 4.4 all remain the same. NOTE: in step 4.2 the organization MUST be the server name the server certificate is for eg. yourserver.com. You just need to replace step 4.3 with the following
# openssl ca -verbose -in client-req.pem -out client.pem -notext -keyfile server-key.pem -cert server.pem -config ./openssl.cnf -days 365
Make sure your openssl.conf is pointing to the server cert instead of your demoCA certs
Powered by AkoComment 1.0 beta 2!