Contents

SSL with Java

HttpsClient所需要的knowledge

terminology

对称加密

加密和解密使用的密钥是相同的。

非对称加密

加密和解密使用的密钥是不相同的。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。

数字证书

针对非对称加密中,如何将公钥给客户端,客户端怎么信任这个公钥的安全性。因此,就出现了数字证书。

就如certificate含义一样,证书是由权威部门颁发的,它是可以信任的。那证书里有什么呢?当然要有公钥,其次是颁发机构和有效期。这有点像身份证,公钥是身份证号,机构是公安局,有效期是多少年。

证书内容具体如下:

  • issuer:证书的发布机构
  • valid from/to:证书有效期
  • public key:公钥
  • subject:证书的所有者,即是这个证书是发布给谁的
  • fingerprints:指纹。通过指纹算法 计算整个证书的hash值放在证书中。它是用来保证,证书内容没有被修改过。
  • signature algorithm:签名算法。有了指纹后,CA机构通过自己的私钥,将指纹和指纹算法加密后,放到证书中。CA加密的过程就是签名,加密的算法就是签名算法。

上面其实指出了cert怎么生成的,CA(Certificate Authority)会将issuer、valid from/to、public key、subject信息放到证书里,再用指纹算法计算一个hash值放在cert中(类似计算文本摘要,保证内容未被修改),最后通过自己的私钥将指纹和指纹算法签名后放在cert中(非对称加密 强保证了指纹的安全性)。

这样又有了新问题,要想验证证书是否合法,需要CA1的公钥,但怎么获取CA1公钥,怎么判断CA1公钥是正确的。

所以,为了确保上面的2个问题,就需要更牛的CA2用私钥给它签名,形成CA1证书。说CA2更牛,是因为用root CA的私钥去签名形成了CA2证书,它是可以通过root CA的公钥去验证的。

到此,就会发现 通过证书已经形成了一条信任链。这条信任链的根部就是root CA的根证书(会内嵌在操作系统或浏览器中)。而CA2的证书就是中介证书,它连接着CA1的证书。因此解密流程是:通过root CA的公钥解密CA2,成功继续解密CA1,获取最终通信公钥。所以,一般在访问https网站后,服务器会发送你end-user证书和中介证书,而rootCA证书浏览器中含有。

最后总结一下,要验证信任链中的某个证书的合法性,需要它issuer的公钥去解密。

X509

它是管理证书格式和用法的一个规范

PEM

它是一种编码方式采用base64,用于keys和certificates encoding。

PKCS12和JKS

它们都是将多组key和cert储存起来,并用密码保护起来的存储容器。

PKI

public key infrastructure公开密钥建设。有点抽象,简要说来,它是一种机制,可以保障网络间的通信安全。实现它的关键,就是公开public key形成证书。详解

Self-signed Certificates

自签发证书:它们不是被其他CA签名,而是证书创建者自己签发的。所有的根证书都是self-signed。所以,它的trust chain length = 1。

实践

上面理论太抽象,下面通过OpenSSL模拟证书的签发过程

搭建root CA和intermediate CA

详细参考

照着上面的文章做,生成root CA cert和intermediate CA cert,然后用intermediate CA来签发 client的证书。

主要用到命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
## generate root private key:
openssl genrsa -aes256 -out private/ca.key.pem 4096

## generate root self-signed certificate:
openssl req -config openssl.cnf \
      -key private/ca.key.pem \
      -new -x509 -days 7300 -sha256 -extensions v3_ca \
      -out certs/ca.cert.pem

## show root certificate
openssl x509 -noout -text -in certs/ca.cert.pem

## generate intermediate certificate signing request:
openssl req -config intermediate/openssl.cnf -new -sha256 \
      -key intermediate/private/intermediate.key.pem \
      -out intermediate/csr/intermediate.csr.pem
      
## using root CA configuration to sign the intermediate CSR:
openssl ca -config openssl.cnf -extensions v3_intermediate_ca \
      -days 3650 -notext -md sha256 \
      -in intermediate/csr/intermediate.csr.pem \
      -out intermediate/certs/intermediate.cert.pem
      
## verify the intermediate certificate against the root certificate:
openssl verify -CAfile certs/ca.cert.pem \
      intermediate/certs/intermediate.cert.pem

测试验证cert

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
## merge localhost and intermediate cert into one cert file:
## attention cat cert order
cat intermediate/certs/localhost.cert.pem intermediate/certs/intermediate.cert.pem > intermediate/certs/localhost-intermediate-chain.cert.pem

## verify
openssl verify -CAfile certs/ca.cert.pem -untrusted intermediate/certs/intermediate.cert.pem intermediate/certs/localhost-intermediate-chain.cert.pem
      
## start openssl server
openssl s_server -accept 12345 -cert intermediate/certs/localhost.cert.pem -key intermediate/private/localhost.pem -CAfile intermediate/certs/intermediate.cert.pem

## check an SSL connection. All the certificates (including Intermediates) should be displayed
openssl s_client -connect localhost:12345 -CAfile certs/ca.cert.pem

注意我们的openssl server启动后,client就可以模拟浏览器一样,通过Root CA cert校验server发来的证书并建立连接。同时通过浏览器访问https://localhost:12345 也可以看到Certificate trust chain 如下:

https://raw.githubusercontent.com/Fedomn/misc-blog-assets/master/ssl-certificate-trust-chain-test.png

参考s_server命令

one-way-ssl & two-way-ssl

一般的web应用采用的是单向SSL,因为用户数目广泛,且无需在通讯层对用户身份进行验证,一般都在应用逻辑层来保证用户的合法登入。

但是对于企业应用对接,可能会要求对客户端做身份验证。这时就需要做SSL双向认证。

我们上面的实践,是在模拟web网站SSL单向认证的步骤,大概清楚了证书签发的流程 与 trust chain是怎么运作的,后面通过java代码实现单向与双向的SSL认证。

java client

首先前提,我们模拟简单的self-signed SSL证书,也就是说trust chain length=1的情况。并测试了单向和双向SSL通信可能会遇到的问题。详见:HttpClient模拟代码

这里只介绍Java生态 SSL相关知识点:

truststore

一个文件,包含了许多根证书,通过它来验证信任其他证书。jre/jdk默认的truststore位置$JAVA_HOME/lib/security/cacerts

查看它的命令是:keytool -list -keystore $JAVA_HOME/lib/security/cacerts,默认密码是:changeit

keystore

一个文件,存储ssl通信时,所需要的私钥和证书。如果你是一个建立SSL上的server,证书用来发送给client,私钥则用来解密https建立连接时 客户端发来的对称加密密钥。

注意,这里不再引出https单向认证和双向认证具体过程,只要记住非对称加密只是在HTTPS建立连接时候,用来加密对称加密密钥的手段,所以HTTPS最终是通过对称加密通信。

config

针对HttpClient有代码级的配置,这里只记录一下针对jvm的配置:

1
2
3
4
5
6
7
8
9
-Djavax.net.debug=ssl

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.keyStore=xxx.p12
-Djavax.net.ssl.keyStorePassword=$PASS

-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.trustStore=xxx.jks
-Djavax.net.ssl.trustStorePassword=$PASS

keytool

Keytool is a utility bundled with the JRE for managing key pairs and certificates. This allows us to view/modify/create certificate stores in the Java world.

常用命令:

1
2
3
4
5
6
7
8
## show
keytool -keystore ./client.p12 -storepass password -list

## import cert
keytool -import -alias server-trust -file client.crt -keystore server-trust.p12 -storetype PKCS12 -storepass password

## delete cert
keytool -delete -alias testclient -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

reference