个人建议:
本实验是在Seedlab Crypto_PKI实验基础上完成的,有一些实验步骤直接使用了Crypto_PKI实验的相关文件,对于没看过PKI实验的人来说,读起来可能有些晦涩,所以建议先读Crypto_PKI实验:Crypto_PKI实验传送门
文章目录
Transport Layer Security (TLS) Lab
实验过程:
task1:TLS Client
Task 1.a: TLS handshake
使用如下代码,为了打印出TLS使用的各个算法,可以在实验手册代码基础上加上pprint.pprint(ssock.cipher())语句:
#!/usr/bin/python3
import socket, ssl, sys, pprint
hostname = sys.argv[1]
port = 443
cadir = '/etc/ssl/certs'
# Set up the TLS context
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # For Ubuntu 16.04 VM
context.load_verify_locations(capath=cadir)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
# Create TCP connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((hostname, port))
input("After making TCP connection. Press any key to continue ...")
# Add the TLS
ssock = context.wrap_socket(sock, server_hostname=hostname,
do_handshake_on_connect=False)
ssock.do_handshake() # Start the handshake
print("=== Cipher used: {}".format(ssock.cipher()))
print("=== Server hostname: {}".format(ssock.server_hostname))
print("=== Server certificate:")
pprint.pprint(ssock.getpeercert())
pprint.pprint(context.get_ca_certs())
input("After TLS handshake. Press any key to continue ...")
# Close the TLS Connection
ssock.shutdown(socket.SHUT_RDWR)
ssock.close()
TLS为套接字提供安全通信需要四个步骤。
- 第一步是创建TLS上下文对象。对象中保存了我们对证书的认证与加密算法选择的偏好设置。代码中context部分.
- 第二步是建立TCP握手协议,进行TCP连接,代码sock部分.
- 第三步是调用第一步创建的上下文对象,对其使用wrap_socket()方法,表示让OpenSLL库负责控制我们的TCP链接。然后与通信对方交换必要的握手信息,并建立加密链接。代码中ssock =context.wrap_socket…部分
- 最后一步是使用wrap_socket()调用返回的ssl_sock对象,进行所有的后续通信。后续进行通信需要使用类似 ssock.方法名 格式进行使用.
Question
-
打印出
即ECDHE-RSA-AES128-GCM-SHA256,每个参数的意思如下图:
即使用AES128-GCM算法进行加密通信 -
-
存储许多证书文件 :
这些文件为根CA文件,用与建立TLS连接时,验证服务器发回来的证书的有效性. -
使用wireshark抓与www.baidu.com进行TLS连接的相关信息,得到一系列数据包,一一分析:
-
TCP握手:
TCP三次握手协议,可以看出,首先进行的TCP握手,符合直觉,TLS是在传输层基础上的嘛,所以应该建立在TCP连接基础上进行,当然,也有建立在UDP基础上的,叫DTLS.
-
TLS握手
图片中TLS数据包均为TLS握手相关数据包.如下一一分析:
-
客户端首先发出Client Hello相关数据,包括客户端支持的所有密码套件,客户端随机数(用作Nonce)呃…信息量不少
-
服务器回复客户端Server Hello相关数据:
以及证书(可能证书数据量太大,就和上面的分开传输了):
-
随后,客户端验证证书合法性,如果合法,客户端向服务器发送密钥交换Client Key Exchange,以及Change Cipher Spec(更改密码规范消息),客户端握手结束:
-
服务器回复Change Cipher Spec(更改密码规范消息)以及一个New Session值,握手结束,有时候这两个会分开发送,即不一定会一起发送:
Task 1.b: CA’s Certificate
eee…这个实验耗费了我好长时间…走了好大的弯路…
先写一下正确的实验思路与过程吧:
根据实验要求,需要选择两个网址进行测试:
目标网址:www.baidu.com:
-
在上面任务中.我们打印出来服务器发回的证书的信息,找到证书信息中的issuer 信息:
-
到/etc/ssl/certs目录中寻找和服务器发回证书的organizationalUnitName字段或者commonName字段匹配的.pem文件,至于什么时候选择什么字段,我也不清楚,在测试www.baidu.com时,需要的文件为commonName字段对应于/etc/ssl/certs目录中的GlobalSign_Root_CA.pem文件,而在后面的www.jd.com的测试中,我们发现是organizationalUnitName字段信息匹配的文件有效…至于匹配机制具体是什么,不清楚…就这一点,导致我走了很多弯路,最后再说…
-
将/etc/ssl/certs目录中的GlobalSign_Root_CA.pem文件复制到实验目录中新建的./certs文件夹,并使用对应命令生成文件的哈希值,进行软链接操作,得到如下效果:.
-
随后,修改前面代码中cadir=’./certs’进行测试,发现结果相符合,于是task1a相同,结果可以打印出证书对应信息,连接成功.
目标网址:www.jd.com
我们本来打算测试www.alibaba.com,但是由于这个网站的证书对应的验证证书与www.baidu.com文件的.pem文件一样,所以换一个证书不一样的网站进行测试使用京东.
直接测试,由于./cert未配置对应证书,得到错误信息:
此时,修改代码中cadir=’./certs’为主机存储证书目录:cadir=‘etc/ssl/certs’,得到对应证书发布者信息:
在etc/ssl/certs目录找到对应.pem文件,移动到./cert文件,并进行哈希运算,软链接处理,随后再换回cadir=’./certs’,即可得到正确的返回结果了,连接成功.
以问题为导向的思路总结
问题一: 根据实验要求,我们需要在/etc/ssl/certs目录中找到对应的证书文件来验证服务器返回的证书,可是怎么找到对于的.pem文件呢?
针对这个问题,首先想到的是,到代码打印出来的服务器证书中寻找.pem字段信息。但是,证书字段中并没有出现.pem格式的字段,于是我就想:是不是ssl库中有什么函数或方法可以打印出来需要匹配的.pem文件名称,于是,一通搜索…看ssl的源码,虽然也没看多懂,还是没有发现什么可用的东西,,,耗费了很长时间后,算了,不找了,便尝试着去etc/ssl/certs目录寻找几个名字和证书中subject字段最匹配的证书文件,找到好几个,但是GlobalSign_Root_CA.pem文件看起来最匹配,试了试,哎。。。可以哎
- 或许本来就没有这个方法。。。证书文件名是直接和哈希值匹配的。。。我裂开…
问题二:
实验手册上都是操作.crt文件,而在etc/ssl/certs目录中的都是.pem文件,这…能成功嘛?两个文件有什么区别?
根据软链接,找到文件名相同,后缀不同的两个文件,这两个本来就相互链接的,在etc/ssl/certs目录下使用ls -l命令可以找到,移动到实验文件夹下.
使用diff命令比较,发现文件内容相同:
查阅资料:.pem为BASE64编码的一种特定格式的文件,文件首尾有固定字符串如–BEGIN–…而.crt文件是常见于UNIX系统,大多数为PEM格式的一种文件
所以两者基本相等,
使用两种文件名生成的哈希值也相同,而且都可以进行证书验证
…bingo!
task 1.c
对www.sdu.edu.cn网站进行测试,配置好/etc/hosts后进行如下分情况测试:
False:不进行主机名验证,错误的证书可以验证成功
True:进行主机名验证,错误的证书不可以验证成功
Question:
如果不进行主机名检验,那么攻击者就可以通过使用其他网站的合法证书来冒充自己伪造的网站的合法证书,发回给用户进行验证,由于忽略了主机名校验,用户也不会察觉,达到网站欺骗用户的目的.
task 1.d: Sending and getting Data
(1). 将手册代码添加到之前的代码后,运行得到如下结果:
将对www.baidu.com网站回复数据返回到baidu.html文件,使用浏览器打开.
(2). 修改HTTP请求行代码,使程序返回一个图像文件数据,…
不会修改http请求,
之前学爬虫用到过,
尝试进行了修改,
但是老是报错…
以后再补…
后再补…
再补…
补…
task2:TLS Server
task 2.a. Implement a simple TLS server
本次实验需要使用seedlab Crypto_PKI实验中生成的一些文件,在那个实验中,我们设置了CN为SEEDPKILab.com的服务器证书以及相关文件,在本次实验中,我们将使用这些文件作为服务器程序的证书并发送回客户端程序.
对实验手册中server.py代码进行修改成如下:
#!/usr/bin/python3
import socket, ssl, pprint
html = """
HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n
<!DOCTYPE html><html><body><h1>This is SEEDPKILab.com!</h1></body></html>
"""
SERVER_CERT = '../lab_pki/server.crt'#seedlab Crypto_PKI实验中得到的SEEDPKILab.com的证书文件
SERVER_PRIVATE = '../lab_pki/server.key'#seedlab Crypto_PKI实验中得到的SEEDPKILab.com的私钥文件
# context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # For Ubuntu 20.04 VM
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) # For Ubuntu 16.04 VM
context.load_cert_chain(SERVER_CERT, SERVER_PRIVATE)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.bind(('0.0.0.0', 4433))
sock.listen(5)
while True:
newsock, fromaddr = sock.accept()
try :
ssock = context.wrap_socket(newsock, server_side=True)
print("TLS connection established")
data = ssock.recv(1024) # Read data over TLS
pprint.pprint("Request: {}".format(data))
ssock.sendall(html.encode('utf-8')) # Send data over TLS
ssock.shutdown(socket.SHUT_RDWR) # Close the TLS connection
ssock.close()
except Exception:
print("TLS connection fails")
continue
有几点需要注意:
-
在server.py代码中,我们使用sock.bind((‘0.0.0.0’, 4433))进行绑定监听端口4433,所以,我们需要在task1中客户端代码基础上进行一些修改,即修改端口号(否则客户端程序与服务器程序运行不会成功):
-
我们需要在客户端运行代码时,主机名参数使用SEEDPKILab.com,所以需要在本地/etc/hosts文件进行静态IP配置,添加127.0.0.1 SEEDPKILab.com条目,使客户端访问时,会进行本地访问.
-
而在server.py程序中sock.bind((‘0.0.0.0’, 4433))代码句,进行绑定代码的IP地址为0.0.0.0,表示针对本机上任意IP地址,server.py都会接受,进而进行回复数据,这一点很重要: 与上面配置IP的作用正好对应,使得服务器与客户端程序可以进行连接.
-
需要开启两个Terminal,一个运行server.py程序,另一个运行客户端程序,客户端得到如下结果:
实验中用到的linux语句
在调试server.py程序过程中,我们可能会遇到如下情况:
这是端口占用问题,此时虽然关闭了server.py程序,但是0.0.0.0:4433仍处于监听状态,可以使用如下语句进行关闭,以便后续调试.
netstat -tunlp
这是一个组合命令,-t,-u,-n,-l,-p组合而成,可以打印出linux网络状态信息,tcp,udp端口,IP地址等信息,查看 Local Address中0.0.0.0:4433的状态,如果正在运行,得到对应的PID后,使用如下命令kill:
kill -9 PID(填入上面得到的PID)
-9表示强制杀死后台进程.
task 2.b. Testing the server program using browsers
由于在Crypto_PKI实验中,已经在浏览器中安装过自己创建的根CA证书,所以,可以直接进行测试,运行server.py程序后,在浏览器中得到如下结果:
task 2.c. Certificate with multiple names
按照要求进行server.openssl.cnf文件配置,随后,按照实验手册命令,使用根CA文件ca.crt签署拥有多个主机名的证书,输出server_alt.crt文件和server_alt.key文件,随后使用这两个文件添加到server.py程序中证书路径中,进行测试.
需要注意的一点:
对于server.openssl.cnf文件配置中的req_distinguished_name字段信息配置中,直观感觉,应该是可以自己随意指定,只要最后在CN字段加上自己需要的的hostname即可,但是结果总是充满惊喜,在我随意填入一个ST字段信息后,生成证书文件命令会出现如下报错信息:
根据提示,需要和CA文件中ST字段信息相同,设置成shandong,为什么?
…
google一下,看到一篇博客写的,对于一个面向社会的CA签发机构企业来说,其签发的证书前边的国家省份等信息可以随意设置,但用私有CA签发证书时需要和服务器的证书填写的一样,要不然签署不成功,也没说么原因,记住吧…
最后对server.openssl.cnf进行如下配置:
在一个Terminal运行server.py程序,另一个Terminal运行task1中客户端程序,使用alt_name字段的主机名www.csdn2020.com进行访问(由于是在本机测试,所以需要在etc/hosts文件配置静态IP,添加127.0.0.1 www.csdn2020.com条目:
后在进行测试 )得到如下结果:
也可以在中浏览器进行测试,由于我们已经在浏览器中导入了私有ca.crt证书,而我们生成的服务器证书就是这个ca.crt签发的,所以会通过验证:
当然,我么也可以在其他主机进行测试,只需要etc/hosts文件配置静态IP做一下IP修改即可.
task3. A Simple HTTPS Proxy
遇到了技术问题…
代码需要补充的部分报错
我对python网络ssl编程了解太少
最近太忙
还得期末复习
以后有时间再系统学习一下ssl编程
再补充这部分的实验report吧.
立个Flag:
春节前,搞定
实验总结
- 对于ssl编程的相关知识第一次接触,有些地方不太熟练,也没有深入学习,导致了一些低级错误的出现,浪费了很多时间
- 由于这个实验是今年才发布的,所以网络上关于本实验的参考资料少之又少,所以基本上等同于自己孤军奋战,没有参考,可能会有些地方做错,欢迎指正,评论区共同交流…