由于项目需求需要使用Websocket并实现SSL双向认真,java开发server端,C、go、python开发三种不同的client端。由于人力、时间有限,而且只大体上了解TLS/SSL协议,在具体实现过程中,发现网上参考资料是不少,但是写的都不是很详细,并存在误导的情况,怕了不少坑。
写这篇文章主要目的是想和有同样需要的小伙伴们分享一下经验:
- 时间充足的话,建议还是全面系统的了解一下SSL协议及相关编程语言的实现过程、方法;
- 如果着急赶项目,可以参考按照本文进行操作即可。
废话少说,直接上干货。
一、PKI、CA、TLS/SSL、OpenSSL等概念及原理
此处不再赘述,搜索引擎自行走一趟。
二、基于OpenSSL生成自签名证书
1、明确X509证书标准的两种编码格式PEM和DER
- PEM(Privacy Enhanced Mail):内容是Base64编码的ASCII码文件,通常用于证书颁发机构(Certificate Authorities,CA),扩展名可为.pem/.crt/.cer/.key。纯文本以"-----BEGIN XXX-----" 和 "-----END XXX-----"作开头和结尾。服务器认证证书,中级认证证书(可理解为公钥)和私钥都可以储存为PEM格式。Apache和类似的服务器使用PEM格式证书。
- DER(Distinguished Encoding Rules):使用二进制,扩展名为.der,但也经常使用.cer用作扩展名,所有类型的认证证书和私钥都可以存储为DER格式。Java是其典型使用平台。
2、证书相关文件常用扩展名
- CRT :多用于*NIX系统,多数使用PEM编码,DER编码也有使用;
- CER :多用于Windows系统,多数使用DER编码,PEM编码也有使用;
- KEY:通常用于存放公钥或者私钥,非X509证书,PEM/DER编码均有;
- CSR(Certificate Signing Request):证书签名请求,不是证书,用于证书颁发机构申请签名证书,是一个公钥和附加信息,在生成CSR时,还会生成一个私钥;
- PFX/P12(predecessor of PKCS#12):CRT和KEY存在一个PFX文件中,通常会有一个”提取密码“,用于提取证书内容,使用的DER编码;
- JKS (Java Key Storage):Java的专利,与OpenSSL关系不大,使用专用工具keytool生成具,可将PFX转为JKS。
使用openssl自签名产生SSL证书过程如下图:
OpenSSL执行过程:
//1、生成CA秘钥,过程中要输入密码,这里用123456
openssl genrsa -des3 -out ca.key 2048
//2、生成证书请求文件,过程中输入密码:123456
openssl req -new -out ca.csr -key ca.key -keyform PEM -subj "/C=CN/ST=省/L=城市/O=单位/OU=部门/CN=域名或ip"
//3、生成自签名 根证书,过程中输入密码:123456
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt -CAcreateserial
//4、生成server端秘钥,过程中输入密码:123456
openssl genrsa -des3 -out server.key 2048
//5、生成server证书请求文件,过程中输入密码:123456
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=省/L=城市/O=单位/OU=部门/CN=域名或ip"
//6、生成自签名SSL证书,过程中输入密码:123456
openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA ca.crt -CAkey ca.key -days 365 -CAcreateserial
//7、生成client证书,与生成server证书过程一样,重复4-6步
三、WebSocket基于TLS/SSL双向认证通信(Python示例)
网上有一些http和https的SSL双向认证示例,没有挨个尝试,可自行搜索引擎。这里写一个Websocket的示例,仅供分享。
- Python环境:3.6.6
- 依赖模块:websockets
这里引用了部分Websockets example中的示例代码,感谢!
server.py(server端代码)
#!/usr/bin/env python
client.py(client端代码)
#!/usr/bin/env python
import asyncio
import pathlib
import ssl
import websockets
//服务端声明SERVER_AUTH,并加载server端证书
ssl_context=ssl.create_default_context(ssl.Purpose.SERVER_AUTH,cafile="cert/server.crt")
ssl_context.check_hostname=False
//certfile和keyfile参数为,证书及文件的路径
ssl_context.load_cert_chain(certfile="cert/client.crt",keyfile="cert/client.key")
ssl_context.verify_mode=ssl.CERT_REQUIRED
async def hello():
uri = "wss://localhost:1234"
async with websockets.connect(
uri, ssl=ssl_context
) as websocket:
name = input("What's your name? ")
await websocket.send(name)
print(f"> {name}")
greeting = await websocket.recv()
print(f"< {greeting}")
asyncio.get_event_loop().run_until_complete(hello())
总结
本文简要介绍了TLS/SSL双向认证的过程,并给出了示例代码,仅供参考,欢迎交流。
以上内容虽然简单,但均为原创,转载、引用请务必著名出处、作者,谢谢!!!