以下http client 4.5版本实现https请求的例子,实际运行过程中会碰到一些问题。
Https Client example
package org.apache.http.examples.client;import java.io.File;import javax.net.ssl.SSLContext;import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.TrustSelfSignedStrategy;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContexts;import org.apache.http.util.EntityUtils;/** * This example demonstrates how to create secure connections with a custom SSL context. */public class ClientCustomSSL { public final static void main(String[] args) throws Exception { // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(new File("my.keystore"), "changeit".toCharArray(), new TrustSelfSignedStrategy()).build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); try { HttpGet httpget = new HttpGet("https://httpbin.org/"); System.out.println("Executing request " + httpget.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); EntityUtils.consume(entity); } finally { response.close(); } } finally { httpclient.close(); } }} |
以上httpclient 4.5版本自带的示例https request运行时,访问https://httpbin.org/后抛出如下错误:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
解决方案
需要在浏览器中将对应的https://httpbin.org/请求域名的CA证书从浏览器里导出另存为httpbin.org.cer,然后用keytool命令导入到上述java代码指定的my.keystore文件中,my.keystore文件是一个包含公钥的truststore文件。
keytool -import -trustcacerts -alias httpbin.org -file httpbin.org.cer -keystore my.keystore -storepass changeit |
以上代码中的my.keystore文件是使用java自带的工具keytool生成的,另外需要注意的是这里生成的这份my.keystore文件,只能运行https://httpbin.org的请求,而不能运行其他如https://www.google.com的HTTPS请求。
下面介绍一下这个工具其及使用方法。
keytool 工具说明
keytool是java密钥和证书管理工具,可以用来创建包含公钥和密钥的keystore(database)文件,并且利用keystore文件来创建只包含公钥的truststore文件,并发布这个truststore给客户端使用,keystore里包含两种数据:
- 密钥实体
key entity: 密钥secret key又或者是私钥和配对公钥,采用非对称加密。 - 可信任的证书实体
trusted certificate entries: 只包含公钥。 - 别名
alias: 每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写。
用keytool创建keystore文件
创建keystore.jks和密钥对,密码为changeit,keytool工具的参数详细说明,参考官方文档:
keytool -genkey -alias server-alias -keyalg RSA -keypass changeit -storepass changeit -keystore keystore.jks |
生成了keystore.jks文件之后,根据需求不同分二种证书做法,一种是CA认证的证书,一种是自签名认证的证书,先说CA证书制作:
- 为上面的
keystore.jks生成证书请求文件(certificate signing request),即CSR文件。 - 提交
CSR给第三方权威CA机构认证,并获得有效的CRT证书文件。 - 将
CRT证书配置到tomcat,或者是nginx和apache服务器中。
CSR 文件生成
keytool -certreq -alias server-alias -keystore keystore.jks -keypass changeit -storepass changeit -file server.csr |
提交此server.csr给第三方CA机构认证,CA认证完成,会收到server.crt文件,使用下面的指令导入truststore.jks文件中,或者是直接配置到web服务器中,如nginx或者是apache2。
CA 证书导入
导入上述得到的server.crt证书到客户端使用的truststore.jks公钥文件中:
keytool -import -v -trustcacerts -alias server-alias -file server.crt -keystore truststore.jks -keypass changeit -storepass changeit |
自签名认证的证书生成和配置
有些开发环境中使用的SSL证书不需要第三方CA权威机构认证,或者不是提供给浏览器使用的,只是提供给java内部进行服务器和客户端之间相互验证,那么可以使用自签名认证的方式生成server.crt文件,并导入到java客户端使用的truststore文件中。
自签名证书的导出
keytool -export -alias server-alias -storepass changeit -keystore keystore.jks -file server.crt |
自签名证书导入到truststore文件
将上面生成的server.crt文件导入到cacerts文件中,这是提供给客户端使用的truststore文件:
keytool -import -v -trustcacerts -alias server-alias -file server.crt -keystore cacerts -keypass changeit -storepass changeit |
tomcat 单向认证配置示例
keystore.jks是服务器端的密钥文件,cacerts文件是提供给java客户端使用的公钥文件,更多详细说明可查看tomcat官网文档。
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="${catalina.home}/conf/keystore.jks" keystorePass="changeit" clientAuth="true" sslProtocol="TLS" /> |
tomcat 双向认证配置示例
增加truststore的相关配置如下,其中peer-cacerts是由客户端提供的truststore文件。
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="${catalina.home}/conf/keystore.jks" keystorePass="changeit" truststoreFile="${catalina.home}/conf/peer-cacerts" truststorePass="changeit" clientAuth="true" sslProtocol="TLS" /> |
keytool其他命令示例
查看单个证书 Check a stand-alone certificate
keytool -printcert -v -file server.crt |
列出keystore存在的所有证书
keytool -list -v -keystore keystore.jks -storepass changeit |
使用别名查看keystore特定条目
keytool -list -v -keystore keystore.jks -alias server-alias -storepass changeit |
删除keystore里面指定证书
keytool -delete -alias server-alias -keystore keystore.jks -storepass changeit |
更改keysore密码
keytool -storepasswd -new new_storepassword -keystore keystore.jks |
列出信任的CA证书
# for Mac OS export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts |
导入新的CA证书到信任证书
如果要新增特定的CA证书,并且不影响原来其他的HTTPS请求,则需要将CA证书导入到java自带的cacerts文件中,这个truststore文件的默认密码就是changeit。
keytool -import -trustcacerts -file /path/to/ca.crt -alias CA_ALIAS -keystore $JAVA_HOME/jre/lib/security/cacerts |