赞
踩
TLS/SSL的功能实现主要依赖于三类基本算法:散列(哈希)函数 Hash、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列(哈希)函数验证信息的完整性。

可以理解为,TLS/SSL干了两件事,其一是保证传输内容不被篡改,最常用保证内容不被篡改的方法就是使用散列算法,以MD5为例,通过对传输内容计算MD5值,通过对比MD5值来验证接收数据是否完整。只有MD5只能保障数据传输完整,但不能保障别人无法看到内容,所以需要对传输的数据进行加密,这就是第二件事。
使用一个密码对内容进行加密和解密。常见的有DES,AES,IDEA算法。
加密:密文=加密算法(明文,密码)
解密:明文=解密算法(密文,密码)
关键点在于加密和解密使用的同一密码,且加密/解密区别于对应算法的两种不同模式。需要注意的是,在安全领域中,该算法中的密码保存本身就是一个不安全的问题。
非对称加密算法指的是加、解密使用不同的密钥,一把为公开的公钥,另一把为私钥。公钥加密的内容只能由私钥进行解密,反之由私钥加密的内容只能由公钥进行解密。也就是说,这一对公钥、私钥都可以用来加密和解密,并且一方加密的内容只能由对方进行解密。
加密:公钥加密,私钥解密的过程,称为「加密」
签名:私钥加密,公钥解密的过程,称为「签名」
非对称加密例子:让A写下一个任意3位数,并将这个数和91相乘;然后将积的最后三位数告诉B,这样B就可以计算出A写下的是什么数字了。
加密传输:A写下的是123 (明文),并且A计算出123 * 91 (B的公钥加密)等于11193,将结果的末三位193(加密结果)传给B;
解密过程:B把接收的193再乘以11 (B的私钥),193 * 11(解密过程) = 2123 末三位123(明文)就是A需要传输的内容;
原理:91乘以11等于1001,而任何一个三位数乘以1001后,末三位显然都不变。
# Generate the certificates and keys for testing. PROJECT_NAME="www.test.com" # Generate the openssl configuration files. cat > ca_cert.conf << EOF [ req ] distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] O = $PROJECT_NAME Dodgy Certificate Authority EOF cat > server_cert.conf << EOF [ req ] distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] O = $PROJECT_NAME CN = 127.0.0.1 EOF cat > client_cert.conf << EOF [ req ] distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] O = $PROJECT_NAME Device Certificate CN = 127.0.0.1 EOF mkdir ca mkdir server mkdir client mkdir certDER # private key generation openssl genrsa -out ca.key 2048 openssl genrsa -out server.key 2048 openssl genrsa -out client.key 2048 # cert requests openssl req -out ca.req -key ca.key -new -config ./ca_cert.conf openssl req -out server.req -key server.key -new -config ./server_cert.conf openssl req -out client.req -key client.key -new -config ./client_cert.conf # generate the actual certs. openssl x509 -req -in ca.req -out ca.crt -sha1 -days 5000 -signkey ca.key openssl x509 -req -in server.req -out server.crt -sha1 -CAcreateserial -days 5000 -CA ca.crt -CAkey ca.key openssl x509 -req -in client.req -out client.crt -sha1 -CAcreateserial -days 5000 -CA ca.crt -CAkey ca.key openssl x509 -in ca.crt -outform DER -out ca.der openssl x509 -in server.crt -outform DER -out server.der openssl x509 -in client.crt -outform DER -out client.der mv ca.crt ca.key ca/ mv server.crt server.key server/ mv client.crt client.key client/ mv ca.der server.der client.der certDER/ rm *.conf rm *.req rm *.srl
# 此代码仅用于分析握手阶段,后续异常请忽略 import socket import ssl def run_server(): # context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context = ssl.SSLContext(ssl.PROTOCOL_TLS) context.set_ciphers('ALL:@SECLEVEL=0') # context.options = ssl.OP_NO_TLSv1_2 context.load_cert_chain(certfile="server/server.crt", keyfile="server/server.key") context.load_verify_locations(cafile="ca/ca.crt") context.verify_mode = ssl.CERT_REQUIRED with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock: sock.bind(("127.0.0.1", 1443)) sock.listen(5) with context.wrap_socket(sock, server_side=True) as ssock: conn, addr = ssock.accept() with conn: print("Connected by", addr) while True: data = conn.recv(1024) if not data: break conn.sendall(data) if __name__ == '__main__': run_server()
import socket import ssl def connect_to_server(): hostname = '127.0.0.1' # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # context = ssl.SSLContext(ssl.PROTOCOL_TLS) # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) context.set_ciphers('ALL:@SECLEVEL=0') context.check_hostname = False context.load_cert_chain(certfile="client/client.crt", keyfile="client/client.key") context.load_verify_locations("ca/ca.crt") context.verify_mode = ssl.CERT_REQUIRED with socket.create_connection((hostname, 1443)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as ssock: print(ssock.version()) if __name__ == '__main__': connect_to_server()
import ssl import urllib3 from requests.packages.urllib3.util import ssl_ def urllib_req(): SSL_OPTIONS = ssl.OP_NO_TLSv1_1 ctx = ssl_.create_urllib3_context(ssl.PROTOCOL_TLS) ctx.options |= SSL_OPTIONS ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) ctx.set_ciphers('ALL:@SECLEVEL=0') ctx.check_hostname = False ctx.load_cert_chain(certfile="client/client.crt", keyfile="client/client.key") ctx.load_verify_locations("ca/ca.crt") http = urllib3.PoolManager( num_pools=1, maxsize=1, block=1, ssl_context=ctx, ) resp = http.request('GET', 'https://127.0.0.1:1443') print(resp.status) if __name__ == '__main__': urllib_req()
服务端使用ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
在该模式下,启用的是TLSv1.2与TLSv1.3两个安全的版本,客户端使用TLSv1.2和TLSv1.3连接都能成功,但是使用TLSv1.1和TLSv1.0版本会连接失败。失败时候会抛出如下异常。
ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version
服务端使用ssl.SSLContext(ssl.PROTOCOL_TLSv1_1)方式,即指定使用TLSv1.1版本。客户端只能使用TLSv1.1版本来连接,其他连接都会失败。
通过上面实验,可以得知的结论是,如果服务端开始允许的TLS版本覆盖比较全,在后续迭代过程中,因为安全的考虑,禁用了某些版本TLS,将会直接导致采用被禁用版本TLS的客户端无法与服务端直接通信。且该过程发生在握手时候的认证过程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。