当前位置: 首页 > 知识库问答 >
问题:

C#RFC2898DeriveBytes正在工作,但Python PBKDF2生成的密钥和IV不能用于Python AES解密

微生博简
2023-03-14

我手头有一个解密AES加密密码文本的问题,该密码文本由以下规范组成:·256字节的RFC2898派生salt,然后是使用密码“password”和派生IV进行AES加密的消息。示例消息是“这是我的秘密字符串,lorem ipsum”,密码是“password”这是用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弦,lorem ipsum”

预期输出:“这是我的秘密字符串,lorem ipsum”

我试图找到这个问题,当我使用C#RFC2898DeriveBytes方法生成的keyBytes和IV时,该方法也适用于精细的python代码,但python代码不能使用PBKDF2生成的keyBytes和IV正确解密整个消息。

C#RFC2898DeriveBytes和python PBKDF2都使用HMACSHA1哈希算法生成密钥字节,但C#RFC2898DeriveBytes方法生成的密钥字节和IV不同,而python PBKDF2为IV调用返回生成的密钥字节的前16个字节。

请给我一些有用的指导方针。

谢谢,默尔

共有2个答案

柴意智
2023-03-14
# 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'
锺离晗昱
2023-03-14

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中的解决方案是对PBKDF2进行一个48字节的调用,然后将其拆分为32字节的AES密钥和16字节的IV。

您的解密响应表明密钥正确,但IV不正确。

 类似资料:
  • 我正在使用并且不给密码短语,然后成功生成并显示密钥指纹。 然后给然后它在提供后提示输入我的用户密码时会说明添加的密钥数:1。 现在,如果我正在执行它会再次提示输入密码。 如果我执行也是它会请求输入密码。 PS:我使用过:和每次执行时,系统提示我输入密码,似乎没有什么工作正常。 [编辑] 显示 debug1:SSH2_MSG_KEXINIT已发送 debug1:SSH2_MSG_KEXINIT已接收

  • 我正在尝试使用AES加密算法加密文本,将此加密文本保存到文件中,然后稍后重新打开并解密这些文本。下面是我的加密和解密逻辑 这就是解密逻辑 我收到一个“大小错误”异常。我的猜测是,通过使用 SecureRandom 类,这两种方法在加密或欺骗文本时使用不同的密钥。有没有办法在两个例程中使用相同的密钥?

  • 使用EVP_BytesToKey()返回错误的key和iv可能出了什么问题? 我试过用iter计数值做实验,但似乎没有一个能产生工作键和IV。我假设命令行默认的iter计数是1。 同样确认的是,如果我用命令行显示的工作键和iv覆盖从EVP_BytesToKey()返回的内容和硬代码无符号char数组,我的其余代码工作正常,解密正确。 有人能帮忙吗?

  • 我已经看到了其他一些关于创建加密的初始化向量(IV)的问题,似乎使用随机值是一种选择。然而,我需要生成用于解密的IV,所以我必须使用基于一些salt的数据加密的相同的数据。 node.js加密函数createDecipher表示: crypto.createdecipher()的实现使用OpenSSL函数EVP_BytesToKey派生密钥,摘要算法设置为MD5,一次迭代,没有salt。 好的,听

  • 在许多情况下,安全软件涉及(桌面)应用程序和web界面之间的交互。对于(RSA)非对称加密,我们使用OpenSSL的C库(目前版本为1.0.2d)和OpenSSL PHP库。 所有标准的东西: 私有加密(C)数据和公共解密(PHP) 公共加密(C)数据和私有解密(PHP) 作品还有另一种方法:PHP- 此外,使用PHP生成加密的私钥,并使用C/C解密该密钥也可以。但不是相反:我想用PHP解密一个用

  • 使用Python-GnuPG我想 null 不幸的是,加密返回错误: 但它仍然生成加密ASCII铠甲消息,如果解密结果为对象,则bool值为属性,并包含以下属性: 不确定错误发生的确切位置以及如何处理