โดย 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)
    }

หมายเหตุเกี่ยวกับฟังก์ชันเข้ารหัส ()

  1. IV: เวกเตอร์การเริ่มต้น เวกเตอร์การเริ่มต้นต้องเป็นแบบสุ่มและใหม่ทุกครั้งที่ใช้ฟังก์ชันการเข้ารหัสของเรา คิดว่ามันเป็นเกลือสุ่มสำหรับการเข้ารหัส
  2. Scrypt: Scrypt ใช้เพื่อสร้างคีย์ส่วนตัวที่ปลอดภัยจากรหัสผ่าน สิ่งนี้จะทำให้ผู้โจมตีบังคับการเข้ารหัสของเราได้ยากขึ้น
  3. เกลือ: เกลือสุ่มใหม่จะถูกใช้สำหรับการเข้ารหัสของเราแต่ละครั้ง ซึ่งทำให้ผู้โจมตีไม่สามารถใช้แฮชที่คำนวณไว้ล่วงหน้าเพื่อพยายามถอดรหัสรหัสได้ (ดู ตารางสีรุ้ง)
  4. เข้ารหัส "พารามิเตอร์":
  5. N คือปัจจัยด้านต้นทุน จะต้องเป็นกำลังของสอง และยิ่งสูงเท่าไรก็ยิ่งมีความปลอดภัยมากขึ้นเท่านั้น แต่ต้องใช้ทรัพยากรในการรันมากขึ้น
  6. R คือขนาดบล็อก
  7. P คือปัจจัยการขนาน ซึ่งมีประโยชน์สำหรับการรันบนหลายคอร์
  8. 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

หมายเหตุเกี่ยวกับฟังก์ชันถอดรหัส ()

  1. ฟังก์ชัน decrypt() ต้องการเกลือและ iv แบบเดียวกับที่ใช้ในการเข้ารหัส เราใช้พจนานุกรมเพื่อความสะดวกในการแยกวิเคราะห์ แต่ถ้าเราต้องการไซเฟอร์เท็กซ์หนึ่งสตริงแทน เราก็อาจใช้รูปแบบเช่น salt.iv.cipher_text
  2. พารามิเตอร์การกำหนดค่าบนฟังก์ชัน 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