Я делаю менеджер паролей на Python, который хранит и шифрует пароли (с использованием модуля pycriptodome) с основным паролем, установленным пользователем, но расшифровка не работает. Вот функция, которую я использую для расшифровки паролей
def decrypt_data(key, iv, ct): # Makes the credentials readable and usable
print(f'{bcolors.warning}\nDebug decrypt_data(1):\tiv:\t\t\t{iv}') # debug text
print(f'Debug decrypt_data(2):\tiv variable type:\t{type(iv)}') # debug text
print(f'Debug decrypt_data(3):\tiv_bytes:\t\t{bytes(iv, 'utf-8')}') # debug text
print(f'Debug decrypt_data(4):\tiv_bytes type:\t\t{type(bytes(iv, 'utf-8'))}\n{bcolors.endc}') # debug text
cipher = AES.new(key, AES.MODE_CBC)
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
print(f'{bcolors.okcyan}Decripting done{bcolors.endc}')
return decrypted.decode("UTF-8")
Вывод консоли:
Debug decrypt_data(1): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug decrypt_data(2): iv variable type: <class 'str'>
Debug decrypt_data(3): iv_bytes: b'+00X7cdUZm7L61ifRb9EDQ=='
Debug decrypt_data(4): iv_bytes type: <class 'bytes'>
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'{bcolors.warning}Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Cipher\_mode_cbc.py", line 246, in decrypt
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
Для справки вот код шифрования, а также часть основной функции.
def encrypt_data(key, pt): # protects the credentials in the file once saved
print(f'\nDebug encrypt_data(1):\tplaintext:\t\t{pt}') # debug text
print(f'Debug encrypt_data(2):\tbytetext:\t\t{bytes(pt, 'utf-8')}') # debug text
print(f'Debug encrypt_data(3):\tpadded plaintext:\t{pad(bytes(pt, 'utf-8'), AES.block_size)}') # debug text
cipher = AES.new(key, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(bytes(pt, 'utf-8'), AES.block_size))
print(f'Debug encrypt_data(4):\tct_bytes:\t\t{ct_bytes}') # debug text
iv = b64encode(cipher.iv).decode('utf-8')
ct = b64encode(ct_bytes).decode('utf-8')
print(f'Debug encrypt_data(5):\tb64 decoded ct:\t\t{ct}') # debug text
print(f'Debug encrypt_data(6):\tiv:\t\t\t{iv}') # debug text
print(f'Debug encrypt_data(7):\tciphertext:\t\t{ct}\n') # debug text
print(f'Encripting done')
return ct, iv
if __name__ == '__main__':
# login screen
if not os.path.exists(credentials_file):
signin()
main_pw = login()
padded_key = pad(bytes(main_pw, 'utf-8'), AES.block_size) # Pad the key to the correct length
print(f'Debug main(1):\t\tmain_pw:\t\t{main_pw}') # debug text
print(f'Debug main(2):\t\tbyte_pw:\t\t{bytes(main_pw, 'utf-8')}') # debug text
print(f'Debug main(3):\t\tpadded_key:\t\t{padded_key}') # debug text
print(f'Debug main(4):\t\tencrypted main_pw:\t{encrypt_data(padded_key, main_pw)[0]}') # debug text
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
print(f'Debug main(6):\t\tunpadded decrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}\n') # debug text
это полный вывод консоли:
Debug main(1): main_pw: test
Debug main(2): byte_pw: b'test'
Debug main(3): padded_key: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'I\xdd\x95\x9fXl"\xa1\xd9Xfd~\xc5\xc2\xcd'
Debug encrypt_data(5): b64 decoded ct: Sd2Vn1hsIqHZWGZkfsXCzQ==
Debug encrypt_data(6): iv: lmuFGlygOos07SPYpBWNBw==
Debug encrypt_data(7): ciphertext: Sd2Vn1hsIqHZWGZkfsXCzQ==
Encripting done
Debug main(4): encrypted main_pw: Sd2Vn1hsIqHZWGZkfsXCzQ==
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'\t\r\xfbd\x07\x1d\xcay>\xa49\xdeK\xd9W\xbb'
Debug encrypt_data(5): b64 decoded ct: CQ37ZAcdynk+pDneS9lXuw==
Debug encrypt_data(6): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug encrypt_data(7): ciphertext: CQ37ZAcdynk+pDneS9lXuw==
Encripting done
Debug encrypt_data(1): plaintext: test
Debug encrypt_data(2): bytetext: b'test'
Debug encrypt_data(3): padded plaintext: b'test\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Debug encrypt_data(4): ct_bytes: b'=\xf6\xa4\xcetfD\xe4\x12\xef\xff\xce\x0f(]t'
Debug encrypt_data(5): b64 decoded ct: PfakznRmROQS7//ODyhddA==
Debug encrypt_data(6): iv: w/FfW0RCcLqv8FjqRtCpxg==
Debug encrypt_data(7): ciphertext: PfakznRmROQS7//ODyhddA==
Encripting done
Debug decrypt_data(1): iv: +00X7cdUZm7L61ifRb9EDQ==
Debug decrypt_data(2): iv variable type: <class 'str'>
Debug decrypt_data(3): iv_bytes: b'+00X7cdUZm7L61ifRb9EDQ=='
Debug decrypt_data(4): iv_bytes type: <class 'bytes'>
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(bytes(ct, 'utf-8')), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Cipher\_mode_cbc.py", line 246, in decrypt
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
[processo terminato con codice 1 (0x00000001)]
Я попробовал заполнить перед расшифровкой, используя pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
вместо pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
, но выдал следующую ошибку:
Traceback (most recent call last):
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 151, in <module>
print(f'Debug main(5):\t\tdecrypted main_pw:\t{decrypt_data(padded_key, encrypt_data(padded_key, main_pw)[1], encrypt_data(padded_key, main_pw)[0])}') # debug text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\py_proj\Password Manager\.venv\Lib\pw_manager.py", line 107, in decrypt_data
pt = unpad(cipher.decrypt(pad(bytes(ct, 'utf-8'), AES.block_size)), AES.block_size)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Utente\AppData\Local\Programs\Python\Python312\Lib\site-packages\Crypto\Util\Padding.py", line 92, in unpad
raise ValueError("Padding is incorrect.")
ValueError: Padding is incorrect.
[processo terminato con codice 1 (0x00000001)]
Во-вторых: вы возвращаете decrypted.decode(...)
, но никогда ничего не назначаете decrypted
. Я не думаю, что код, который вы нам показываете, поднимает эти проблемы.
Ваша функция decrypt_data
также не использует IV, который вы передаете в качестве аргумента.
он передает аргумент iv, потому что в более старой версии программы я создавал зашифрованный объект с помощью cipher = AES.new(key, AES.MODE_CBC, iv=iv)
, но он выдал ошибку Incorrect IV length (it must be 16 bytes long)'. Removing
iv=iv` эта ошибка больше не выводилась, но я все еще не могу проверить, расшифровывается ли он зашифрованные тексты правильно, с ним или без него, из-за моей главной проблемы, я думаю, это проблема для меня в будущем. Я не писал об этом, потому что забыл об этом.
Конечно, вы должны использовать IV во время расшифровки. Ошибка в вашем коде заключается в отсутствии декодирования Base64 зашифрованного текста и IV во время расшифровки. Другая ошибка заключается в том, что в тестах 5 и 6 зашифрованный текст и IV несовместимы, поскольку они исходят от двух разных вызовов encrypt_data()
.
Вот упрощенная и функциональная версия вашего кода, которая для простоты принимает простые текстовые строки в кодировке Base64 в качестве ключа и материала IV (их легко хранить, например, в текстовых файлах).
Обратите внимание, что режим CBC, вероятно, не то, что вам нужно, поскольку он не обеспечивает никакой аутентификации (т. е. вы получаете обратно текст, который был изначально зашифрован).
import base64
import secrets
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
def encrypt_data(key_str: str, plaintext: str) -> tuple[str, str]:
key_bytes = base64.b64decode(key_str.encode("utf-8"))
cipher = AES.new(key_bytes, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(plaintext.encode("utf-8"), AES.block_size))
iv = base64.b64encode(cipher.iv).decode("utf-8")
ciphertext = base64.b64encode(ct_bytes).decode("utf-8")
return ciphertext, iv
def decrypt_data(key_str: str, iv_str: str, ciphertext_str: str) -> str:
key_bytes = base64.b64decode(key_str.encode("utf-8"))
iv_bytes = base64.b64decode(iv_str.encode("utf-8"))
ciphertext_bytes = base64.b64decode(ciphertext_str.encode("utf-8"))
cipher = AES.new(key_bytes, AES.MODE_CBC, iv=iv_bytes)
plaintext_bytes = unpad(cipher.decrypt(ciphertext_bytes), AES.block_size)
return plaintext_bytes.decode("UTF-8")
key_bytes = secrets.token_bytes(32)
key_str = base64.b64encode(key_bytes).decode("utf-8")
plaintext = "Hello, World!"
print(f"{key_str=}, {plaintext=}")
ciphertext, iv = encrypt_data(key_str, plaintext)
print(f"{ciphertext=}, {iv=}")
decrypted = decrypt_data(key_str, iv, ciphertext)
print(f"{decrypted=}")
Это распечатывает (например)
key_str='Zdu1DUQC/dZhGC8Q1n0raD8BlW7pfzLZxwmPqn0o6s8=', plaintext='Hello, World!'
ciphertext='Es7w8eWcRHGhfpwBDnr6vQ==', iv='Grf/VFkH8Qp7VS6RVj2e2g=='
decrypted='Hello, World!'
Вероятно, вам не следует использовать режим шифрования без аутентификации, такой как CBC. Вы же хотите быть уверены, что зашифрованный текст не был подделан (или поврежден), верно?