使用Java进行通用访问卡(CAC)身份验证

29

我基本上正在寻找一个学习如何使用Java与政府CAC卡进行接口的起点。

最终,我的目标是找出如何使用CAC卡认证(通过PIN码)来授权访问使用Tomcat/J2EE服务器托管的网站。

但我需要一个起点。所以我想我会先编写一个小的Java程序,仅从插入在键盘上的读卡器(带有数字键盘上方的CAC读卡器)中读取CAC卡信息。

通过搜索Google,我发现了cacard Java项目 (https://cacard.dev.java.net/),该项目已被OpenSSO项目替代。但我似乎找不到如何使用它连接到卡片,从卡片中读取等的示例代码。

有人知道我可以在哪里找到一些示例代码,以便我可以开始学习如何使用Java与CAC卡交互吗?

谢谢

编辑:

经过更多研究,我在想,我是否可以在server.xml文件的连接器元素中设置clientAuth="true"

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

clientAuth:如果您希望Tomcat要求所有SSL客户端提供客户端证书才能使用此套接字,请将此值设置为true。


我们希望确保用户必须重新输入他们的个人识别码才能访问我们基于浏览器的应用程序之一。有没有一种方法可以“取消认证”用户,以便他们必须再次进行身份验证?也许可以在服务器端关闭套接字,以便必须重新建立连接? - user240233
Paul,我想做的几乎和你做的一样。我对这些设备一无所知,从零开始。你能否发布你用于解决问题的代码--已经删除/模糊敏感信息?不管怎样,谢谢。祝周五愉快。Steve - Steve
3个回答

16

你是在创建网页应用程序还是尝试编写在客户端运行的软件(有点像你自己的网络浏览器)?

如果你正在创建一个网页应用程序,那么它基本上只需要标准的客户端证书认证。硬件令牌发出的证书对于服务器并没有太大的影响;如果你想要接受 只有 CAC 证书,你可以在服务器验证客户端证书时指定一组可接受的证书策略。(策略验证是 PKIX 验证的标准部分。)如果此应用程序面向政府客户,则需要与其安全团队密切合作,以确保您的解决方案符合其严格的要求。如果这是你的情况,请告诉我,我会更新我的答案,并列出我们遇到的一些问题。

如果你正在编写一个客户端,并需要访问物理读卡器,则可以尝试使用Sun PKCS #11 提供程序,自从 Java 1.5 以来就有了。我曾经尝试过这个提供商,并且您可以在另一个答案中了解更多信息。


在服务器上,您应该检查证书是否已被撤销。但是,其中一些 CRL 太巨大了——我们有超过 100 Mb 的 CRL 文件,而内置的 Sun 撤销检查程序无法很好地扩展到这种大小。

您还需要确保在 Tomcat 的“信任”密钥库中拥有正确的根 CA 证书(政府根 CA 证书稍微难以找到,因为他们希望用户正确验证它们)。我们还发现,除非用户手动导入中间证书到其浏览器中,否则 Firefox 不会发送整个证书链。


我们正在创建Web应用程序,那么我不需要担心读取卡片,对吗?浏览器应该将证书传递给我的Web应用程序,只要用户输入正确的PIN码,Tomcat就会允许他们访问。如果输入错误的PIN码,Tomcat将会拒绝访问,对吗? - Paul
没错。PIN码告诉卡片启用支持认证的签名操作。请参阅上面我的编辑获取更多提示。您使用的是美国国防部发行的CAC吗?这个应用程序是为政府机构开发的吗?如果是,那么您需要遵循许多额外的规则。 - erickson
1
+1. 您的Web服务器将只接收请求中的X.509证书。 - Kevin
@erickson,我有一个关于你的陈述“如果是这样,你需要遵循很多额外的规则”的问题。你介意在这里看一下吗?http://stackoverflow.com/questions/41833825/authenticate-a-user-using-cac-common-access-card-in-a-web-application-running - twindham

9
你需要创建一个名为card.config的文件,并在其中包含以下行:
name = myConfig
library = /path/to/library/that/implements/cac/card/reader 

然后尝试这个:

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());
         }
      }
   }
}

此时您拥有一个密钥库,可以使用它创建 SSL 套接字以与 HTTPS Web 服务器通信。


0

考虑使用证书认证,使用单点登录类型的应用程序,例如OpenSSOJOSSO。代理应该更容易嵌入,并且他们已经实现了大部分细节。如果您需要自己完成,他们还有很多与所需步骤相关的文档,例如:数字证书设置


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接