การถอดรหัส AES CBC ใน python จาก OpenSSL AES

Sep 30 2020

ฉันต้องการถอดรหัสไฟล์ที่เข้ารหัสบน OpenSSL ด้วย python แต่ฉันไม่เข้าใจตัวเลือกของ pycrypto

นี่คือสิ่งที่ฉันทำใน OpenSSL

  1. openssl enc -aes-256-cbc -a -salt -pbkdf2 -iter 100000 -in "clear.txt" -out "crypt.txt" -pass pass: "mypassword"

  2. openssl enc -d -aes-256-cbc -a -pbkdf2 -iter 100000 -in "crypt.txt" -out "out.txt" -pass pass: "mypassword"

ฉันพยายามแล้ว (ซึ่งเห็นได้ชัดว่าใช้ไม่ได้)

obj2 = AES.new("mypassword", AES.MODE_CBC)
output = obj2.decrypt(text)

ฉันแค่ต้องการทำขั้นตอนที่สองใน python แต่เมื่อดูตัวอย่าง:

https://pypi.org/project/pycrypto/

obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
obj2.decrypt(ciphertext)

ฉันไม่ต้องการ IV ฉันจะระบุเกลือได้อย่างไร? แฮช pbkdf2? ฉันยังดูที่หัวข้อนี้

จะถอดรหัสไฟล์ที่เข้ารหัส OpenSSL AES ใน Python ได้อย่างไร

แต่ไม่ได้ช่วย

ใครช่วยแสดงวิธีทำโดยใช้ python ได้ไหม

ขอขอบคุณ.

คำตอบ

4 Topaco Sep 30 2020 at 04:16

คำสั่ง OpenSSL ใช้ PBKDF2 เพื่อสร้างคีย์ 32 ไบต์และ 16 ไบต์ IV สำหรับสิ่งนี้เกลือ 8 ไบต์แบบสุ่มจะถูกสร้างขึ้นโดยปริยายและใช้รหัสผ่านที่ระบุจำนวนการวนซ้ำและการแยกย่อย (ค่าเริ่มต้น: SHA-256) คู่คีย์ / IV ใช้เพื่อเข้ารหัสข้อความธรรมดาด้วย AES-256 ในโหมด CBC และช่องว่างภายใน PKCS7 s ที่นี่ . ผลลัพธ์จะถูกส่งคืนในรูปแบบ OpenSSL ซึ่งเริ่มต้นด้วยการเข้ารหัส ASCII 8 ไบต์ของSalted__ตามด้วยเกลือ 8 ไบต์และไซเฟอร์เท็กซ์จริงเข้ารหัส Base64 ทั้งหมด เกลือเป็นสิ่งจำเป็นสำหรับการถอดรหัสดังนั้นจึงสามารถสร้างคีย์และ IV ขึ้นมาใหม่ได้

โปรดทราบว่ารหัสผ่านในคำสั่ง OpenSSL นั้นถูกส่งผ่านโดยไม่มีเครื่องหมายอัญประกาศกล่าวคือในคำสั่ง OpenSSL ที่โพสต์เครื่องหมายคำพูดเป็นส่วนหนึ่งของรหัสผ่าน

สำหรับการถอดรหัสใน Python นั้นจะต้องกำหนด salt และ ciphertext จากข้อมูลที่เข้ารหัสก่อน ด้วยเกลือสามารถสร้างคีย์ / คู่ IV ขึ้นใหม่ได้ ในที่สุดก็สามารถใช้คู่คีย์ / IV สำหรับการถอดรหัสได้

ตัวอย่าง: ด้วยคำสั่ง OpenSSL ที่โพสต์ข้อความธรรมดา

The quick brown fox jumps over the lazy dog

ถูกเข้ารหัสเป็น ciphertext

U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw== 

การถอดรหัสด้วย Python ทำได้ดังนี้ (โดยใช้PyCryptodome ):

from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES
import base64

# Determine salt and ciphertext
encryptedDataB64 = 'U2FsdGVkX18A+AhjLZpfOq2HilY+8MyrXcz3lHMdUII2cud0DnnIcAtomToclwWOtUUnoyTY2qCQQXQfwDYotw=='
encryptedData = base64.b64decode(encryptedDataB64)
salt = encryptedData[8:16]
ciphertext = encryptedData[16:]

# Reconstruct Key/IV-pair
pbkdf2Hash = PBKDF2(b'"mypassword"', salt, 32 + 16, count=100000, hmac_hash_module=SHA256)
key = pbkdf2Hash[0:32]
iv = pbkdf2Hash[32:32 + 16]

# Decrypt with AES-256 / CBC / PKCS7 Padding
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = unpad(cipher.decrypt(ciphertext), 16)

print(decrypted)

แก้ไข - เกี่ยวกับความคิดเห็นของคุณ: ควรเป็นไปได้ 16 MB แต่สำหรับข้อมูลที่มีขนาดใหญ่ขึ้นโดยทั่วไปข้อความเข้ารหัสจะถูกอ่านจากไฟล์และข้อมูลที่ถอดรหัสจะถูกเขียนลงในไฟล์ซึ่งตรงกันข้ามกับตัวอย่างที่โพสต์ไว้ด้านบน
การถอดรหัสข้อมูลในขั้นตอนเดียวนั้นขึ้นอยู่กับหน่วยความจำที่มีอยู่หรือไม่ หากหน่วยความจำไม่เพียงพอข้อมูลจะต้องถูกประมวลผลเป็นชิ้น ๆ
เมื่อใช้ชิ้นส่วนมันจะสมเหตุสมผลกว่าที่จะไม่เข้ารหัส Base64 ข้อมูลที่เข้ารหัส แต่เก็บไว้ในรูปแบบไบนารีโดยตรง สิ่งนี้เป็นไปได้โดยการละเว้นอ็อพชัน-aในคำสั่ง OpenSSL มิฉะนั้นจะต้องมั่นใจว่าจะมีการโหลดจำนวนเต็มทวีคูณของขนาดบล็อกเสมอ (เทียบกับไซเฟอร์เท็กซ์ที่ไม่ได้ถอดรหัส) โดยที่ 3 ไบต์ของไซเฟอร์เท็กซ์ที่ไม่ได้ถอดรหัสจะสอดคล้องกับ 4 ไบต์ของการเข้ารหัส Base64

ในกรณีของการเข้ารหัสไบนารีที่เก็บไว้: ในระหว่างการถอดรหัสเฉพาะบล็อกแรก (16 ไบต์) เท่านั้นที่ควรอ่าน (ไบนารี) ในขั้นตอนแรก จากสิ่งนี้สามารถกำหนดเกลือได้ (ไบต์ 8 ถึง 16) จากนั้นคีย์และ IV (คล้ายกับรหัสที่โพสต์ด้านบน)
ส่วนที่เหลือของไซเฟอร์เท็กซ์สามารถ (ไบนารี) อ่านเป็นกลุ่มขนาดที่เหมาะสม (= จำนวนหลายขนาดของบล็อกเช่น 1024 ไบต์) แต่ละกลุ่มจะถูกเข้ารหัส / ถอดรหัสแยกกันดูการเข้ารหัส / ถอดรหัส - การโทรหลายรายการ สำหรับการอ่าน / เขียนไฟล์ใน chunks กับงูหลามเห็นเช่นที่นี่
รายละเอียดเพิ่มเติมเป็นคำตอบที่ดีที่สุดภายในขอบเขตของคำถามแยกต่างหาก