Common Access Card (CAC) Authentication Using Java
Asked Answered
D

3

30

I'm bascially looking for someplace to start learning how to interface with a government CAC card using java.

Ultimately, my goal is to find out how to use CAC card authentication (by PIN number) to authorize access to a website hosted using a Tomcat/J2EE server.

But I'll need somewhere to start. So I figure I'd start by writing a small java program to simply read the CAC card information from the CAC card which is inserted into a card reader on my keyboard (DELL keyboard with CAC reader above the numeric keypad).

By searching google, I found the cacard java project (https://cacard.dev.java.net/) which was replaced by the OpenSSO project. But I can't seem to find sample code of how to use it to connect to a card, read from a card, etc.

Does anyone know where I can find some sample code so that I can start learning how to interact with a CAC card using java?

Thanks

EDIT:

After researching more, I was thinking, would I be able to just set clientAuth="true" in the connector element in the server.xml file?

http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html

clientAuth: Set this value to true if you want Tomcat to require all SSL clients to present a client Certificate in order to use this socket.

Denison answered 12/2, 2009 at 23:39 Comment(3)
"We would like to ensure that the user has to renter their PIN to access the one of our browser-based applications. Is there a way to "deauthenticate" the user so that they have to authenticate again?" Maybe close the socket on the server side so that the connection has to be re-established?Sniffle
Paul, I want to do almost the exactly what you did. I know nothing about these devices and I am starting from square one. Would you care to post the code you used for a solution -- with the sensitive information redacted out/obscured? Thanks either way. Happy Friday. SteveDiffidence
For those who come here later, I have been looking into a very similar situation. From my research, it seems that this answer is the best process.Herminiahermione
C
16

Are you creating the web application, or trying to write software that runs at the client (sort of like your own web browser)?

If you are creating a web application, it's pretty much just standard client certification authentication. The fact that the certificate came from a hardware token doesn't change much for the server; if you want to accept only CAC certificates, you can specify set of acceptable certificate policies when the server validates the client certificate. (Policy validation is a standard part of PKIX validation.) If this application is for a government customer, you'll need to work closely with their security team to ensure that your solution meets their requirements, which can be stringent. If this is your scenario, let me know and I'll update my answer with some of the issues that we encountered.

If you are writing a client, and need to access the physical reader, you may be able to use the Sun PKCS #11 provider, since Java 1.5. I've experimented with this provider, and you can read more about it in another answer.


On the server, you should check that the certificate is not revoked. However, some of these CRLs are enormous—we had over 100 Mb worth of CRL files, and the built-in Sun revocation checker does not scale well to this size.

You will also need to make sure that you have the right root CA certificates in Tomcat's "trust" key store (the government root CA certs are little harder to find because they want to make sure users are verifying them properly). We also found that Firefox does not send the entire certificate chain unless users import the intermediate certificates into their browser manually.

Camaraderie answered 13/2, 2009 at 0:31 Comment(5)
We're creating the web application. So I shouldn't have to worry about reading the card at all correct? The browser should pass the certificate to my web app and as long as the user enters the correct PIN, Tomcat would allow them access. Wrong PIN, Tomcat would deny access right?Denison
That's right. The PIN tells the card to enable the signature operation that supports authentication. See my edits above for more tips. Are you using CACs issued by the US DOD? Is this application for a government agency? There are a lot of extra rules you'll need to follow if so.Camaraderie
+1. Your web server will just receive an X.509 cert in the requestOrthopter
@Camaraderie I have a question about your statement "There are a lot of extra rules you'll need to follow if so." Would you mind taking a look at it here? #41834325Winterkill
erickson, I have the same question as @twindham, and have posted it on SO. What are the "extra rules" you mentioned?Herminiahermione
D
9

You need to create a file called card.config and include the following lines in it:

name = myConfig
library = /path/to/library/that/implements/cac/card/reader 

And then try this:

import java.io.*;
import java.util.*;

import java.security.cert.CertificateException;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;

import java.security.KeyStore;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

public class Test  
{
   public static void  main(String arg[]) throws Exception
   {
       try
       {   
         //Create our certificates from our CAC Card
         String configName = "card.config";
         Provider p = new sun.security.pkcs11.SunPKCS11(configName);
         Security.addProvider(p);

         //Get the pin from user entered data
         Console c = System.console();
         char[] pin = c.readPassword("Enter your PIN: ");
         KeyStore cac = null;

         cac = KeyStore.getInstance("PKCS11");
         cac.load(null, pin);

         showInfoAboutCAC(cac);

      }
      catch(Exception ex)
      {
         //System.out.println("*" + ex.getMessage());
         ex.printStackTrace();
         System.exit(0);
      }
   }

   public static void showInfoAboutCAC(KeyStore ks) throws KeyStoreException, CertificateException
   {
      Enumeration<String> aliases = ks.aliases();

      while (aliases.hasMoreElements()) 
      {
         String alias = aliases.nextElement();
         X509Certificate[] cchain = (X509Certificate[]) ks.getCertificateChain(alias);

         System.out.println("Certificate Chain for : " + alias);
         for (int i = 0; i < cchain.length; i ++)
         {
            System.out.println(i + " SubjectDN: " + cchain[i].getSubjectDN());
            System.out.println(i + " IssuerDN:  " + cchain[i].getIssuerDN());
         }
      }
   }
}

At this point you have a keystore that you can use to create the ssl socket to talk to the https web server.

Doge answered 27/10, 2009 at 18:48 Comment(1)
I really like the idea you present here, but could you provide some more details about what library implements the cac card reader? Is there an open-source library that you'd recommend, or is that I library that I'd need to create?Herminiahermione
M
0

Look into using cert authentication using an SSO type application such as OpenSSO or JOSSO. The agent should be simpler to embed, and they have already implemented most of the details. If you need to do it yourself, they also have a lot of documentation related to the steps needed such as: digital certificates setup

Mortenson answered 8/7, 2009 at 22:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.