java 命令行工具之 keytool篇

以下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里包含两种数据:

  1. 密钥实体key entity: 密钥secret key又或者是私钥和配对公钥,采用非对称加密。
  2. 可信任的证书实体trusted certificate entries: 只包含公钥。
  3. 别名alias: 每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写。

用keytool创建keystore文件

创建keystore.jks和密钥对,密码为changeitkeytool工具的参数详细说明,参考官方文档

 keytool -genkey -alias server-alias -keyalg RSA -keypass changeit -storepass changeit -keystore keystore.jks

生成了keystore.jks文件之后,根据需求不同分二种证书做法,一种是CA认证的证书,一种是自签名认证的证书,先说CA证书制作:

  1. 为上面的keystore.jks生成证书请求文件(certificate signing request),即CSR文件。
  2. 提交CSR给第三方权威CA机构认证,并获得有效的CRT证书文件。
  3. 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

References

  1. keytool - Key and Certificate Management Tool
  2. To Use keytool to Create a Server Certificate
  3. httpclient-4.5 custom ssl request
  4. Edit the Tomcat Configuration File