Java中的客户端证书认证

问题描述:

我需要使用智能卡登录网站。我可以成功从智能卡获取密钥库,其中包含用户证书和不可导出私钥(这是一个常规的PrivateKey对象,但“getEncoded”方法返回null)。Java中的客户端证书认证

此网站:https://pst.giustizia.it/PST/authentication/it/pst_ar.wp 有一个登录链接,这个链接更改您可以访问。所以,就像用户所做的那样,我在我的Java应用程序中也这样做:我访问该页面一次以获取该链接,然后在该链接上执行SSL身份验证(有点像模拟访问页面并单击该链接) 。

这是我使用的代码:

public class SSLAuth 
{ 
    private static String LOGIN_PAGE = "https://pst.giustizia.it/PST/authentication/it/pst_ar.wp"; 
    private static String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0"; 

    private TrustStrategy trustStrategy = new TrustStrategy() 
    { 
     public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException 
     { 
      // Temporary work-around. I already know how to fix this 
      return true; 
     } 
    }; 

    public String authenticate(String pin) throws Exception 
    { 
     // Request KeyStore from smart card 
     KeyStore keyStore = Utility.digitalSigner.loadKeyStorePKCS11(); 
     SSLContext sslContext = SSLContexts.custom().useProtocol("TLSv1.2").loadTrustMaterial(keyStore, trustStrategy).build(); 

     // Get login token first 
     String loginToken = null; 
     { 
      Document document = Jsoup.connect(LOGIN_PAGE).ignoreContentType(true).userAgent(USER_AGENT).timeout(10000).followRedirects(true).get(); 
      Elements link = document.select("div > fieldset > p > a"); 
      loginToken = link.get(0).attr("abs:href"); 
     } 

     // Try to authenticate 
     HttpClient httpClient = HttpClients.custom().setUserAgent(USER_AGENT).setSSLContext(sslContext).build(); 
     HttpResponse response = httpClient.execute(new HttpGet(loginToken)); 
     if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) 
      return null; 

     return response.toString(); 
    } 
} 

我只需要在第一时间进行身份验证,因为一旦登录,网站只检查一个名为“JSESSIONID” cookie和客户端的用户代理字符串。我已经测试过了。一旦你有了这两个有效的参数,你甚至可以从另一个浏览器访问该页面。无论如何,“loadKeyStorePKCS11”方法为您提供了上述密钥库,其中包含证书链(99%,也许100%只有一个证书,因为我试过26个不同的智能卡,而且他们只有一个证书:用户的)和一个不可导出的私钥。 我试图在互联网上寻找解决方案,但它们都是关于PKCS#12的,我不需要它。

我试过使用不同的协议(SSL和TLS)和它的不同版本,但没有任何!

Firefox可以做智能卡身份验证,我知道我在程序中丢失了一些东西!当我在“httpClient”对象上调用“execute”方法时,它给了我一个例外:“handshake_failure”(SSLHandshakeException)。

如果我使用“loadKeyMaterial”而不是“loadTrustMaterial”,则会得到“unsupported_certificate”。

我真的不知道我在这一点上必须做什么! 你有什么建议吗? 在此先感谢!

+1

要查看失败的原因,请将'-Djavax.net.debug = ssl:handshake'添加到您的JVM。我的猜测是不可信的根CA或密码不匹配。 –

+0

[这里](https://www.dropbox.com/s/94m10zrfhpy4zmb/ConsoleMessage.txt?dl=0)可以找到整个控制台日志。前80行是来自智能卡驱动程序的调试消息。其余的来自握手调试。提前致谢! – FonzTech

+0

抱歉dropbox被我的代理服务器阻止。 –

感谢发布日志,我会做出这个答案,因为它太多了评论。

简答:服务器要求客户端证书,并且您没有或没有配置为提供可接受的客户端证书。

在日志搜索这样的:

main, READ: TLSv1 Handshake, length = 11296 
*** CertificateRequest 
Cert Types: RSA, DSS 
Cert Authorities: 

这是服务器要求你的客户端证书。以下是可接受的CA列表。您必须在您的密钥库中有一个由其中一个CA颁发的证书,并且必须为客户端的会话启用客户端证书。

日志的下一部分则这样说:

*** ServerHelloDone 
Warning: no suitable certificate found - continuing without client 
authentication 

不幸的是,似乎不必接受的服务器证书,或者未配置为客户证书模式,这就是为什么它减少你关闭并中止握手。

+0

嗨!感谢您的回复。 [这里](http://i.imgur.com/NXyhh68.jpg)你可以找到我的证书。它由可接受的CA中列出的CA颁发。所以我认为这个问题是另一回事......你有什么建议吗? – FonzTech

+0

我将“loadTrustMaterial”更改为“loadKeyMaterial”,现在我在“*** CertificateVerify”之后立即收到“unsupported_certificate”。这是什么意思?提前致谢! – FonzTech

+0

将您的证书从您的密钥库导出到文件,然后使用openssl检查它:'openssl x509 -in -inform der -text -noout'。检查签名和公钥算法。您的日志文件指出支持的证书类型是“RSA”和“DSS”。 –