โดย Lane Wagner — @wagslane บน Twitter
ต้องการเข้ารหัสข้อความด้วยรหัสผ่านหรือคีย์ส่วนตัวใน Python หรือไม่? คุณมาถูกที่แล้ว. AES-256 เป็นรหัสแบบสมมาตรทึบที่ใช้กันทั่วไปในการเข้ารหัสข้อมูลสำหรับตนเอง กล่าวอีกนัยหนึ่ง บุคคลคนเดียวกันกับที่เข้ารหัสข้อมูลมักจะถอดรหัสข้อมูลนั้นด้วยเช่นกัน (ลองนึกถึง "ตัวจัดการรหัสผ่าน")
การพึ่งพาอาศัยกัน
สำหรับบทช่วยสอนนี้ เราจะใช้ Python 3 ตรวจสอบให้แน่ใจว่าคุณติดตั้ง pycrypto ซึ่งจะทำให้เราสามารถเข้าถึงการใช้งาน AES-256 ได้:
pip install pycrypto
การขยายความ
AES-256 กำหนดให้ข้อมูลที่จะเข้ารหัสนั้นอยู่ในบล็อกขนาด 16 ไบต์ เราจะเพิ่มช่องว่างที่ส่วนท้ายของไซเฟอร์เท็กซ์ของเราอย่างไร้เดียงสาเพื่อให้เป็นไปตามข้อกำหนดนั้น:
# pad with spaces at the end of the text
# beacuse AES needs 16 byte blocks
def pad(s):
block_size = 16
remainder = len(s) % block_size
padding_needed = block_size - remainder
return s + padding_needed * ' '
นอกจากนี้เรายังจะสร้างฟังก์ชัน unpad() ที่ตัดช่องว่างเพิ่มเติมออกหลังจากการถอดรหัส:
# remove the extra spaces at the end
def unpad(s):
return s.rstrip()
กำลังเข้ารหัส
ตอนนี้เราสร้างฟังก์ชัน encrypt(plain_text,password) แบบง่ายๆ ฟังก์ชันนี้ใช้รหัสผ่านเพื่อเข้ารหัสข้อความธรรมดา ใครก็ตามที่สามารถเข้าถึงข้อความที่เข้ารหัสและรหัสผ่านจะสามารถถอดรหัสได้
def encrypt(plain_text, password):
# generate a random salt
salt = os.urandom(AES.block_size)
# generate a random iv
iv = Random.new().read(AES.block_size)
# use the Scrypt KDF to get a private key from the password
private_key = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)
# pad text with spaces to be valid for AES CBC mode
padded_text = pad(plain_text)
# create cipher config
cipher_config = AES.new(private_key, AES.MODE_CBC, iv)
# return a dictionary with the encrypted text
return {
'cipher_text': base64.b64encode(cipher_config.encrypt(padded_text)),
'salt': base64.b64encode(salt),
'iv': base64.b64encode(iv)
}
หมายเหตุเกี่ยวกับฟังก์ชันเข้ารหัส ()
- IV: เวกเตอร์การเริ่มต้น เวกเตอร์การเริ่มต้นต้องเป็นแบบสุ่มและใหม่ทุกครั้งที่ใช้ฟังก์ชันการเข้ารหัสของเรา คิดว่ามันเป็นเกลือสุ่มสำหรับการเข้ารหัส
- Scrypt: Scrypt ใช้เพื่อสร้างคีย์ส่วนตัวที่ปลอดภัยจากรหัสผ่าน สิ่งนี้จะทำให้ผู้โจมตีบังคับการเข้ารหัสของเราได้ยากขึ้น
- เกลือ: เกลือสุ่มใหม่จะถูกใช้สำหรับการเข้ารหัสของเราแต่ละครั้ง ซึ่งทำให้ผู้โจมตีไม่สามารถใช้แฮชที่คำนวณไว้ล่วงหน้าเพื่อพยายามถอดรหัสรหัสได้ (ดู ตารางสีรุ้ง)
- เข้ารหัส "พารามิเตอร์":
- N คือปัจจัยด้านต้นทุน จะต้องเป็นกำลังของสอง และยิ่งสูงเท่าไรก็ยิ่งมีความปลอดภัยมากขึ้นเท่านั้น แต่ต้องใช้ทรัพยากรในการรันมากขึ้น
- R คือขนาดบล็อก
- P คือปัจจัยการขนาน ซึ่งมีประโยชน์สำหรับการรันบนหลายคอร์
- Base64: เราเข้ารหัสข้อมูลประเภทไบต์ทั้งหมดของเราเป็น base64 เพื่อความสะดวกในการแสดงสตริง
กำลังถอดรหัส
def decrypt(enc_dict, password):
# decode the dictionary entries from base64
salt = base64.b64decode(enc_dict['salt'])
enc = base64.b64decode(enc_dict['cipher_text'])
iv = base64.b64decode(enc_dict['iv'])
# generate the private key from the password and salt
private_key = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)
# create the cipher config
cipher = AES.new(private_key, AES.MODE_CBC, iv)
# decrypt the cipher text
decrypted = cipher.decrypt(enc)
# unpad the text to remove the added spaces
original = unpad(decrypted)
return original
หมายเหตุเกี่ยวกับฟังก์ชันถอดรหัส ()
- ฟังก์ชัน decrypt() ต้องการเกลือและ iv แบบเดียวกับที่ใช้ในการเข้ารหัส เราใช้พจนานุกรมเพื่อความสะดวกในการแยกวิเคราะห์ แต่ถ้าเราต้องการไซเฟอร์เท็กซ์หนึ่งสตริงแทน เราก็อาจใช้รูปแบบเช่น salt.iv.cipher_text
- พารามิเตอร์การกำหนดค่าบนฟังก์ชัน Scrypt และ AES จะต้องเหมือนกับฟังก์ชันเข้ารหัส
ขอรหัสเต็มให้ฉัน!
คุณอาจต้องการเห็นการทำงานทั้งหมดในสคริปต์ตัวอย่าง ไม่ต้องมองอีกต่อไป!
# AES 256 encryption/decryption using pycrypto library
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
import os
# pad with spaces at the end of the text
# beacuse AES needs 16 byte blocks
def pad(s):
block_size = 16
remainder = len(s) % block_size
padding_needed = block_size - remainder
return s + padding_needed * ' '
# remove the extra spaces at the end
def unpad(s):
return s.rstrip()
def encrypt(plain_text, password):
# generate a random salt
salt = os.urandom(AES.block_size)
# generate a random iv
iv = Random.new().read(AES.block_size)
# use the Scrypt KDF to get a private key from the password
private_key = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)
# pad text with spaces to be valid for AES CBC mode
padded_text = pad(plain_text)
# create cipher config
cipher_config = AES.new(private_key, AES.MODE_CBC, iv)
# return a dictionary with the encrypted text
return {
'cipher_text': base64.b64encode(cipher_config.encrypt(padded_text)),
'salt': base64.b64encode(salt),
'iv': base64.b64encode(iv)
}
def decrypt(enc_dict, password):
# decode the dictionary entries from base64
salt = base64.b64decode(enc_dict['salt'])
enc = base64.b64decode(enc_dict['cipher_text'])
iv = base64.b64decode(enc_dict['iv'])
# generate the private key from the password and salt
private_key = hashlib.scrypt(password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)
# create the cipher config
cipher = AES.new(private_key, AES.MODE_CBC, iv)
# decrypt the cipher text
decrypted = cipher.decrypt(enc)
# unpad the text to remove the added spaces
original = unpad(decrypted)
return original
def main():
password = input("Password: ")
# First let us encrypt secret message
encrypted = encrypt("The secretest message", password)
print(encrypted)
# Let us decrypt using our original password
decrypted = decrypt(encrypted, password)
print(bytes.decode(decrypted))
ขอบคุณที่อ่าน
Lane on Twitter: @wagslane
Lane บน Dev.to: wagslane
ดาวน์โหลด Qvault: https://qvault.io
โพสต์ AES-256 Cipher — ตัวอย่างการเข้ารหัส Python ปรากฏตัวครั้งแรกใน Qvault