C# RFC2898DeriveBytes работает, но сгенерированный ключ Python PBKDF2 и IV не работают с расшифровкой Python AES
У меня есть проблема с расшифровкой зашифрованного текста AES, спецификации которого соответствуют следующим. Зашифрованный текст состоит из: · 256 байтов соли, полученной из RFC2898, за которой следует сообщение, зашифрованное AES с использованием пароля, 'password' и выведено IV. Пример сообщения: "Это моя секретная строка, lorem ipsum", а пароль - "пароль", который зашифрован с использованием кода C#. Это сообщение прекрасно расшифровывается с помощью следующего кода C#.
private static readonly int SALT_SIZE = 256;
public static void Decrytor(){
// Encrypted Message
var cipherText = "i+EKwmlAF0VYh4GwDd+bGf3+yreYsPJW2Oq/w9FXjsp7RI3VqRiqtnqiAD4n6U0JJSTe2ct4B7lgrG+dHxeGcXYEYIERXvU0xnUdH+z3mRwmgYOqCU9HRUKy/z3GKISTm8qH030KTYm3YMBjnKpU8gaRcoDPP/nCiB3o5fPdyspgJgT/qt5BuvwYq7n0qg6ez/Wi4447gq/qHwG3wuuYLSBUCfmIkgGaO1KXqv3SsR8EAhrmMBmPDJfjc3sydNqs5B8J9/JvZFEZULTb8rLQZKQvgHhH9/53Bzs3zmoq0RFbgSueUbyeWb9rLAzYieTz8Yj0srG4GtwPrTPoItc6/hvx5stZ6pX8tgyk9Y3baT0JFMtGgxve7yduy8idTCQdAwRc5NOo4+CBk7P/sIw6+Q==";
var key = "password";
// Extract the salt from our cipherText
var allTheBytes = Convert.FromBase64String(cipherText);
var saltBytes = allTheBytes.Take(SALT_SIZE).ToArray();
var cipherTextBytes = allTheBytes.Skip(SALT_SIZE).Take(allTheBytes.Length - SALT_SIZE).ToArray();
var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes);
// Derive the previous IV from the Key and Salt
var keyBytes = keyDerivationFunction.GetBytes(32);
var ivBytes = keyDerivationFunction.GetBytes(16);
// Create a decrytor to perform the stream transform.
// Create the streams used for decryption.
// The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
var aesManaged = new AesManaged();
var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes);
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
var streamReader = new StreamReader(cryptoStream);
// Return the decrypted bytes from the decrypting stream.
Console.WriteLine("\n{0}\n", streamReader.ReadToEnd());
}
Вывод: "Это моя секретная строка, Lorem Ipsum"
но когда я пытаюсь расшифровать сообщение, следуя эквивалентной реализации Python2.7, он не дешифрует первые несколько символов правильно
import base64
from Crypto.Cipher import AES
from Crypto.Protocol import KDF
def p_decrypt( self, text ):
text_dec = base64.b64decode(text)
salt = text_dec[:256]
enc_txt = text_dec[256:]
key_bytes = KDF.PBKDF2(self.key, salt, dkLen=32)
iv = KDF.PBKDF2(self.key, salt)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
return cipher.decrypt(enc_txt)
Вывод: " 增 "j "t string, lorem ipsum"
Ожидаемый результат: "Это моя секретная строка, Lorem Ipsum"
Я попытался найти проблему, когда я использовал keyBytes и IV, сгенерированные методом C# RFC2898DeriveBytes, который также отлично работает с кодом Python, но код Python неправильно расшифровывает все сообщение, используя сгенерированные PBKDF2 keyBytes и IV.
И C# RFC2898DeriveBytes, и Python PBKDF2 генерируют keyBytes, используя алгоритм хэширования HMACSHA1, но метод C# RFC2898DeriveBytes генерирует разные keyBytes и IV, тогда как Python PBKDF2 возвращает первые 16 байтов сгенерированных keyBytes для вызова IV.
Пожалуйста, дайте мне несколько полезных рекомендаций по этому вопросу.
Спасибо, М Умер
2 ответа
Rfc2898DeriveBytes
является объектом потокового ответа, поэтому объединение двух последовательных вызовов аналогично выполнению одного вызова с добавлением обеих длин.
var pbkdf2WithTwoCalls = new Rfc2898DeriveBytes(...)
var pbkdf2WithOneCall = new Rfc2898DeriveBytes(sameParametersAsAbove);
byte[] twoCallA = pbkdf2WithTwoCalls.GetBytes(32);
byte[] twoCallB = pbkdf2WithTwoCalls.GetBytes(16);
byte[] oneCall = pbkdf2WithOneCall.GetBytes(32 + 16);
if (!oneCall.SequenceEquals(twoCallA.Concat(twoCallB))
throw new TheUniverseMakesNoSenseException();
Таким образом, ваше решение в Python - сделать один 48-байтовый вызов PBKDF2, а затем разделить его на 32-байтовый ключ AES и 16-байтовый IV.
Ваш ответ на расшифровку означает, что ключ верен, а IV - нет.
# coding:utf8
# python3
# pip3 install pycryptodome
import base64
from Crypto.Cipher import AES
from Crypto.Protocol import KDF
from Crypto.Util.Padding import unpad
cipherText = 'i+EKwmlAF0VYh4GwDd+bGf3+yreYsPJW2Oq/w9FXjsp7RI3VqRiqtnqiAD4n6U0JJSTe2ct4B7lgrG+dHxeGcXYEYIERXvU0xnUdH+z3mRwmgYOqCU9HRUKy/z3GKISTm8qH030KTYm3YMBjnKpU8gaRcoDPP/nCiB3o5fPdyspgJgT/qt5BuvwYq7n0qg6ez/Wi4447gq/qHwG3wuuYLSBUCfmIkgGaO1KXqv3SsR8EAhrmMBmPDJfjc3sydNqs5B8J9/JvZFEZULTb8rLQZKQvgHhH9/53Bzs3zmoq0RFbgSueUbyeWb9rLAzYieTz8Yj0srG4GtwPrTPoItc6/hvx5stZ6pX8tgyk9Y3baT0JFMtGgxve7yduy8idTCQdAwRc5NOo4+CBk7P/sIw6+Q=='
the_pass = 'password'
text_dec = base64.b64decode(cipherText)
salt = text_dec[:256]
enc_txt = text_dec[256:]
key_iv_bytes = KDF.PBKDF2(the_pass, salt, dkLen=48)
key = key_iv_bytes[:32]
iv = key_iv_bytes[32:]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(enc_txt)
plaintext = unpad(plaintext, 16)
print(plaintext)
# b'This is my secret string, lorem ipsum'