设计文档

姓名:贾昊龙

学号:18307130049

主体思路

TLS握手过程

  • 通过socket编程首先先建立好明文传输的链接
  • hello阶段(如果是SSL的话,这里是SSL中握手协议的作用,并且后面的一些过程也是该协议)
    • 在这里,客户端通过链接来向服务器发送客户端所支持的协议以及加密算法,并生成随机数random1在这里参考了TLS协议分析这篇blog以及SSL/TLS链接的建立的blog,在这里采用AES-RSA-MD5作为cipher suit
    • 服务器返回消息,在AES-RSA-MD5上与客户端达成一致,生成random 2
  • 证书发送及认证(certificate)阶段
    • 首先服务器将自己的证书下发给客户端,让客户端进行验证,之后客户端验证后从证书中取出公钥
    • 由于难以自行形成证书,所以可以通过把简写的CA.txt文件当作证书,通过私钥验签的方式验证身份
  • 密钥交换
    • 使用RSA密钥交换方式,客户端生成session key
  • 将明文和计算的hash值进行连接
  • AES将明文+mac一起进行加密传输

函数解释

  • server

    函数 解释
    send_CA() 利用私钥加密CA证书发送给客户端
    key_exchanging() 使用私钥解密客户端发来的用公钥加密的sessionkey
    deAes() AES解密过程
    verify_mac() MD5计算收到的会话信息产生的hash值
    getplaintext() 验证客户端封装的hash值和重新计算的hash值是否匹配
    msg() 监听并打印会话信息
  • client

    函数 解释
    verify_ca() 客户端用公钥进行验签过程
    key_exchanging() 使用公钥加密sessionkey并发送给服务器
    enAes() AES加密过程
    enmac() MD5计算发送的信息产生的hash值并组装
    add_to_16() 将信息扩展位数成16整数倍
    msg() 加密信息发送到服务器

具体实现

Socket Connection

建立socket连接的过程如下

  • 首先是server

    服务器创建socket对象,绑定对应端口以及IP,等待接收连接

    import socket
    
    #创建socket对象s,基于internet地址和tcp协议
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #绑定本地端口8001
    s.bind(("127.0.0.1", 8001))
    #在本地8001端口监听,最大连接数量6
    s.listen(6)
    # 接受来自客户端的连接
    
    connection, address = s.accept()
    
    print("服务器:Waiting for connection...")
  • 其次是client

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('127.0.0.1', 8001))

Hello阶段

  • server

    服务器等待客户的发送过来的消息,并且根据对方可用的加密算法来选择自己的加密算法,在这里直接假设双方只支持AES-RSA-MD5的cipher suit

    data = connection.recv(1024) #the message received
    print('客户端: '+data.decode('utf-8'))   #hello,this is a hello message from the client
    
    x = ' OK.Let''s use the AES-RSA-MD5. '
  • client

    客户端通过已经建立好的链接来想想服务器发送消息,消息的主要目的是发送支持的加密算法,由服务器来选择相应的加密算法,在这里采用AES-RSA-MD5

    x = 'hello,this is a hello message from the client.Can we use AES-RSA-MD5?'
    print('客户端:'+x)
    client.send(x.encode('utf-8'))

证书发送及认证(certificate&Verify)阶段

  • server

    当彼此选好了加密算法之后,服务器将证书发给客户端,并且用私钥数字签名的方式交付给客户端来进行CA的验证

    x1 = '\n服务器: These are my information, please check.'
    print('服务器:'+x+x1)
    connection.send((x+x1).encode('utf-8'))
    
    #私钥加密CA证书发送给客户端
    def send_CA():
        with open('private_key_ca.pem') as privatefile:
            p = privatefile.read()
            privkey = rsa.PrivateKey.load_pkcs1(p)
            x = '\nserver''s private key is as following'
            print(x+'\n'+p)
        with open('CA') as content:
            ca = content.read()
            print('服务端\n'+ca)
        a = rsa.sign(ca.encode('utf-8'), privkey, 'SHA-1')
        return a
    
    signature = send_CA()
    connection.sendto(signature, address)
  • client

    客户端拿到证书之后用公钥的解密并且进行验证

    def verify_ca(data):
        with open('public_key_ca.pem') as publickfile:
            p = publickfile.read()
            pubkey = rsa.PublicKey.load_pkcs1(p)
            print('---------CA公钥---------\n'+p)
        with open('CA') as content:
            ca = content.read()
            print(ca)
        try:
            rsa.verify(ca.encode('utf-8'), data, pubkey)
            print('验证成功')
        except:
            print('验证失败')
    
    data = client.recv(1024)
    verify_ca(data)

密钥交换

  • client

    采取24位长的 ‘0singdancerapbasketball0’作为session key(直接设置),之后就是用公钥进行加密,然后发送给服务器

    def key_exchanging():
        secretkey = '0singdancerapbasketball0'
        with open('public_key_ca.pem') as publicfile:
            p = publicfile.read()
            pubkey = rsa.PublicKey.load_pkcs1(p)
        crypto = rsa.encrypt(secretkey.encode('utf-8'), pubkey)
        return crypto
    
    print('开始密钥交换,用公钥来对session key进行加密然后发送')
    client.send(key_exchanging())
  • server

    服务器用私钥解密,获得session key

    def key_exchanging(data):
        with open('private_key_ca.pem') as privatefile:
            p = privatefile.read()
            privkey = rsa.PrivateKey.load_pkcs1(p)
        secretkey = rsa.decrypt(data, privkey)
        return secretkey
    
    print('开始密钥交换,等待对称密钥')
    data = connection.recv(1024)
    secretkey = key_exchanging(data).decode('utf-8')
    print('服务器:客户端发过来的对称密钥为:{'+secretkey+'}(忽略大括号)')

