开源项目SMSS发开指南(五)——SSL/TLS加密通信详解(下)
继上一篇介绍如何在多种语言之间使用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());
}
server.jkd对应之前生成的证书文件,路径根据自己项目的实际路径指定。keystorepass和keypass是生成证书时输入的秘钥。
SSL握手和SSLEngine初始化
正如前文介绍的一样,SSL握手协议中规定了交换秘钥和协商对称加密的过程。因此,实际上在JDK的抽象中,SSL的握手过程本质上是对SSLEngine的初始化。因此与客户端不同的地方在于,服务端需要在有客户端连接进入后再进行SSLEngine的初始化,并保证每一个新连接对应一个SSLEngine对象。当客户端会话关闭后,释放对应的SSLEngine。
/**
* 服务端握手操作
*/
private SSLEngine sslHandshake(SocketChannel socket) throws IOException {
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
SSLSession sslSession = sslEngine.getSession();
ByteBuffer remoteAppData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
ByteBuffer localNetData = ByteBuffer.allocate(sslSession.getPacketBufferSize());
ByteBuffer remoteNetData = ByteBuffer.allocate(sslSession.getPacketBufferSize());
sslEngine.beginHandshake();
SSLEngineResult.HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
SSLEngineResult result;
// 循环判断指导握手完成
while (hsStatus != SSLEngineResult.HandshakeStatus.FINISHED) {
switch (hsStatus) {
case NEED_WRAP:
localNetData.clear();
result = sslEngine.wrap(ByteBuffer.allocate(0), localNetData); // 第一个参数设置空包,SSLEngine会将握手数据写入网络包
hsStatus = result.getHandshakeStatus();
if (handleResult(result)) {
localNetData.flip();
// 确保数据全部发送完成
while (localNetData.hasRemaining()) {
socket.write(localNetData);
}
}
break;
case NEED_UNWRAP:
int len = socket.read(remoteNetData); // 读取网络数据
if (len == -1) {
break;
}
remoteNetData.flip();
remoteAppData.clear();
do {
result = sslEngine.unwrap(remoteNetData, remoteAppData); // 与握手相关的数据SSLEngine会自行处理,不会输出至第二个参数
hsStatus = result.getHandshakeStatus();
} while (handleResult(result) && hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP);
// 一次性没有完成处理的数据通过压缩的方式处理,等待下一次数据写入
remoteNetData.compact();
break;
case NEED_TASK:
// SSLEngine后台任务
Runnable runnable;
while ((runnable = sslEngine.getDelegatedTask()) != null) {
runnable.run();
}
hsStatus = sslEngine.getHandshakeStatus();
break;
default:
break;
}
}
return sslEngine;
}
其它与客户端共性的部分,不再赘述。
三、HTTPS相关配置
配置tomcat
对于Java开发者而言,对Tomcat应该不陌生。下面的配置基于tomcat7。
conf/server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="server.jks"
keystorePass="password"
/>
配置springboot
利用springboot开发微服务应用的时候,可以直接部署jar包。下面的配置基于springboot 2.x以上版本。
application.yml
server:
port: 8081
ssl:
key-store: classpath:server.jks
enabled: true
key-store-password: password
key-store-type: JKS
证书路径为resources下或在启动配置中自由指定。如果配置成功在启动日志上会打印出8081(https)的相关消息,如果你需要让容器同时支持http和https也可以利用@Configuration通过代码加载配置,网上的资料很全,不再赘述。
不过这样配置其实并不能完成前后端分离的访问请求,因为浏览器转发的时候会默认对证书进行验证。由于我们的证书不是通过公认的CA机构签发,因此会被默认阻止。当然你也可以通过设置让浏览器放行,不过对于实际项目而言意义不大。
Android 6.0以上版本由于默认使用TLS通信,因此上面的配置可以应对移动端的访问限制。下面的配置是针对后端仅支持http,android端的配置:
<application /*其它配置*/ android:usesCleartextTraffic="true">
总结
以我个人对未来的展望,由于微服务和高可用的部署会被应用到越来越多的场景中,各种语言间的相互调用会成为常态。底层硬件和一些对性能要求较高的场景自然需要C/C++作为支持,但是涉及业务层和互联网方向的应用更多还是会以Java和其他高级语言为主。相互驱动,互为支持必不可少,而安全通信也会更加被重视。当然在实际的项目中,为了获得更加稳定的支撑,我们可能会选择使用框架。不过,能够深入学习对开发者而言非常重要。我利用两篇博客总结了在多语言支持下与安全通信相关的主要知识体系,基本以应用为主,少量结合了一些理论知识。希望能够为大家的学习有所帮助。
相关博客:
开源项目SMSS发开指南(四)——SSL/TLS加密通信详解(上)
开源项目SMSS发开指南(五)——SSL/TLS加密通信详解(下)的更多相关文章
- 开源项目SMSS发开指南(四)——SSL/TLS加密通信详解
本文将详细介绍如何在Java端.C++端和NodeJs端实现基于SSL/TLS的加密通信,重点分析Java端利用SocketChannel和SSLEngine从握手到数据发送/接收的完整过程.本文也涵 ...
- SSL/TLS 握手过程详解
在现代社会,互联网已经渗透到人们日常生活的方方面面,娱乐.经济.社会关系等都离不开互联网的帮助.在这个背景下,互联网安全就显得十分重要,没有提供足够的安全保障,人们是不会如此依赖它的.幸运的是,在大牛 ...
- SSL/TLS 握手优化详解
随着 HTTP/2 的逐渐普及,以及国内网络环境越来越糟糕(运营商劫持和篡改),HTTPS 已经开始成为主流.HTTPS 在 TCP 和 HTTP 之间增加了 TLS(Transport Layer ...
- “全栈2019”Java异常第十五章:异常链详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...
- 开源项目SMSS开发指南(二)——基于libevent的线程池
libevent是一套轻量级的网络库,基于事件驱动开发.能够实现多线程的多路复用和注册事件响应.本文将介绍libevent的基本功能以及如何利用libevent开发一个线程池. 一. 使用指南 监听服 ...
- 开源项目SMSS开源项目(三)——protobuf协议设计
本文的第一部分将介绍protobuf使用基础以及如何利用protobuf设计通信协议.第二部分会给出smss项目的协议设计规范和源码讲解. 一.Protobuf使用基础 什么是protobuf pro ...
- SSL及其加密通信过程
SSL及其加密通信过程 什么是SSL SSL英文全称Secure Socket Layer,安全套接层,是一种为网络通信提供安全以及数据完整性的安全协议,它在传输层对网络进行加密.它主要是分为两层: ...
- SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)
SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码) 摘自: https://blog.csdn.net/sjin_1314/article/det ...
- SSL/TLS 加密新纪元 - Let's Encrypt
转自: https://linux.cn/article-6565-1.html SSL/TLS 加密新纪元 - Let's Encrypt 根据 Let's Encrypt 官方博客消息,Let's ...
随机推荐
- Win10该文件没有与之关联的应用来执行该操作...请在"默认应用设置"页面中创建关联
问题发现:一直使用的一款软件--火柴,这两天忽然发现通过ctrl + 回车快捷键无法进入到文件所在的目录中(之前几天印象中还可以使用该功能).后来测试又发现无法打开网易云音乐中下载的音乐而进入到该音乐 ...
- xshell连接kali linux虚拟机
这次测试一波三折 刚开始在百度经验看的先修改ssh参数,蓝色的字是百度的,重点都在图片上 1.修改sshd_config文件,命令为: vi /etc/ssh/sshd_config 将#Passwo ...
- 1209. Construct the Rectangle
1209. Construct the Rectangle class Solution { public: /** * @param area: web page’s area * @retur ...
- Linux学习_菜鸟教程_4
Linux远程登录 已经了解了登录流程,学会了用SecureCRT进行操作 Linux文件基本属性 Linux为保护系统安全,对不同的用户,开放不同的文件访问权限. 在linux中,我们可以使用ll或 ...
- 【转】ArcGIS Server 10.1 动态图层—更改风格
在 ArcGIS Server REST API中我们可以通过向Graphicslayer中添加临时图元的方法来完成对显示结果的渲染:如果仅仅是更改原有地图服务显示风格,在ArcGIS10.1下使用动 ...
- react-mockjs
2020-01-17 react-mockjs 使用 最近参加了公司的一个新的项目,前后端同时开发,这时后端提供不了前端接口,那么就要靠咱们前端自己mock数据啦. 用到mock 数据的工具是 moc ...
- 【一头扎进Spring】 01 | 从 HelloWorld 开始看Spring
Spring 是一个开源框架. Spring 为简化企业级应用开发而生. 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能. Spring 是一个 IOC(DI ...
- P2756 飞行员配对方案问题 二分图匹配 匈牙利算法
题目背景 第二次世界大战时期.. 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外 ...
- Java 用链表实现栈和队列
栈 是一种基于后进先出(LIFO)策略的集合类型.当邮件在桌上放成一叠时,就能用栈来表示.新邮件会放在最上面,当你要看邮件时,会一封一封从上到下阅读.栈的顶部称为栈顶,所有操作都在栈顶完成. 前面提到 ...
- Git高级之配置多个SSH key
最近我们在代码托管平台上使用SSH的方式下拉代码,通常是用一个ssh key来拉取所有托管平台的代码,如码云,GitHub.GitLab等,但是总用一个不是太好.会有安全风险,这就需要为每个托管平台设 ...