大家好。
我想知道是否可以使用 PyCrypto进行双重RSA/PKCS#1加密。
我有一个服务器,它有自己的 RSA 密钥(openssl
在安装所述服务器时使用命令生成)和一个可以请求服务器密钥的公共部分的客户端。此外,该客户端可以要求服务器为其生成另一个 RSA 密钥(或密钥对)。在这种情况下,服务器还保留私有(或“整个”RSA 密钥)并将其密钥的公共部分发送给客户端。
我一直在玩 RSA/PKCS 和 AES 加密。我创建了一个测试 Python 文件,它只使用一个 RSA 密钥就可以很好地加密。它所做的是使用对称 AES 系统(使用“即时”生成的随机密钥)加密数据,使用 RSA/PKCS#1 系统对用于 AES 的密码进行加密,并将其放入结果中发送:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Interesting links:
# 1> http://stackoverflow.com/a/9039039/289011
# 2> http://eli.thegreenplace.net/2010/06/25/aes-encryption-of-files-in-python-with-pycrypto/
from Crypto.PublicKey import RSA
import base64
import os
from Crypto.Cipher import AES
import Crypto.Util.number
import random
import struct
import cStringIO
from Crypto.Cipher import PKCS1_OAEP
def encrypt(string):
#Begin RSA Part to get a cypher that uses the server's public key
externKeyFilename="/home/borrajax/rsaKeys/server-key.pub"
externKeyFile = open(externKeyFilename, "r")
rsaKey= RSA.importKey(externKeyFile, passphrase="F00bAr")
pkcs1Encryptor=PKCS1_OAEP.new(rsaKey)
#End RSA Part
#Begin AES Part
iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
thisMessagePassword = os.urandom(16)
aesEncryptor = AES.new(thisMessagePassword, AES.MODE_CBC, iv)
chunksize=64*1024
#End AES Part
#Begin RSA Encription of the AES Key
rsaEncryptedPassword = pkcs1Encryptor.encrypt(thisMessagePassword)
retvalTmp = cStringIO.StringIO()
retvalTmp.write(struct.pack('<Q', len(string)))
retvalTmp.write(struct.pack('<Q', len(rsaEncryptedPassword)))
retvalTmp.write(rsaEncryptedPassword)
retvalTmp.write(iv)
while len(string) > 0:
chunk = string[0:chunksize]
string = string[chunksize:]
if len(chunk) % 16 != 0:
chunk += ' ' * (16 - len(chunk) % 16)
retvalTmp.write(aesEncryptor.encrypt(chunk))
return retvalTmp.getvalue()
def decrypt(string):
stringAsBuffer = cStringIO.StringIO(string)
retval = str()
chunksize=64*1024
externKeyFilename="/home/borrajax/rsaKeys/server-key.pem"
externKey = open(externKeyFilename, "r")
rsaKey = RSA.importKey(externKey, passphrase="F00bAr")
pkcs1Decryptor=PKCS1_OAEP.new(rsaKey)
origsize = struct.unpack('<Q', stringAsBuffer.read(struct.calcsize('Q')))[0]
rsaEncryptedPasswordLength = long(struct.unpack('<Q', stringAsBuffer.read(struct.calcsize('Q')))[0])
rsaEncryptedPassword = stringAsBuffer.read(rsaEncryptedPasswordLength)
thisMessagePassword = pkcs1Decryptor.decrypt(rsaEncryptedPassword)
iv = stringAsBuffer.read(16)
decryptor = AES.new(thisMessagePassword, AES.MODE_CBC, iv)
while True:
chunk = stringAsBuffer.read(chunksize)
if len(chunk) == 0:
break
retval += decryptor.decrypt(chunk)
return retval
if __name__ == "__main__":
encryptedThingy=encrypt(base64.b64encode("Toñóooooañjfl凯兰;kañañfjaafafs凱蘭pingüiñoo你好to金玉Toñóooooañjfl凯兰;kañañfjaafafs凱蘭pingüiñoo你好to金玉Toñóooooañjfl凯兰;kañañfjaafafs凱蘭pingüiñoo你好to金玉Toñóooooañjfl凯兰;kañañfjaafafs凱蘭pingüiñoo你好to金玉Toñóooooañjfl凯兰;kañañfjaafafs凱蘭pingüiñoo你好to金玉"))
print "Decrypted thingy: %s" % base64.b64decode(decrypt(encryptedThingy))
如您所见,AES 密码是使用服务器的 RSA 密钥加密的。现在,我想更加偏执,并使用客户端的公钥加密已经加密的密码,因此“加密”方法将类似于:
def encrypt(string):
#Begin RSA Part to get a cypher that uses the server's public key
externServerKeyFilename="/home/borrajax/rsaKeys/server-key.pub"
externServerKeyFile = open(externServerKeyFilename, "r")
rsaServerKey= RSA.importKey(externServerKeyFile, passphrase="F00bAr")
pkcs1ServerEncryptor=PKCS1_OAEP.new(rsaServerKey)
#End RSA Part
#Begin RSA Part to get a cypher that uses the client's public key
externClientKeyFilename="/home/borrajax/rsaKeys/client-key.pub"
externClientKeyFile = open(externClientKeyFilename, "r")
rsaClientKey= RSA.importKey(externClientKeyFile, passphrase="F00bAr")
pkcs1ClientEncryptor=PKCS1_OAEP.new(rsaClientKey)
#End RSA Part
#Begin AES Part
iv = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
thisMessagePassword = os.urandom(16)
aesEncryptor = AES.new(thisMessagePassword, AES.MODE_CBC, iv)
chunksize=64*1024
#End AES Part
#Begin RSA Encription of the AES Key
rsaEncryptedPasswordWithServer = pkcs1ServerEncryptor.encrypt(thisMessagePassword)
rsaEncryptedPasswordWithServerAndClient = pkcs1ClientEncryptor.encrypt(rsaEncryptedPasswordWithServer) #Katacrasssshh here!!
retvalTmp = cStringIO.StringIO()
retvalTmp.write(struct.pack('<Q', len(string)))
retvalTmp.write(struct.pack('<Q', len(rsaEncryptedPasswordWithServerAndClient)))
#...Probably some yadda yadda here with key lengths and stuff so it would help re-build the keys in the server's side...
retvalTmp.write(rsaEncryptedPasswordWithServerAndClient)
retvalTmp.write(iv)
while len(string) > 0:
chunk = string[0:chunksize]
string = string[chunksize:]
if len(chunk) % 16 != 0:
chunk += ' ' * (16 - len(chunk) % 16)
retvalTmp.write(aesEncryptor.encrypt(chunk))
return retvalTmp.getvalue()
但是当我尝试重新加密密钥时,我得到了一个ValueError("Plaintext too large")
例外。这是有道理的(至少对于对加密一无所知的人来说是有道理的),因为 PKCS 添加了一个填充,所以当我thisMessagePassword
用服务器的公钥加密“”时,我得到一个 256 字节的字符串,这对于第二个 PKCS 来说太长了加密器(我一直在做一些“手动测试”,限制似乎是 214 字节......我的意思是......这是最后一个不抛出异常的值)。
我知道这可能是一个奇怪的结构,使用服务器的公钥进行加密和使用客户端的密钥进行签名可能更有意义,但我只是想尝试一下加密的东西并尝试了解它们是如何进行的工作以及为什么。这就是为什么任何提示都会受到赞赏的原因。
先感谢您!