我正在尝试使用带有clientAuth = true的自签名证书配置Tomcat6,然后使用HttpClient 4.1.x在客户端调用Tomcat服务器。
我按照http://virgo47.wordpress.com/2010/08/23/tomcat-web-application-with-ssl- client-certificates/中提供的说明进行操作,并且在从浏览器或从openssl客户端(我运行了命令“ openssl s_client -cert client.crt -key client.key -CAfile ca.crt -connect localhost:8443”)。
我面临的问题是HttpClient。我写了以下代码来创建HttpClient
private DefaultHttpClient creatHttpClient(KeyStore keyStore, char[] keyStorePassword, KeyStore trustStore, char[] trustStorePassword) { try { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); // This contains CA certs TrustManager[] tm = trustManagerFactory.getTrustManagers(); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, keyStorePassword); // This contain client private key KeyManager[] km = keyManagerFactory.getKeyManagers(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(km, tm, new SecureRandom()); SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 10000); HttpConnectionParams.setSoTimeout(params, 30000); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("https", 443, sslSocketFactory)); ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(schemeRegistry); final DefaultHttpClient httpClient = new DefaultHttpClient( clientConnectionManager, params); return httpClient; } catch(Exception e) { throw e; } }
为此,我收到了
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
我启用了调试
static { System.setProperty("javax.net.debug", "ssl,handshake,trustmanager"); }
我收到以下调试日志
main, handling exception: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty main, SEND TLSv1 ALERT: fatal, description = internal_error main, WRITE: TLSv1 Alert, length = 2 main, called closeSocket() main, IOException in getSession(): javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty main, called close() main, called closeInternal(true) main, called close() main, called closeInternal(true)
在这种情况下可能会出什么问题?我不想忽略ssl证书错误,而是想正确地进行身份验证。
我解决了自己的问题。问题是我正在使用以下方式加载密钥库和信任库
InputStream ksin = this.getClass().getResourceAsStream("client.jks"); InputStream tsin = this.getClass().getResourceAsStream("cacerts.jks");
&在Junit测试中使用了这些行,并且无法找到.jks文件。
我通过以下内容找出了答案
if(0 == keyStore.size()) { throw new RuntimeException("Keystore is empty"); } if(0 == trustStore.size()) { throw new RuntimeException("Truststore is empty"); }
在密钥库和信任库初始化之后。
因此,我将行更改为
InputStream ksin = Thread.currentThread().getContextClassLoader().getResourceAsStream("client.jks"); InputStream tsin = Thread.currentThread().getContextClassLoader().getResourceAsStream("cacerts.jks");
&一切正常。