系统环境:
windows+jdk1.6.0_31+tomcat6.0+httpclient4.3.1
双向SSL配置
上一篇博文中生成了客户端证书来搭建单项SSL验证,双向SSL就是服务器端也要求客户端使用受信任的证书来访问。
1.生成证书
需要生成客户端证书(含私钥)并将客户端公钥证书导入至服务端信任证书库。
生成客户端证书
cd %JAVA_HOME%/bin keytool -genkey -v -alias client -keyalg RSA -storetype PKCS12 -keystore D:/lib/client.p12 -storepass client
创建了别名为server的证书(pcks12类型,含密钥的用户证书交换类型),文件名为client.p12,密码为client,密钥算法为RSA。
生成时需要输入多项证书信息,因不影响本次时间测试,不在此展示。
导出客户端证书
jdk keytool无法直接导入pcks12类型证书,需要从pcks12证书中导出cer格式证书(公钥证书,才能导入至证书库中
javakeytool -export -v -alias client -keystore D:/lib/client.p12 -storetype PKCS12 -rfc -file D:/lib/client.cer -storepass client
导出了名为client.cer的公钥证书,证书库密码为client
导入客户端证书至服务端信任库
keytool -import -v -file D:/lib/client.cer -keystore D:/lib/servertrust.keystore -alias client -keypass client -storepass servertrust
客户端公钥证书被导入到了名为servertrust.keystore的证书库中,别名为client,证书密码为client,证书库密码为servertrust。
查看服务端信任库
keytool -list -v -keystore D:/lib/servertrust.keystore -storepass servertrust
可以看到服务端信任库中包含客户端公钥证书
- 您的 keystore 包含 1 输入
- 别名名称: client
- 创建日期: 2014-1-2
- 输入类型: trustedCertEntry
- 所有者:CN=client, OU=client, O=client, L=client, ST=client, C=US
2.配置tomcat
配置conf/server.xml,找到上一篇中配置的
标签,将
clientAuth="false"修改为
clientAuth="true"使服务端要求验证客户端证书。
在标签中加入服务端信任库属性
truststoreFile="D:/lib/servertrust.keystore" truststorePass="servertrust"
使tomcat信任servertrust.keysto中的公钥证书。
重启tomcat后,访问 https://server:8443/发现无法访问,报ssl错误,因为服务端要求客户端证书,而客户端没有对应的证书(含私钥)。
3.导入客户端证书
首先使用浏览器测试客户端证书。
浏览器客户端导入客户端证书
双击客户端(私钥)证书D:/lib/client.p12,按提示进行操作,即可将客户端证书导入浏览器。
或:打开IE》选项》内容》证书》导入》选择证书类型p12、选择证书》输入客户端证书密码》确认,也可导入客户端证书。
导入客户端证书成功后,可以通过浏览器访问 https://server:8443/。
java客户端导入客户端证书
通过jdk访问时报java.net.SocketException: Software caused connection abort: recv failed,也是因为客户端没有受信任的证书。java程序不会导入浏览器中的客户端证书,需要我们手工指定客户端证书。
在上一篇已将服务端公钥证书导入至jdk信任证书库中(客户端信任服务端证书)后,只需要在jvm参数中加入
-Djavax.net.ssl.keyStore=D:/lib/client.p12 -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.keyStorePassword=client即在java程序中使用客户端自身的证书,程序即可访问 https://server:8443/。
4.指定客户端信任库
这里配置中使用的是jdk系统默认的证书库文件,程序中也可以指定使用其他信任证书库文件。
导入服务端证书至客户端信任库
keytool -import -v -alias server -file D:/lib/server.cer -keystore D:/lib/clienttrust.keystore -keypass server -storepass clienttrust
查看客户端信任库
keytool -list -v -keystore D:/lib/clienttrust.keystore -storepass clienttrust
配置客户端信任库
删除jdk默认信任证书库文件"%JAVA_HOME%\jre\lib\security\cacerts",再在jvm参数中加入
-Djavax.net.ssl.trustStore=D:/lib/clienttrust.keystore -Djavax.net.ssl.trustStorePassword=clienttrust -Djavax.net.ssl.trustStoreType=JKS程序即可使用该jvm访问https。
web应用在服务器启动时,会自动读取jdk默认的证书库来访问https服务。
使用spring security oauth2框架,通过https协议来访问rest服务时,通过上述配置步骤可以验证通过。
5.使用HttpClient访问双向SSL服务
Test类代码如下:
DefaultHttpClient httpclient = new DefaultHttpClient(); try { System.out.println("---keyStore---"); KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream keyinstream = new FileInputStream(new File( "D:/lib/client.p12")); try { keyStore.load(keyinstream, "client".toCharArray()); } finally { keyinstream.close(); } Enumeratione = keyStore.aliases(); while (e.hasMoreElements()) { System.out.println(e.nextElement()); } System.out.println("---trustStore---"); KeyStore trustStore = KeyStore.getInstance(KeyStore .getDefaultType()); FileInputStream instream = new FileInputStream(new File( "D:/lib/clienttrust.keystore")); try { trustStore.load(instream, "clienttrust".toCharArray()); } finally { instream.close(); } e = trustStore.aliases(); while (e.hasMoreElements()) { System.out.println(e.nextElement()); } // 可以不指定trustStore,直接使用系统默认位置%JAVA_HOME%\jre\lib\security\cacerts与new // SSLSocketFactory(keyStore,"client") SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "client", trustStore); Scheme sch = new Scheme("https", socketFactory, 8443); httpclient.getConnectionManager().getSchemeRegistry().register(sch); HttpGet httpget = new HttpGet( "https://server:8443/api/rest/test?access_token=86cb4392-425a-4b30-8e31-7250661a15c4"); System.out.println("executing request" + httpget.getRequestLine()); HttpResponse response = httpclient.execute(httpget); System.out.println("-------------response-------------"); System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); System.out.println(EntityUtils.toString(response.getEntity())); if (entity != null) { entity.consumeContent(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } httpclient.getConnectionManager().shutdown();
程序中使用client.p12作为客户端证书(含私钥,公钥证书client.cer已导入服务端信任证书库servertrust.jks),clienttrust.keystore作为客户端信任证书库(含服务端公钥证书server.cer),c/s两端服务器校验都成功后,SSL通道建立完成,即可直接发送信息。