Sunteți pe pagina 1din 5

CA Certificates in Java

Example of two-way
authentication with https
Paddy McCarthy
National Center for Atmospheric Research
Research Applications Lab
Boulder, Colorado
25 November 2009
The following example demonstrates how to set up a secure (https) connection
using two-way authentication in Java. For programmers not using a J2EE framework,
this document serves to describe the mechanics of setting up a secure connection
using Java Secure Socket Extension (JSSE).
The Federal Aviation Administration provides an XML source of Notices to Airmen
(NOTAMs) data via the Aeronautical Information Data Access Portal (AIDAP), which is
managed by the National Airspace System (NAS) Aeronautical Information
Management Enterprise System (NAIMES). All connections to AIDAP are through
https, using two-way authentication.
Two-way authentication requires two keys -- a private key provided by NAIMES for
user authentication, and a public key downloaded from the AIDAP server to
authenticate the host. Potential users of AIDAP data must contact NAIMES to
request access, and are then provided a private key with a password. User
authentication is performed in the client code by creating an SSLSocketFactory with
the private key, and associating the SocketFactory with the HttpsURLConnection to
the AIDAP server. Authentication of the server is performed by downloading the
public key from the AIDAP server, and loading it into a local keystore that is
referenced by the client application via a runtime property.

Server Authentication
Authentication of the server is accomplished by downloading the public key from
the AIDAP server and inserting it into a local keystore. The keystore is then
referenced within the client application via a java property.
The symptom that indicates server authentication is not succeeding in the
handshake is a message like the following:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

A handy utility called InstallCert from sun.com takes care of acquiring the server's
public key and inserting it into a keystore. There is a link to this java class at:
http://blogs.sun.com/andreas/entry/no_more_unable_to_find
The source code itself is located at:
http://blogs.sun.com/andreas/resource/InstallCert.java
Download the source code, then compile it using:
javac InstallCert

Then run it using a command like the following:


java InstallCert www.aidaptest.naimes.faa.gov

When the utility requests input, enter the index number of the certificate you would
like to add to your keystore. Simply start with the first certificate, then re-run the
utility for each subsequent certificate until they have all been added to your
keystore.
This operation creates a new keystore file named "jssecacerts" in the directory
where you ran the utility. Feel free to rename and/or move the keystore.
In order to use the keystore in the handshake, the only necessary step is to
reference the keystore file in your application using the "trustStore" property. This
can be done on the command line with:
"-Djavax.net.ssl.trustStore=/path/to/jssecacerts"

or in the code with statements such as:


Properties systemProps = System.getProperties();
systemProps.put( "javax.net.ssl.trustStore", "/path/to/jssecerts");
System.setProperties(systemProps);

Note that if you have set a password on the keystore, you will need to also set the
"trustStorePassword" property using one of these methods. When specifying
property files such as these, they cannot be relative to the classpath -- instead, they
must be absolute paths or relative to a given jar file.
After performing these steps, server authentication should be handled when the
client connects to the server. As a result, the client application will no longer throw a
javax.net.ssl.SSLHandshakeException. If the server does not require user
authentication, connections should succeed at this point. However, if user
authentication is required by the server, the client should get an error message
back from the server indicating that access is denied.

For the AIDAP server, here is an example of what is returned when server
authentication is in place without user authentication (this will be different for every
server):
<HTML><HEAD><TITLE>Forbidden</TITLE></HEAD>
<BODY>
<H1>Forbidden</H1>
Your client is not allowed to access the requested object.
</BODY></HTML>

User Authentication
If user authentication is required by a server, the administrator of the server will
create a public/private key pair for your client and send you the private key and a
password. The administrator will register the public key with the server so your
client can be authenticated when it connects.
The following code fragment associates the private key with the
HttpsURLConnection by loading it into an SSLSocketFactory:
URL url = new URL( "https://www.aidaptest.naimes.faa.gov/aidap/XmlNotamServlet" );
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
File pKeyFile = new File("/path/to/your_private_key.pfx");
String pKeyPassword = "your_private_keypass";
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLSocketFactory sockFact = context.getSocketFactory();
con.setSSLSocketFactory( sockFact );

(Lots of exception handling ommitted)

Complete Code Example


Below is a complete code example for a client that connects to the AIDAP server
using two-way authentication in order to retrieve a dataset in XML format.

/**=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
** Copyright UCAR (c) 2009
** University Corporation for Atmospheric Research(UCAR)
** National Center for Atmospheric Research(NCAR)
** Research Applications Laboratory(RAL)

** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA


*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
* Created by Paddy McCarthy on Nov 25, 2009
*/
package edu.ucar.rap.util.net;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Properties;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class AidapClient {
public static void main( String[] args )
throws Exception
{
// Use the public key from the AIDAP server as the trust store for this client.
// (note: created this keystore using InstallCerts.java from sun.com)
Properties systemProps = System.getProperties();
systemProps.put( "javax.net.ssl.trustStore", "/d1/cvs_all/jssecacerts");
System.setProperties(systemProps);
try {
// Open a secure connection.
URL url = new URL( "https://www.aidaptest.naimes.faa.gov/aidap/XmlNotamServlet" );
String requestParams = "uid=adds&password=aAsS22.q&active=y&type=F";
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
// Set up the connection properties
con.setRequestProperty( "Connection", "close" );
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
con.setConnectTimeout( 30000 );
con.setReadTimeout( 30000 );
con.setRequestMethod( "POST" );
con.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
con.setRequestProperty( "Content-Length", Integer.toString(requestParams.length()) );
// Set up the user authentication portion of the handshake with the private
// key provided by NAIMES Tech Support.
// Based on an example posted by Torsten Curdt on his blog:
// http://vafer.org/blog/20061010073725 (as of Nov, 2009)
File pKeyFile = new File("/d1/cvs_all/aidapuser_1f5d_2011_03_1192.pfx");
String pKeyPassword = "UB#20abba";

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");


KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLSocketFactory sockFact = context.getSocketFactory();
con.setSSLSocketFactory( sockFact );
// Send the request
OutputStream outputStream = con.getOutputStream();
outputStream.write( requestParams.getBytes("UTF-8") );
outputStream.close();
// Check for errors
int responseCode = con.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = con.getInputStream();
} else {
inputStream = con.getErrorStream();
}
// Process the response
BufferedReader reader;
String line = null;
reader = new BufferedReader( new InputStreamReader( inputStream ) );
while( ( line = reader.readLine() ) != null )
{
System.out.println( line );
}
inputStream.close();
} catch (Exception e) { e.printStackTrace(); }
}
}

Contact Paddy McCarthy at: paddy@ucar.edu

S-ar putea să vă placă și