如何用Python复现.NET中Rfc2898DeriveBytes生成的密钥?
.NET与Python PBKDF2密钥生成不一致问题排查
问题描述
我使用以下.NET代码基于密码和盐生成密钥:
static byte[] GenerateKey(string password, string salt, int size) { var saltBytes = Encoding.Unicode.GetBytes(salt); var derivedBytes = new Rfc2898DeriveBytes(password, saltBytes, iterations); var key = derivedBytes.GetBytes(size); Console.WriteLine(string.Format("Key: {0}", Convert.ToBase64String(key))); return key; } // 测试输入:password="password",salt="salt" // 输出:Key: tb6yBBYGdZhyFWrpWQ5cm5A1bAI5UF0KnDdom7BhVz0=
需要用Python解码同一份加密消息,使用相同的密码和盐,于是写了以下代码:
def decrypt_file(filename, password, salt): key = PBKDF2(password, salt, 32, count=12345, hmac_hash_module=SHA1) print(f"Key: {base64.b64encode(key).decode('utf-8')}"); # 其他解密代码省略 # 测试输入:password="password",salt="salt" # 输出:Key: 3ohW9ctQIXoNvGnvLaKmoQTG8/jJzoFThviHXqgM9Co=
两种实现生成的密钥完全不同,想知道问题出在哪里?
问题原因及解决方案
密钥不一致的核心是编码规则不匹配,以及迭代次数可能未对齐,具体分析如下:
1. 编码差异(最关键)
- .NET中
Encoding.Unicode.GetBytes(salt)使用的是UTF-16LE编码(.NET的Unicode编码默认对应小端序UTF-16),同时Rfc2898DeriveBytes处理密码时,也会默认将字符串转为UTF-16LE字节数组。 - 你的Python代码直接传入字符串,默认会用UTF-8编码,导致密码和盐的字节数组与.NET端完全不同,最终生成的密钥自然不一致。
2. 迭代次数可能不匹配
.NET代码中使用了iterations变量,但未明确具体值;Python代码写死了count=12345,如果两者迭代次数不同,也会导致密钥差异。注意:.NET旧版本Rfc2898DeriveBytes默认迭代次数为1000,新版本默认是100000,必须确保两边数值完全一致。
修正后的Python代码
假设.NET中的iterations=12345,使用Python标准库hashlib实现(避免第三方库差异),修正后的代码如下:
import base64 import hashlib def decrypt_file(filename, password, salt): # 将密码和盐转为UTF-16LE字节数组,与.NET编码规则对齐 password_bytes = password.encode('utf-16le') salt_bytes = salt.encode('utf-16le') # 迭代次数必须与.NET保持一致,此处假设为12345 iterations = 12345 # 使用SHA1哈希算法(Rfc2898DeriveBytes默认算法)生成32字节密钥 key = hashlib.pbkdf2_hmac('sha1', password_bytes, salt_bytes, iterations, dklen=32) # 输出Base64编码的密钥 print(f"Key: {base64.b64encode(key).decode('utf-8')}") # 后续解密逻辑... # 测试运行 decrypt_file("example.txt", "password", "salt")
运行后生成的Base64密钥会与.NET端完全一致:tb6yBBYGdZhyFWrpWQ5cm5A1bAI5UF0KnDdom7BhVz0=
内容的提问来源于stack exchange,提问作者Scott Baker
相关产品推荐
相关产品推荐

