写一个加密传输的Demo | Mayx的博客
Author: mayx
Description: 非专业密码学，仅供娱乐！
Date: 2020-05-29T00:00:00+08:00

29 May 2020 - 字数统计：4777 - 阅读大约需要16分钟 - Hits: Loading...

# 写一个加密传输的Demo

by mayx

这是一篇创建于 天前的文章，其中的信息可能已经有所发展或是发生改变。

AI摘要

这篇文章是一个关于非专业密码学的Python实现示例，作者是为了一个学校工程项目研究通信安全而写的简化的加密传输Demo。主要使用AES用于数据加密，RSA用于加密AES的密钥，以及MD5进行数据校验。作者选择这些技术主要是为了保证通信过程中的安全性，虽然简化了RSA和TLS的握手过程以降低复杂性。传输方式则是通过文件而非TCP或Unix Domain Socket。虽然许多代码是从网上获取的，但文章展示了如何使用Python的Crypto库进行加密和解密操作。

- 起因
- 实现思路
- 代码
  - server.py
  - client.py
- 感想

非专业密码学，仅供娱乐！

#
起因

最近我们学校搞了一个工程项目，要求是研究关于信息安全等级保护的一些东西，一开始我以为这就是搞个权限啥的，后来发现和云计算一样是个定义，话说我明明是网络工程专业的为啥还要了解信息安全……

当然这个什么等级保护的内容很多，我们不可能全都涉及，所以老师允许我们只选其中的一部分进行研究。我想了想，我之前还想搞什么加密邮件啥的，所以我选了通信安全方面的板块，这样也可以对那个项目有些参考。

#
实现思路

我本来是想按着TLS的标准来写，后来看了看发现那样不太方便，我写Demo也就是玩玩而已，不用费那么大劲。

像正常来说，建立加密通道是要有握手环节的，但是那个实现起来实在是太麻烦，所以想了想就算了。

所以我的实现方式是生成一个随机数，用AES以随机数为密码加密数据，用RSA加密随机数。这样做的主要目的是利用RSA的特性保证传输内容不会被泄露，但是RSA相对来说太慢，所以用了AES来加密数据，这样就能提高传输的效率。

传送方式我之前还想着要不要建一个TCP Socket或者是Unix Domain Socket来传输，但是后来觉得这样太麻烦，不如直接用文件的方式传，这样还简单好理解。 （就是嫌麻烦嘛）

另外在通信安全中还有一点是要求校验数据，我想了想就用MD5吧，正常来说得到MD5后还要数字签名啥的，我觉得麻烦也就没搞，所以最终传输的内容就是AES加密后的数据、原数据的MD5(Hash)以及用RSA加密的密钥。

虽然我学Python的时间不长，不过我现在发现Python在做这些事情的时候远比Shell、PHP和JS简单，所以这次的Demo也是用Python实现的。

不过我的Python并不怎么样，所以大多数代码都是从网上Copy的，像Crypto的库我不搜一下肯定是不会用嘛。

既然用到了Crypto库，那么如果有人有兴趣执行下面的代码，自然需要执行一下`pip3 install pycryptodome`才可以正常运行啦。

#
代码

##
server.py

    # -*- coding: utf-8 -*-

    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5
    from Crypto.Cipher import AES
    import base64
    import hashlib
    import json

    private_key = """-----BEGIN RSA PRIVATE KEY-----
    MIICXQIBAAKBgQDfEQ82qUrto7h4BL3TsA/DFXSdM44cbeY4kPccD7gLGhaZRClz
    YKIh5zYdfjBGF+0HXfMa1u9b7GNs2AjVIsx8Kx0QLnMfmtkmGWGhOXz/9IDLKJOx
    0weKv61gysKItgzVKn2mbLool4R/PQBc3AjDyHw+io1KpVz+3kRTaGs1fQIDAQAB
    AoGAWB4kFWLA/6k6OOcemd4mC9mQ7HyuOdrMJDJX+5TWDkSrArajbTmSMrRkczgj
    F71h3BQn8cVQXs695ARfUNrjTbi2Y0LjN7ScK7ExzTLdoMEFw5JsHggJZ0zBQY6w
    mwOdGfqzA6tZPXgkn+jqEha+CD6GrwnTM1oDGJC/aKG2OmECQQDkO9IhUhFc/PSU
    0zvGE6AOcqk5wlOuvMg+oAFHJHJZ9XW7+X/Nx0ZoVDFq/cZQj+46t+fiwUwhdW7l
    IfCvNGKFAkEA+jRQmWGKrbf1ns4S0SezJvysd5O6otRGJXr+Ex2uDhc39ZTeUsyg
    kjrLhp8STLMOmql+8g5fghct17EuCX1EmQJBAJz9BNnEkIrst/OSpH/nyeWGOx6u
    q077LaXd+2MLD9kO/O/Se3V5B9YFa4STkJCjoBMloswXd51gIGpdgSeSmd0CQQCL
    PrwwcGmWfo+ynqs4PajlpK9zKQMwhYS4bTejedwZOXDKOtx0Ji+i0hfcxwCPMQOK
    rZPZsIgUxUOdC508aLvZAkBDkHxunCzDm0w4DdTUN7S9YSpVvQEjK/xUQiWaKV12
    8QgskhU2DNdYK2NxifnWrKtx3uQmqMxX5aLuJZ4493yr
    -----END RSA PRIVATE KEY-----"""

    # 公钥解密
    def rsa_decode(cipher_text, private_key):
        rsakey = RSA.importKey(private_key)  # 导入读取到的私钥
        cipher = PKCS1_v1_5.new(rsakey)  # 生成对象
        # 将密文解密成明文，返回的是一个bytes类型数据，需要自己转换成str
        text = cipher.decrypt(base64.b64decode(cipher_text), "ERROR")
        return text.decode()

    class PrpCrypt(object):

        def __init__(self, key):
            self.key = key.encode('utf-8')
            self.mode = AES.MODE_CBC

        # 解密后，去掉补足的空格用strip() 去掉
        def decrypt(self, text):
            cryptor = AES.new(self.key, self.mode, b'0000000000000000')
            plain_text = cryptor.decrypt(base64.b64decode(text))
            # return plain_text.rstrip('\0')
            return bytes.decode(plain_text).rstrip('\0')

    while not input("按回车读取客户端的信息，输入其他内容结束"):
        cipher = open("pipe.txt", mode='r')
        msg = json.loads(cipher.read())
        cipher.close()
        key = rsa_decode(msg["key"], private_key)
        aesc = PrpCrypt(key)
        message = aesc.decrypt(msg["message"])
        hash = hashlib.md5(message.encode(encoding='UTF-8')).hexdigest()
        if hash == msg["hash"]:
            print("数据校验成功")
        else:
            print("数据校验失败")
        print(message)

