|  |  |  |  | --- | 
					
						
							|  |  |  |  | layout: post | 
					
						
							|  |  |  |  | title: 写一个加密传输的Demo | 
					
						
							|  |  |  |  | tags: [加密, Demo] | 
					
						
							|  |  |  |  | ---  | 
					
						
							|  |  |  |  |   非专业密码学,仅供娱乐!<!--more-->     | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  | # 起因
 | 
					
						
							|  |  |  |  |   最近我们学校搞了一个工程项目,要求是研究关于信息安全等级保护的一些东西,一开始我以为这就是搞个权限啥的,后来发现和云计算一样是个定义,话说我明明是网络工程专业的为啥还要了解信息安全……    | 
					
						
							|  |  |  |  |   当然这个什么等级保护的内容很多,我们不可能全都涉及,所以老师允许我们只选其中的一部分进行研究。我想了想,我之前还想搞什么[加密邮件](/2019/07/02/encmail.html)啥的,所以我选了通信安全方面的板块,这样也可以对那个项目有些参考。    | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  | # 实现思路
 | 
					
						
							|  |  |  |  |   我本来是想按着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
 | 
					
						
							|  |  |  |  | ```python | 
					
						
							|  |  |  |  | # -*- 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
 | 
					
						
							|  |  |  |  | ```python | 
					
						
							|  |  |  |  | # -*- 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转换之类乱七八糟的问题。    | 
					
						
							|  |  |  |  |   希望中文网络环境能多一些大家遇到的冷门知识的解决方法啊……     | 
					
						
							|  |  |  |  |   对了,这个代码实际上只有防止中间人窥探信息的能力,并没有防篡改的能力,毕竟没有两端握手,没法做验证,所以这就是一个业余的加密传输代码,仅供参考。 |