继上一篇介绍如何在多种语言之间使用SSL加密通信,今天我们关注Java端的证书创建以及支持SSL的NioSocket服务端开发。完整源码 一、创建keystore文件
网上大多数是通过jdk命令创建秘钥文件,但是有时候我们需要将配套的秘钥以及证书让多个模块来使用,他们很可能是由不同语言开发。在这样的情形下,我们通常会选择openssl。
生成服务端的秘钥文件openssl genrsa -out server.key 2048
这个秘钥文件是经过Base64编码后生成的,你可以使用文本工具打开,有时候这样的编码文件又称为pem文件。
创建基于当前秘钥的证书请求文件openssl req -new -key server.key -out server.csr
生成证书请求文件会要求你输入一些相关信息,这些信息会同秘钥一起被加密存储在.csr文件中。它将被用来向正规的CA机构去申请证书。它也是经过Base64编码后的。
申请X509证书openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
我们申请自签名的X509证书,有效期1年,证书包含了公钥和相关信息。由于自签名证书不是由公认的CA机构签发,因此使用它来作为服务端证书的时候,浏览器会提示告警信息。不过这不妨碍我们在内部环境中使用。
创建PKCS#12文件openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
PKCS#12是秘钥交换的标准证书。在加密通信的过程中,如果所有的信息都使用非对称加密,性能和时间损耗都非常大。因此,根据SSL握手规则,通信双方首先利用非对称加密算法协商出一个临时通信秘钥,然后在本次会话中仅使用基于当前秘钥对信息进行对称加密。会话结束即丢弃,不保存不复用。p12文件中包含了之前生成的私钥信息和申请的公钥信息及所有相关数据。
利用JDK生成keystore证书keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks -srcstoretype pkcs12 -deststoretype jks
这样生成的证书由于使用的是同一个私钥文件,因此.jks文件与.crt文件是同源的。在多语言支持的大系统中它们可以相互认证,也便于统一管理。
请注意,牵涉到加密通信的系统往往都比较复杂,证书链都必须统一保存。很少会使用各自的工具在不同的场景下独立使用,因此即使是Java开发者也依然应该掌握如何利用openssl生成完整证书的流程。
二、开发基于SSLEngine的非阻塞服务端服务端的开发与客户端区别不大,下面说明初始化和握手流程。其他部分的介绍可以参考我的上一篇博客。
服务端初始化服务端初始化的过程除了需要监听指定端口和处理客户端连接以外,主要是需要初始化SSLContext。SSLContext是整个SSL通信的基础,也可以认为是生成SSLEngine和SSLSession的工厂方法。具体通信的加解密过程又后者完成。
/**
* 初始化 SSL安全层
*/
private SSLContext sslContext;
private void initSSL() throws NoSuchAlgorithmException, KeyStoreException, CertificateException,
FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException {
sslContext = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("server.jks"), keystorepass);
kmf.init(ks, keypass);
tmf.init(ks);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new java.security.SecureRandom());
}