netty中实现双向认证的SSL连接
1. 前期准备工作
双向证书认证的双方称为client和server,首先为client和server生成证书。由于仅仅是自己学习使用,因此可以在本地自建一个CA,然后用CA的证书分别签发client和server的证书。CA的创建和签发使用OpenSSL。
在windows环境上安装OpenSSL,然后依据OpenSSL目录下的openssl.cnf中[ CA_default ]的配置创建相应的文件夹和文件
demoCA/ —- CA的根目录
|– newcerts/—- CA签发出去的证书
|– private/ —- CA自己的私钥,默认名称是cakey.pem
|– serial —- 存放证书序列号的文件
|– index.txt —- 签发过的证书的记录,文本文件
serial这个文件中可以初始写入一行记录,包含两个字符01,表示下一个签发的证书采用的序列号是01
接下来生成CA自己的公私钥(public/private key),生成证书签名请求(CSR, Certificate Signing Request)文件并对该请求进行自签名
在openssl的根目录下运行
openssl genrsa -out ./demoCA/private/cakey.pem 2048
genrsa —- 同时生成public key和private key
很多人将genrsa解释为只生成private key,这是不对的。可以用下面的命令从文件中解出公钥
openssl rsa -in cakey.pem -pubout > capublickey.pub
注意最后的数字2048表示生成的RSA公私钥的长度
JDK7中对证书检查要求公钥的长度最少为1024位,否则会抛出异常
java.security.cert.CertPathValidatorException: Algorithm constraints check failed
该长度限制是可以配置的,配置文件路径是JAVA_HOME/jre/lib/security/java.security
jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
然后用上面生成的公私钥文件创建一个证书签名请求文件
openssl req -new -key ./demoCA/private/cakey.pem -out careq.pem
req —- 创建CSR或者证书
-key —- openssl从这个文件中读取private key
careq.pem的内容格式是
—–BEGIN CERTIFICATE REQUEST—–
MIICnzCCAYcCAQAwWjELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQH
… …
ZYu4AZp0VzqnQzCTeYTbC+AsA0RrPVjr95Il46AHvhq2JQpFw8DhrS8Ja1VburI4
ngFK
—–END CERTIFICATE REQUEST—–
最后将该请求文件给CA机构做签名,但我们现在是想在本地建CA,因此自己对该文件进行自签名即可。
openssl ca -selfsign -in careq.pem -out cacert.pem
其实,上面生成CSR然后做自签名的两个步骤可合并到一步完成
openssl req -new -x509 -key ./demoCA/private/cakey.pem -out cacert.pem
至此,我们已经建立了自己的CA,接下去来分别签发client和server的证书。
创建client和server的证书、key store和trust store
以创建client的证书为例。由于jdk自带的keytool工具可以方便的创建key store和公私钥,因此公私钥和csr的创建直接使用keytool
key store和trust store分别对应于ssl握手证书认证中自己的证书和自己所信任的证书列表,二者的文件格式相同,不同之处是key store里面包含ssl握手一方的公私钥和证书,trust store里面包含ssl握手一方所信任的证书,一般没有这些证书所对应的私钥
- 生成client的keystore 和key pair
keytool -genkey -alias client -keyalg RSA -keystore client.keystore -keysize 2048
- 生成csr
keytool -certreq -alias client -keystore client.keystore -file client.csr
- 用本地CA对该csr签名
client证书中我们想添加证书的一项扩展,比如client id,用来区分client的身份,因此需要额外的一份扩展文件client.cnf,内容如下[v3_req] 1.2.3.412=ASN1:UTF8String:0000001444
可以将该csr和client.cnf文件拷贝到openssl根目录下,运行
openssl ca -in client.csr -out client.pem -config ./openssl.cnf -extensions v3_req -extfile client.cnf
- 将签过名的client.pem导入到keystore文件中
在导入之前,需要先将CA的证书导入keystore文件keytool -keystore client.keystore -importcert -alias CA -file cacert.pem
然后导入client自己的证书。注意alias是client,与生产keystore和key pair的必须匹配
keytool -keystore client.keystore -importcert -alias client -file client.pem
keystore文件内容的查看可以使用
keytool -list -v -keystore client.keystore
或者使用可视化工具KeyStore Explorer查看
5. 创建client的trust store
由于server的证书也是本地CA签发的,因此client只要信任CA的证书那么自然会信任CA签发出的证书,所以我们只需将CA的证书导入trust store即可
<span style="white-space:pre"> </span>keytool -import -alias cacert -file cacert.pem -keystore clienttruststore.keystore
由于clienttruststore.keystore文件尚不存在,此命令首先创建该文件并将CA的证书导入该trust store
server的证书和key store和trust store可类似创建
keytool -genkey -alias server -keyalg RSA -keystore server.keystore -keysize 2048
keytool -certreq -alias server -keystore server.keystore -file server.csropenssl ca -in server.csr -out server.pem -config ./openssl.cnfkeytool -keystore server.keystore -importcert -alias CA -file cacert.pem
keytool -keystore server.keystore -importcert -alias server -file server.pem
keytool -import -alias ca -file cacert.pem -keystore servertruststore.keystore
2. 创建SSL通讯的client和server
由于netty 5现在只有alpha版本,因此保险起见使用4.0.24.final版本的netty。
netty的SSLContext提供了newClientContext来为client创建ssl context,但查看其源码未发现能支持双向认证,即client端的ssl context只接收一个trust store,而不能指定自己的证书以供server端校验。仿照netty example下的securechat的ssl实现但做了修改
首先创建一个能提供client和server的ssl context的工具类,分别加载server和client的key store和trust store里面的证书
public class SslContextFactory
{private static final String PROTOCOL = "TLS"; // TODO: which protocols will be adopted?private static final SSLContext SERVER_CONTEXT;private static final SSLContext CLIENT_CONTEXT;static{SSLContext serverContext = null;SSLContext clientContext = null;String keyStorePassword = "aerohive";try{KeyStore ks = KeyStore.getInstance("JKS");ks.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\server.keystore"), keyStorePassword.toCharArray());// Set up key manager factory to use our key storeKeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf.init(ks, keyStorePassword.toCharArray());// truststoreKeyStore ts = KeyStore.getInstance("JKS");ts.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\servertruststore.keystore"), keyStorePassword.toCharArray());// set up trust manager factory to use our trust storeTrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(ts);// Initialize the SSLContext to work with our key managers.serverContext = SSLContext.getInstance(PROTOCOL);serverContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);} catch (Exception e){throw new Error("Failed to initialize the server-side SSLContext", e);}try{// keystoreKeyStore ks = KeyStore.getInstance("JKS");ks.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\client.keystore"), keyStorePassword.toCharArray());// Set up key manager factory to use our key storeKeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf.init(ks, keyStorePassword.toCharArray());// truststoreKeyStore ts = KeyStore.getInstance("JKS");ts.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\clienttruststore.keystore"), keyStorePassword.toCharArray());// set up trust manager factory to use our trust storeTrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmf.init(ts);clientContext = SSLContext.getInstance(PROTOCOL);clientContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);} catch (Exception e){throw new Error("Failed to initialize the client-side SSLContext", e);}SERVER_CONTEXT = serverContext;CLIENT_CONTEXT = clientContext;}public static SSLContext getServerContext(){return SERVER_CONTEXT;}public static SSLContext getClientContext(){return CLIENT_CONTEXT;}... ...
}
io.netty.example.securechat.SecureChatClientInitializer类的构造器接收一个io.netty.handler.ssl.SslContext类型的对象,这个SslContext的对象最终被用来创建SslHandler,而上面factory产生的是javax.net.ssl.SSLContext的对象,因此可以做改动如下
public class ClientInitializer extends ChannelInitializer
{private final javax.net.ssl.SSLContext sslCtx;public ClientInitializer(javax.net.ssl.SSLContext sslCtx)
{this.sslCtx = sslCtx;
}@Override
public void initChannel(SocketChannel ch) throws Exception
{ChannelPipeline pipeline = ch.pipeline();SSLEngine sslEngine = sslCtx.createSSLEngine(Client.HOST, Client.PORT);sslEngine.setUseClientMode(true);pipeline.addLast(new SslHandler(sslEngine));// On top of the SSL handler, add the text line codec.pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());// and then business logic.pipeline.addLast(new ClientHandler());
}
}
最后添加jvm参数
-Djavax.net.debug=ssl,handshake
来查看ssl握手过程控制台的log
具体实现请参考附件源码。
http://download.csdn.net/detail/virgilli/8373319
附录:
openssl的配置
对证书签名时,遇到openssl异常failed to update database TXT_DB error
有可能是因为签名的csr文件的subject中的一项或几项在该CA之前签发过的证书中已经出现过或者是csr中提供的国家/省份等等的名称与CA自己的不相同,这些限制都可以在openssl.cnf文件中修改
unique_subject=no
[ policy_match ]
countryName = match
#stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
netty中实现双向认证的SSL连接相关推荐
- SSL双向认证和SSL单向认证的区别
双向认证 SSL 协议要求服务器和用户双方都有证书.单向认证 SSL 协议不需要客户拥有CA证书,具体的过程相对于上面的步骤,只需将服务器端验证客户证书的过程去掉,以及在协商对称密码方案,对称通话密钥 ...
- SSL双向认证和SSL单向认证的流程和区别
refs: SSL双向认证和SSL单向认证的区别 https://www.jianshu.com/p/fb5fe0165ef2 图解 https 单向认证和双向认证! https://cloud.te ...
- Openssl 建立双向认证的 SSL/TLS 通信
话不多说,直接利用 Openssl 在两台 uBuntu 之间建立双向认证的 SSL/TLS 通信. 笔者使用的 Openssl 版本为 OpenSSL 1.1.1 11 Sep 2018 生成证书 ...
- SSL/TLS 双向认证(一) -- SSL/TLS 工作原理
本文部分参考: https://www.wosign.com/faq/faq2016-0309-03.htm https://www.wosign.com/faq/faq2016-0309-04.ht ...
- SSL/TLS 双向认证(一) -- SSL/TLS工作原理
本文部分参考: https://www.wosign.com/faq/faq2016-0309-03.htm https://www.wosign.com/faq/faq2016-0309-04.ht ...
- 什么是SSL双向认证,与单向认证证书有什么区别?
SSL/TLS 证书是用于用户浏览器和网站服务器之间的数据传输加密,实现互联网传输安全保护,大多数情况下指的是服务器证书.服务器证书是用于向浏览器客户端验证服务器,这种是属于单向认证的SSL证书.但是 ...
- SSL/TLS 双向认证
其他参考链接 链接: https://blog.csdn.net/xxss120/article/details/78758832. 链接: https://blog.csdn.net/gx_1983 ...
- 【ssl认证、证书】SSL双向认证java实战、keytool创建证书
文章目录 概述 keytool示例 参考 相关文章: //-----------Java SSL begin---------------------- [ssl认证.证书]SSL双向认证和SSL单向 ...
- 服务器双向认证 原理,什么叫SSL双向认证 SSL双向认证过程是怎样的
我们都知道SSL认证能够分成SSL双向认证和SSL单向认证.那么,什么是SSL双向认证?SSL双向认证过程又是怎样的?小编就在接下来的内容为各位详细讲述. 什么叫SSL双向认证 SSL双向认证则是需要 ...
最新文章
- 演示:标准ACL的配置、及使用技巧、和相关局限
- Nuke编辑工具包新版 Cara VR 插件发布
- 如何将文字转换成语音?文字转语音哪个工具好
- 你自己永远是个非常非常弱小的一个东西
- 搜索推荐系统根据用户搜索频率(热搜)排序
- idea链接oracle数据库报错:[66000][12505] Listener refused the connection with the following error:
- android 异步回调中操作UI线程,UI同步、卡死阻塞等性能问题
- Mac OS开发—Xcode给Mac应用添加编辑快捷键(剪切 复制 粘贴 全选 删除 撤销 重做)功能
- MakerDAO新增renBTC和UNI作为Dai抵押品
- Super超图,GIS软件
- c语言源程序最多可能由组成,一个c语言源程序是由什么组成_后端开发
- 速达服务器启动时显示对象名ACCSET无效
- jest中的mock,jest.fn()、jest.spyOn()、jest.mock()
- php中文分隔符,php断句无乱码,PHP根据分隔符断句,中英文无乱码
- 用户权限管理之权限管理
- JVM(三)--垃圾收集算法
- 【Python】Python xlwt : More than 4094 XFs (styles) 解决方法
- 爬虫之遇到woff字体反爬
- 三友硅业基于Lora+IBeacon定位技术下的化工厂人员定位解决方案-化工厂定位-新导智能
- Java stream流式计算详解
热门文章
- 【MySQL技术专题】「问题实战系列」MySQL报错Got an error reading communication packets问题分析指南
- PHP+MySQL+Dreamweaver动态网站开发_第一个PHP程序
- 美女问马云对自己长相怎么看, 马云回答很精彩!
- php 换行 html_HTML中的段落文本如何换行?一篇文章告诉你换行标签br的用法
- ARM与X86 CPU架构区别
- 华三M-lag典型配置
- Kotlin协程 - launch原理 笔记
- 第07节:端到端测试的优化策略
- InnoDB存储结构这一篇就够了
- 【word的pdfmaker.officeaddin加载项出现问题解决方法】