能否在C#/.NET 6中解密openssl enc -k加密的AES密码保护文件?
OpenSSL AES-256-CBC 命令对应的C#原生解密实现
核心原理
要复现openssl enc的解密逻辑,需要注意三个核心规则:
- 加密生成的文件前16字节为固定头:前8字节是ASCII字符串
Salted__,后8字节是本次加密生成的随机盐 - 密钥和IV通过OpenSSL自定义的
EVP_BytesToKey算法生成,默认使用MD5哈希、迭代次数为1,不使用Rfc2898DeriveBytes对应的PBKDF2标准 - AES参数为256位密钥、CBC模式、PKCS7填充,和通用AES实现一致
完整可运行代码
using System; using System.IO; using System.Security.Cryptography; using System.Text; public static class OpenSslAesDecryptor { /// <summary> /// 解密OpenSSL enc -aes-256-cbc -k <密码> 加密的文件 /// </summary> /// <param name="inputFilePath">加密文件路径</param> /// <param name="outputFilePath">解密后输出路径</param> /// <param name="password">加密密码</param> /// <param name="hashAlgorithmName">OpenSSL加密时的哈希算法,1.1.0之前版本默认MD5,1.1.0及之后默认SHA256</param> public static void DecryptFile(string inputFilePath, string outputFilePath, string password, HashAlgorithmName? hashAlgorithmName = null) { hashAlgorithmName ??= HashAlgorithmName.MD5; // 适配旧版本openssl默认值,新版本默认加密可改为HashAlgorithmName.SHA256 const int saltLength = 8; const int keyLength = 32; // AES256对应32字节密钥 const int ivLength = 16; // CBC模式对应16字节IV // 读取加密文件内容 var encryptedData = File.ReadAllBytes(inputFilePath); // 校验文件头是否为Salted__ var header = Encoding.ASCII.GetString(encryptedData, 0, 8); if (header != "Salted__") { throw new InvalidDataException("文件不是标准OpenSSL enc带盐加密的格式"); } // 提取盐值 var salt = new byte[saltLength]; Array.Copy(encryptedData, 8, salt, 0, saltLength); // 提取实际密文 var cipherText = new byte[encryptedData.Length - 16]; Array.Copy(encryptedData, 16, cipherText, 0, cipherText.Length); // 调用EVP_BytesToKey生成密钥和IV var (key, iv) = EVP_BytesToKey(password, salt, hashAlgorithmName.Value, 1, keyLength, ivLength); // 执行AES解密 using var aes = Aes.Create(); aes.Key = key; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using var decryptor = aes.CreateDecryptor(); using var ms = new MemoryStream(); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write); cs.Write(cipherText, 0, cipherText.Length); cs.FlushFinalBlock(); // 写入解密后的文件 File.WriteAllBytes(outputFilePath, ms.ToArray()); } /// <summary> /// 复现OpenSSL的EVP_BytesToKey算法 /// </summary> private static (byte[] key, byte[] iv) EVP_BytesToKey(string password, byte[] salt, HashAlgorithmName hashAlgorithm, int iterations, int keyLength, int ivLength) { var passwordBytes = Encoding.UTF8.GetBytes(password); var totalLength = keyLength + ivLength; var result = new byte[totalLength]; var currentLength = 0; var previousHash = Array.Empty<byte>(); using var hash = HashAlgorithm.Create(hashAlgorithm.Name!) ?? throw new CryptographicException($"不支持的哈希算法:{hashAlgorithm.Name}"); while (currentLength < totalLength) { // 哈希输入 = 上一次哈希结果 + 密码 + 盐 var hashInput = new byte[previousHash.Length + passwordBytes.Length + salt.Length]; Buffer.BlockCopy(previousHash, 0, hashInput, 0, previousHash.Length); Buffer.BlockCopy(passwordBytes, 0, hashInput, previousHash.Length, passwordBytes.Length); Buffer.BlockCopy(salt, 0, hashInput, previousHash.Length + passwordBytes.Length, salt.Length); // 迭代计算哈希 var currentHash = hash.ComputeHash(hashInput); for (int i = 1; i < iterations; i++) { currentHash = hash.ComputeHash(currentHash); } // 复制到结果数组 var copyLength = Math.Min(currentHash.Length, totalLength - currentLength); Buffer.BlockCopy(currentHash, 0, result, currentLength, copyLength); currentLength += copyLength; previousHash = currentHash; } // 拆分密钥和IV var key = new byte[keyLength]; var iv = new byte[ivLength]; Buffer.BlockCopy(result, 0, key, 0, keyLength); Buffer.BlockCopy(result, keyLength, iv, 0, ivLength); return (key, iv); } // 测试调用示例 public static void Main() { DecryptFile( inputFilePath: @"C:\test\encrypted.bin", outputFilePath: @"C:\test\decrypted.txt", password: "你的加密密码" // 如果是openssl 1.1.0及以上版本加密且没指定-md参数,取消注释下面这行 // hashAlgorithmName: HashAlgorithmName.SHA256 ); } }
常见问题说明
- 出现
padding invalid错误90%以上是两个原因:一是用了PBKDF2生成密钥而不是EVP_BytesToKey,二是哈希算法和加密时的openssl版本默认值不匹配,只要对应修改hashAlgorithmName参数即可 - 如果加密时添加了
-nosalt参数,需要去掉盐读取逻辑,直接用空盐计算密钥和IV即可 - 本实现仅针对
openssl enc命令的默认加密逻辑,如果你加密时指定了-md、-iv、-K等自定义参数,需要对应调整密钥、IV生成逻辑
内容的提问来源于stack exchange,提问作者EricBDev
相关产品推荐
相关产品推荐

