PyCrypto: Verschlüsselt eine Zeichenkette zweimal mit RSA und PKCS#1
Hallo an alle.
Ich Frage mich, ob es möglich ist, führen Sie einen Doppelklick RSA/PKCS#1 Verschlüsselung mit PyCrypto.
Ich habe einen server, der hat seinen eigenen RSA-Schlüssel (erzeugt mit der openssl
Befehl, wenn dieser server installiert ist) und einen client kann verlangen, dass der öffentliche Teil des server ' s key. Auch, kann der client kann den server auffordern, ein anderes erzeugen der RSA-Schlüssel (oder das Schlüsselpaar) für Sie. In diesem Fall, der server hält auch das private (oder das "ganze" RSA-Schlüssel) und sendet dem client den öffentlichen Teil seines Schlüssels.
Ich habe das Spiel mit RSA/PKCS-und AES-Verschlüsselung. Habe ich einen test Python-Datei, funktioniert die Verschlüsselung nur mit einem RSA-Schlüssel. Was es tut, ist eine Verschlüsselung der Daten mit dem symmetrischen AES-system (verwendet wird ein zufälliger Schlüssel generiert "on-the-fly"), cyphers das Passwort für die AES mit dem RSA/PKCS#1 system und stellt ihn in der Folge geschickt zu werden:
#!/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))
Wie Sie sehen können, wird der AES-Passwort ist verschlüsselt mit dem server RSA-Schlüssel. Nun, ich würde gerne extra paranoid, und verschlüsseln Sie, dass bereits verschlüsselte Passwort mit dem öffentlichen Schlüssel des Kunden, so dass die "encrypt" - Methode wäre so etwas wie:
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()
Aber wenn ich versuche, erneut zu verschlüsseln, der Schlüssel, bekomme ich eine ValueError("Plaintext too large")
Ausnahme. Was Sinn macht (zumindest macht Sinn, jemand, der kaum weiß etwas über Verschlüsselung), weil PKCS fügt eine Polsterung, so dass, wenn ich verschlüsseln "thisMessagePassword
" mit dem öffentlichen Schlüssel des Servers, bekomme ich einen 256 Byte-Zeichenfolge, die zu lang für die zweite PKCS encryptor (ich mache ein paar "manuelle Prüfung" und die Grenze zu sein scheint, 214 bytes... ich meine... das ist der Letzte Wert, der nicht eine Ausnahme werfen).
Ich mir bewusst bin, dass ist wahrscheinlich eine seltsame Konstrukt und es würde wahrscheinlich mehr Sinn machen, verwenden Sie die server den öffentlichen Schlüssel für Verschlüsselung und Signierung mit dem client-key, aber ich versuche nur zu spielen ein bisschen mit Verschlüsselung die Dinge und versuchen zu verstehen, wie Sie funktionieren und warum. Das ist, warum jeder Hinweis wird dankbar sein.
Vielen Dank im Voraus!
Du musst angemeldet sein, um einen Kommentar abzugeben.
In der Dokumentation von
PKCS1OAEP.encrypt
sagt Folgendes über seine input:SHA-1 (Standard-hash-Funktion) hat einen 160-bit-digest, das ist 20 bytes. Die Einschränkung, dass Sie sehen, klingt in etwa richtig: 256 = 214 + 2 + 2*20.
Neben der extra-Schritt, den Sie hinzufügen möchten, nicht viel hinzuzufügen Wert. Wenn Sie möchten, dass der client um zu beweisen, dass die server, es ist wirklich ihm, und nicht von jemand anderem, Sie sollten angeben, dass der client mit dem privaten Schlüssel und dem server zu halten, die öffentliche Hälfte. Nach der Verschlüsselung Schritt, der client könnte Zeichen das ganze Paket (eingewickelt AES-Schlüssel + verschlüsselte Daten) mit PKCS#1 PSS und senden der Signatur zusammen. Der server prüft die Herkunft mit der client den öffentlichen Schlüssel, entschlüsselt den Schlüssel mit seinem eigenen privaten Schlüssel, und schließlich die Daten entschlüsseln mit AES.
Ich bin nicht empfehlen Sie dies tun, oder was darauf hindeutet, es macht keinen Sinn, aber wenn Sie nur wollen, um mit ihm zu spielen hier ist, was Sie tun können.
Auf entschlüsseln, müssen Sie in umgekehrter Reihenfolge diese Operationen natürlich.
So, was Sie tun, scheint wenig Sinn machen.
Sie wollen sicher das senden einer Nachricht von einem server zu einem client?
Den code, den Sie haben versucht, eine Nachricht verschlüsseln, die unter dem Server den öffentlichen Schlüssel, dann unter dem öffentlichen Schlüssel des Kunden. Der client wird nicht in der Lage sein, dies zu Lesen, denn er sollte nie den server den privaten Schlüssel (Die benötigt wird, um die Nachricht zu Lesen, verschlüsselt unter dem Server-public-key). Oder anders ausgedrückt, wenn der server und client haben beide den gleichen privaten Schlüssel haben, dann sollten Sie nur AES verwenden. Warum machst du das ?
Wirklich, sollten Sie senden Sie einfach eine Nachricht an den client mit ssl/tls/https ,da das schreiben kryptographische code ist problematisch, und Sie haben mindestens zwei schlechte Fehler in Ihrem code zusammen mit den Sie möchten fixiert werden.
Ihre IV muss sicher zufällig. Die python random nicht nennen, das ist, warum Sie verwenden os.random(16) für den Schlüssel. Sie sollten dies tun, für die IV als auch
Benötigen Sie ein hmac zur Authentifizierung der verschlüsselten Daten und Schlüssel hmac mit einem separaten zufälligen Schlüssel. Dann mit dem gleichen key auf das andere Ende, regenerieren die hmac über die gleichen Eingänge, und die beiden vergleichen. Wenn Sie dies nicht tun, könnte jemand unbefugt auf Ihre Daten und nutzen die Fehler in der Krypto-Bibliotheken zu Lesen.
Das problem, das Sie gebucht: Beachten Sie, wie ich oben sagte, sollten Sie das nicht tun, weil es unsinnig. Sie müssen verschlüsselte rsaEncryptedPasswordWithServer mit AES unter einen neuen Schlüssel(und HMAC gemäß 2 oben) und dann verschlüsseln des neuen Schlüssels mit dem Kunden die öffentlichen Schlüssel.