こんにちは、みんな。
PyCryptoで二重のRSA/PKCS#1暗号化が可能かどうか疑問に思っていました。
独自の RSA キー (openssl
サーバーのインストール時にコマンドで生成される) を持つサーバーと、サーバーのキーの公開部分を要求できるクライアントがあります。また、そのクライアントはサーバーに別の RSA キー (またはキーペア) を生成するように依頼できます。その場合、サーバーは秘密 (または「全体」の RSA キー) も保持し、そのキーの公開部分をクライアントに送信します。
私は RSA/PKCS と AES 暗号化をいじっています。1 つの RSA キーのみで正常に暗号化できるテスト Python ファイルを作成しました。それが行うことは、対称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 バイトの文字列が得られますが、これは 2 番目の PKCS には長すぎます。暗号化 (私はいくつかの「手動テスト」を行ってきましたが、制限は 214 バイトのようです... つまり、例外をスローしない最後の値です)。
おそらく奇妙な構造であり、サーバーの公開鍵を暗号化に使用してクライアントの鍵で署名する方が理にかなっていることは承知していますが、暗号化を少し試して、それらがどのように機能するかを理解しようとしています。仕事とその理由。そのため、ヒントをいただければ幸いです。
前もって感謝します!