Python应用:1、SSL自签证书生成及双向认证验证(python)
SSL自签证书生成及双向认证验证(python)
目录
随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。
想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。
很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。
同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。
既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来
,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。
本系列博客所述资料均来自互联网
,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法
。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。
linux下安装openssl
查看
openssl version
该指令查看openssl的版本号,返回错误就说明没有openssl嘛,进行下面的安装步骤
获取
wget --use_no_certificate https://www.openssl.org/source/openssl-1.1.1b.tar.gz
解压
tar -xvf openssl-1.1.1f.tar.gz
编译
cd openssl-1.1.1f
make & make install
配置
vim /etc/ld.so.conf
#添加配置
/usr/local/bin
/usr/local/lib
/usr/local/ssl
创建自签证书
需要注意几点:
- openssl库后的字符代表的安全等级不一样,如openssl1.1.1f中的f
- 启动服务器的时候提示秘钥太短,通过修改生成key的长度 openssl genrsa -des3 -out ca.key 2048 中的2048
- 提示md太弱,修改下面脚本中的:default_md = sha256
开始为md5,后面我修改为sha256才可以 - 运行脚本之后会一步一步让你输入消息,注意到Common Name的时候CA的name和其他证书的name不能一样
- 服务器搭建的时候绑定的ip要为0.0.0.0,本地测试用127.0.0.1没关系
签名脚本
openssl ca命令有一些奇怪的要求,并且默认的openssl配置不允许直接轻松地使用openssl ca。因此,我们创建这个sign.sh程序来替换它。创建sign.sh程序文件,点击/usr/bin/sign.sh并添加到这个文件:
#!/bin/sh
##
## sign.sh -- Sign a SSL Certificate Request (CSR)
## Copyright (c) 1998-1999 Ralf S. Engelschall, All Rights Reserved.
##
# argument line handling
CSR=$1
if [ $# -ne 1 ]; then
echo "Usage: sign.sign <whatever>.csr"; exit 1
fi
if [ ! -f $CSR ]; then
echo "CSR not found: $CSR"; exit 1
fi
case $CSR in
*.csr ) CERT="`echo $CSR | sed -e 's/\.csr/.crt/'`" ;;
* ) CERT="$CSR.crt" ;;
esac
# make sure environment exists
if [ ! -d ca.db.certs ]; then
mkdir ca.db.certs
fi
if [ ! -f ca.db.serial ]; then
echo '01' >ca.db.serial
fi
if [ ! -f ca.db.index ]; then
cp /dev/null ca.db.index
fi
# create an own SSLeay config
cat >ca.config <<EOT
[ ca ]
default_ca = CA_own
[ CA_own ]
dir = /etc/ssl
certs = /etc/ssl/certs
new_certs_dir = /etc/ssl/ca.db.certs
database = /etc/ssl/ca.db.index
serial = /etc/ssl/ca.db.serial
RANDFILE = /etc/ssl/ca.db.rand
certificate = /etc/ssl/certs/ca.crt
private_key = /etc/ssl/private/ca.key
default_days = 365
default_crl_days = 30
default_md = md5
preserve = no
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
EOT
# sign the certificate
echo "CA signing: $CSR -> $CERT:"
openssl ca -config ca.config -out $CERT -infiles $CSR
echo "CA verifying: $CERT <-> CA cert"
openssl verify -CAfile /etc/ssl/certs/ca.crt $CERT
# cleanup after SSLeay
rm -f ca.config
rm -f ca.db.serial.old
rm -f ca.db.index.old
# die gracefully
exit 0
自动生成自签证书脚本
#!/bin/sh
#
# ssl 证书输出的根目录。
sslOutputRoot="./cert_ssl"
if [ $# -eq 1 ]; then
sslOutputRoot=$1
fi
if [ ! -d ${sslOutputRoot} ]; then
mkdir -p ${sslOutputRoot}
fi
cd ${sslOutputRoot}
echo "开始创建CA根证书..."
#
# 创建CA根证书,稍后用来签署用于服务器的证书。如果是通过商业性CA如
# Verisign 或 Thawte 签署证书,则不需要自己来创建根证书,而是应该
# 把后面生成的服务器 csr 文件内容贴入一个web表格,支付签署费用并
# 等待签署的证书。关于商业性CA的更多信息请参见:
# Verisign - http://digitalid.verisign.com/server/apacheNotice.htm
# Thawte Consulting - http://www.thawte.com/certs/server/request.html
# CertiSign Certificadora Digital Ltda. - http://www.certisign.com.br
# IKS GmbH - http://www.iks-jena.de/produkte/ca /
# Uptime Commerce Ltd. - http://www.uptimecommerce.com
# BelSign NV/SA - http://www.belsign.be
# 生成CA根证书私钥
openssl genrsa -des3 -out ca.key 2048
# 生成CA根证书
# 根据提示填写各个字段, 但注意 Common Name 最好是有效根域名(如 zeali.net ),
# 并且不能和后来服务器证书签署请求文件中填写的 Common Name 完全一样,否则会
# 导致证书生成的时候出现
# error 18 at 0 depth lookup:self signed certificate 错误
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
echo "CA根证书创建完毕。"
echo "开始生成服务器证书签署文件及私钥 ..."
#
# 生成服务器私钥
openssl genrsa -des3 -out server.key 2048
# 生成服务器证书签署请求文件, Common Name 最好填写使用该证书的完整域名
# (比如: security.zeali.net )
openssl req -new -key server.key -out server.csr
ls -altrh ${sslOutputRoot}/server.*
echo "服务器证书签署文件及私钥生成完毕。"
echo "开始使用CA根证书签署服务器证书签署文件 ..."
#
# 签署服务器证书,生成server.crt文件
# 参见 http://www.faqs.org/docs/securing/chap24sec195.html
# sign.sh START
#
# Sign a SSL Certificate Request (CSR)
# Copyright (c) 1998-1999 Ralf S. Engelschall, All Rights Reserved.
#
CSR=server.csr
case $CSR in
*.csr ) CERT="`echo $CSR | sed -e 's/\.csr/.crt/'`" ;;
* ) CERT="$CSR.crt" ;;
esac
# make sure environment exists
if [ ! -d ca.db.certs ]; then
mkdir ca.db.certs
fi
if [ ! -f ca.db.serial ]; then
echo '01' >ca.db.serial
fi
if [ ! -f ca.db.index ]; then
cp /dev/null ca.db.index
fi
# create an own SSLeay config
# 如果需要修改证书的有效期限,请修改下面的 default_days 参数.
# 当前设置为10年.
cat >ca.config <<EOT
[ ca ]
default_ca = CA_own
[ CA_own ]
dir = .
certs = ./certs
new_certs_dir = ./ca.db.certs
database = ./ca.db.index
serial = ./ca.db.serial
RANDFILE = ./ca.db.rand
certificate = ./ca.crt
private_key = ./ca.key
default_days = 3650
default_crl_days = 30
default_md = sha256
preserve = no
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
EOT
# sign the certificate
echo "CA signing: $CSR -> $CERT:"
openssl ca -config ca.config -out $CERT -infiles $CSR
echo "CA verifying: $CERT <-> CA cert"
openssl verify -CAfile /cert_ssl/ca.crt $CERT
# cleanup after SSLeay
rm -f ca.config
rm -f ca.db.serial.old
rm -f ca.db.index.old
# sign.sh END
echo "使用CA根证书签署服务器证书签署文件完毕。"
#创建客户端证书
cp -f server.crt client.crt
cp -f server.key client.key
# 使用了 ssl 之后,每次启动 apache 都要求输入 server.key 的口令,
# 你可以通过下面的方法去掉口令输入(如果不希望去掉请注释以下几行代码):
echo "去除客户端的key限制:"
cp -f client.key client.key.org
openssl rsa -in client.key.org -out client.key
echo "去除完毕。"
exit 0
知识点:实际上服务器证书和客户端都是属于同一级,都是通过CA根证书衍生的
服务器测试代码
import socket
import ssl
import threading
import time
from time import sleep
#端口范围:2900-3000
class ssl_client:
def __init__(self,ssl_client,ssl_client_address,test_file):
self.client = ssl_client
self.addr = ssl_client_address
self.test_file = test_file
def build(self):
# 循环接收和发送数据
ssl_client = self.client
while True:
recv_data = ssl_client.recv(1024)
# 有消息就回复数据,消息长度为0就是说明客户端下线了
if recv_data:
#print("客户端是:", tcp_client_address)
#print("客户端发来的消息是:", recv_data.decode())
if recv_data.decode() == "recv_big_file":
with open(self.test_file,mode="rb") as fd:
while True:
readbuf = fd.read(1024)
if len(readbuf.decode()) <= 0:
break
ssl_client.send(readbuf)
sleep(0.01)
else:
ssl_client.send(recv_data)
else:
print("%s 客户端下线了..." % ssl_client)
ssl_client.close()
break
class server_ssl:
def __init__(self,port = 9443,client_num = 100,test_file_name = 'socket_test_file.txt'):
self.port = port
self.client_num = client_num
self.test_file_name = test_file_name
def build_server(self):
# 生成SSL上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER,)
context.verify_mode = ssl.CERT_REQUIRED
# 加载服务器所用证书和私钥
context.load_cert_chain('cert/server.crt', 'cert/server.key','123456')
context.load_verify_locations('cert/ca.crt')
# 监听端口
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
sock.bind(('0.0.0.0', self.port))
sock.listen(self.client_num)
# 将socket打包成SSL socket
with context.wrap_socket(sock, server_side=True,) as ssock:
while True:
# 接收客户端连接
try:
client_socket, addr = ssock.accept()
except:
print("客户端连接失败")
continue
client = ssl_client(client_socket,addr,self.test_file_name)
# 创建多线程对象
thd = threading.Thread(target = client.build, args = ())
# 设置守护主线程 即如果主线程结束了 那子线程中也都销毁了 防止主线程无法退出
thd.setDaemon(True)
# 启动子线程对象
thd.start()
'''
# 接收客户端信息
msg = client_socket.recv(1024).decode("utf-8")
print(f"receive msg from client {addr}:{msg}")
# 向客户端发送信息
msg = f"yes , you have client_socketect with server.\r\n".encode("utf-8")
client_socket.send(msg)
client_socket.close()
'''
if __name__ == "__main__":
server = server_ssl(port = 2903)
server.build_server()
客户端测试代码
import socket
import ssl
from time import sleep
class client_ssl:
def send_hello(self,):
# 生成SSL上下文
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# 加载信任根证书
context.load_verify_locations('cert/ca.crt')
context.load_cert_chain('cert/client.crt','cert/client.key')
#这里将check_hostname,否则本地验证的时候域名会校验不过
context.check_hostname = False
# 与服务端建立socket连接
with socket.create_connection(('127.0.0.1', 2903)) as sock:
# 将socket打包成SSL socket
# 一定要注意的是这里的server_hostname不是指服务端IP,而是指服务端证书中设置的CN,我这里正好设置成127.0.1而已
with context.wrap_socket(sock, server_hostname='127.0.0.1') as ssock:
# 向服务端发送信息
send_msg = "test is ok".encode("utf-8")
# 接收服务端返回的信息
while True:
ssock.send(send_msg)
msg = ssock.recv(1024).decode("utf-8")
if len(msg) >= 0:
print(f"receive msg from server : {msg}")
sleep(2)
ssock.close()
if __name__ == "__main__":
client = client_ssl()
client.send_hello()
资料
-
python的ssl库
https://docs.python.org/3/library/ssl.html
-
自签证书生成
https://www.jb51.net/article/60371.htm
那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)