哈希值的计算

  • client
    • 利用MD5来计算session的哈希值,并且之后加载到上面,中间的split为18307130049JHL,而由于加密信息的无规律,基本不会出现和split相同的内容进而引起对内容的解释的错误
def enmac(plaintext):
    h1 = hashlib.md5()
    h1.update(plaintext.encode('utf-8'))
    x = plaintext+'18307130049JHL'+h1.hexdigest()
    print('明文+hash:'+x)
    return x
  • server

    而服务器端也需要计算哈希值,根据得到的session的内容来计算哈希值,与客户端的哈希值进行匹配

    def verify_mac(plaintext):
        h1 = hashlib.md5()
        h1.update(plaintext.encode('utf-8'))
        return h1.hexdigest()
    
    def getplaintext(textandmac):
        str = textandmac.split('18307130049JHL')
        if(str[1] != verify_mac(str[0])):
            return 'false'
        else:
            return str[0]

AES加密

  • client

    将明文+hash的内容利用session key进行加密并传输

    def encAes(x):
        key = '0singdancerapbasketball0' #24位密钥
        text = x
        aes = AES.new(str.encode(key), AES.MODE_ECB)  # 采用ECB加密模式
        encrypted_text = str(base64.encodebytes(aes.encrypt(add_to_16(text))), encoding='utf8').replace('\n', '')  # 加密
        print('加密结果为:', encrypted_text)
        return encrypted_text
    
    def msg(client):
        while True:
            re_data = input()
            ciphertext = encAes(enmac(re_data))
            client.send(ciphertext.encode('utf-8'))
    
    print('------------------------------------------------')
    print('验证交互信息')
    print('------------------------------------------------')
    print('用户请输入\n[只支持英文信息]')
    msg(client)
  • server

    在服务端,将利用session来进行解密

    def deAes(text, key):
        aes = AES.new(str.encode(key), AES.MODE_ECB)  # 采用ECB加密模式
        decrypted_text = str(aes.decrypt(base64.decodebytes(bytes(text, encoding='utf8'))).rstrip(b'\0').decode("utf8"))  # 解密
        return decrypted_text
    
    
    def msg(connection):
        while True:
            try:
                data = connection.recv(1024)
                ciphertext = data.decode('utf-8')
                textandmac = deAes(ciphertext, secretkey)
                plaintext = getplaintext(textandmac)
                if(plaintext == 'false'):
                    print('mac值false,退出')
                    return
                else:
                    print('mac值相同验证通过')
                    print('客户端:', plaintext)
            except:
                print('connection failed')
                return
    
    print('------------------------------------------------')
    print('验证交互信息')
    print('------------------------------------------------')
    print('等待用户输入')
    
    msg(connection)

运行截图

下面是一些运行截图

首先是运行server.py之后运行client.py(server图片–client图片–server图片–client图片)

TLS-1

TLS-2

TLS-3

TLS-4

附CA

-------------Verify--------------
Sequence num:01010101
Version:X.509
Public key Algorithm: RSA
date:2020.10.28-2020.11.28
Server:jhl
split:18307130049JHL
--------------DONE---------------
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMhK8UrmtY+iSUrQhhcxAA8eAgB99gpKjbx8uo15geRbcr3pqR192Wdm
UcyXoZ7MH+FEtQ4v14awNsoub0NcgUepHj2xzzat7pes4NZz/9YCLEgFA+5/VnKn
ez5FoHwfQT58XsXFXKOni35sMIVEgoCLdURELd09nBsGCqzR9tiVAgMBAAE=
-----END RSA PUBLIC KEY-----

回顾

  • 首先,先了解一下什么是TLS/SSL以及他们的作用

  • 相当于是在应用层和在运输层之间添加了一个SSL协议层,而SSL协议分为了两层,一层是SSL记录协议:是为上层的数据提供数据的封装加密等等;而SSL握手协议:是在数据传输开始之前,双方进行身份确认,协商机密算法,交换加密密钥的过程

  • 关于TLS/SSL建立连接以及握手的过程

    TLS握手过程

    • 阶段一

      • Client Hello,发送random 1,客户端支持的加密算法的组合列表等
      • Server Hello,从客户端发送的加密算法的组合列表中进行选择,生成random 2
    • 阶段二(服务器鉴别以及密钥交换)

      • 服务器将自己的证书CA发送给客户端,让客户端进行验证,客户端验证后从CA中取出对应的公钥
      • 可要求客户端上报CA,
      • 通知客户端Server Hello结束
    • 客户鉴别和密钥交换

      • 验证好服务端的CCA之后,取出公钥,将session key用公钥加密之后发送给服务器,服务器用私钥解密之后获得session key
    • 阶段四

      • 计算session对应的hash值,之后双方在进行通信的过程中,如果两个hash值匹配了,说明对应的session会话过程建立起来了,之后将hash值放在明文后面,并且用session key来进行加密

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

TCP_Attack 上一篇
process-lab 下一篇