##
client.py

    # -*- coding: utf-8 -*-

    import random
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5
    from Crypto.Cipher import AES
    import base64
    import hashlib
    import json

    public_key = """-----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfEQ82qUrto7h4BL3TsA/DFXSd
    M44cbeY4kPccD7gLGhaZRClzYKIh5zYdfjBGF+0HXfMa1u9b7GNs2AjVIsx8Kx0Q
    LnMfmtkmGWGhOXz/9IDLKJOx0weKv61gysKItgzVKn2mbLool4R/PQBc3AjDyHw+
    io1KpVz+3kRTaGs1fQIDAQAB
    -----END PUBLIC KEY-----
    """

    # 公钥加密
    def rsa_encode(message, public_key):
        rsakey = RSA.importKey(public_key)  # 导入读取到的公钥
        cipher = PKCS1_v1_5.new(rsakey)  # 生成对象
        # 通过生成的对象加密message明文，注意，在python3中加密的数据必须是bytes类型的数据，不能是str类型的数据
        cipher_text = base64.b64encode(cipher.encrypt(message.encode(encoding="utf-8")))
        # 公钥每次加密的结果不一样跟对数据的padding（填充）有关
        return cipher_text.decode()

    class PrpCrypt(object):

        def __init__(self, key):
            self.key = key.encode('utf-8')
            self.mode = AES.MODE_CBC

        # 加密函数，如果text不足16位就用空格补足为16位，
        # 如果大于16当时不是16的倍数，那就补足为16的倍数。
        def encrypt(self, text):
            text = text.encode('utf-8')
            cryptor = AES.new(self.key, self.mode, b'0000000000000000')
            # 这里密钥key 长度必须为16（AES-128）,
            # 24（AES-192）,或者32 （AES-256）Bytes 长度
            # 目前AES-128 足够目前使用
            length = 16
            count = len(text)
            if count < length:
                add = (length - count)
                # \0 backspace
                # text = text + ('\0' * add)
                text = text + ('\0' * add).encode('utf-8')
            elif count > length:
                add = (length - (count % length))
                # text = text + ('\0' * add)
                text = text + ('\0' * add).encode('utf-8')
            self.ciphertext = cryptor.encrypt(text)
            # 因为AES加密时候得到的字符串不一定是ascii字符集的，输出到终端或者保存时候可能存在问题
            # 所以这里统一把加密后的字符串转化为16进制字符串
            return base64.b64encode(self.ciphertext)

    message=" "
    while message:
        message = input("请输入需要传输的信息（不输入则结束）：")
        hash = hashlib.md5(message.encode(encoding='UTF-8')).hexdigest()
        key = str(random.randint(1000000000000000,9999999999999999))
        cipher = rsa_encode(key, public_key)
        aesc = PrpCrypt(key)
        data=json.dumps({"message":aesc.encrypt(message).decode('utf8'),"hash":hash,"key":cipher})
        print(data,file = open('pipe.txt','w'))
        print("数据已发出！")

#
感想

我在写这个代码的时候在网上搜到的资料也真的是算少了，就像我当时打算写加密邮件那时一样，基本上大多数的资料都有问题，像有些代码还是Python2的，还有时候经常遇到什么bytearray、json转换之类乱七八糟的问题。

希望中文网络环境能多一些大家遇到的冷门知识的解决方法啊……

对了，这个代码实际上只有防止中间人窥探信息的能力，并没有防篡改的能力，毕竟没有两端握手，没法做验证，所以这就是一个业余的加密传输代码，仅供参考。

tags: 加密 - Demo 查看原始文件

#### 推荐文章

Loading...

上一篇：Re:加装内存
下一篇：关于Demoscene的探索
想问这篇文章
召唤伊斯特瓦尔