2012-04-18 78 views
0

大家好。PyCrypto:使用RSA和PKCS#1加密两次字符串#1

我想知道是否可以用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() 

但是,当我尝试重新加密键,我得到一个异常。这是有道理的(至少对于几乎不知道加密的人来说是有意义的),因为PKCS添加了一个填充,所以当我用服务器的公钥加密“thisMessagePassword”时,我得到一个256字节的字符串,这对于第二个字符来说太长了PKCS加密器(我一直在做一些“手动测试”,限制似乎是214个字节......我的意思是...这是最后一个不抛出异常的值)。

我知道这可能是一个奇怪的构造,它可能会更有意义使用服务器的公钥加密和用客户端密钥签名,但我只是想玩一点加密的东西,并尝试了解他们如何工作以及为什么。这就是为什么任何提示将不胜感激。

预先感谢您!

回答

3

PKCS1OAEP.encrypt的文档说以下有关它的输入:

消息(字符串) - 来加密该消息,也被称为明文。它可以是可变长度的,但不能超过RSA模数(以字节为单位)减2,减去散列输出大小的两倍。

SHA-1(默认散列函数)具有160位摘要,即20个字节。您看到右声道的限制:256 = 214 + 2 + 2 * 20。

除此之外,您计划添加的额外步骤不会增加太多价值。如果你想让客户向服务器证明它确实是他,而不是其他人,你应该为客户提供私钥,并让服务器让公众减少一半。在加密步骤之后,客户端可以使用PKCS#1 PSS将整个包(包装的AES密钥+加密数据)签名为并发送签名。服务器将使用客户端的公钥验证源,然后使用其私钥解密密钥,最后使用AES解密数据。

1

所以,你在做什么似乎没有多大意义。 您想安全地将消息从服​​务器发送到客户端?

您试图在服务器公共密钥下加密消息的代码,然后在客户端的公共密钥下。客户端将无法读取它,因为他永远不会拥有服务器的私钥(需要读取服务器公钥下加密的邮件)。换句话说,如果服务器和客户端都有相同的私钥,那么你应该只使用AES。你为什么做这个 ?

真的,您可能应该使用ssl/tls/https向客户端发送消息,因为编写加密代码是有问题的,并且您至少在代码中出现了两个不良错误以及需要修复的错误。

  1. 您的IV需要安全随机。 python随机调用不是,这就是为什么你使用os.random(16)作为键。你应该这样做的第四代

  2. 您需要使用hmac验证加密的数据和密钥的hmac与单独的随机密钥。然后在另一端使用相同的密钥,通过相同的输入重新生成hmac,并比较两者。如果你不这样做,有人可能会篡改你的数据并使用加密库中的错误来读取它。

  3. 您发布的问题:请注意,如上所述,您不应该这样做,因为这无关紧要。您需要在新密钥下使用AES加密rsaEncryptedPasswordWithServer(并使用上述2中的HMAC),然后使用客户端公钥对新密钥进行加密。

1

我不建议你这样做,或者暗示它有意义,但如果你只是想玩它,这里是你可以做的。

  1. 确保第一RSA密钥的应用模量小于的应用第二RSA密钥的模量。
  2. 使用较小的模数和适当的PKCS#1填充执行第一次RSA加密
  3. 使用较大模数和无填充执行第二次RSA加密。

在解密时,您必须颠倒这些操作的顺序。

相关问题