如何用.NET 8 System.Security.Cryptography替代CryptAcquireContext实现解密
替换advapi32.dll的RC4解密逻辑为.NET 8原生实现
需求说明
- 用C# + .NET 8内置的
System.Security.Cryptography实现解密,替代原基于advapi32.dll的VB加密/解密逻辑 - 无法修改第三方加密逻辑,需完全兼容原算法
- 解密方法会被频繁调用,要求性能可靠
- 不再依赖advapi32.dll
原VB代码(基于advapi32.dll)
#Region "Constants" Private Const lngALG_CLASS_DATA_ENCRYPT As Integer = 24576 Private Const lngALG_CLASS_HASH As Integer = 32768 Private Const lngALG_SID_MD5 As Integer = 3 Private Const lngALG_SID_RC4 As Integer = 1 Private Const lngALG_TYPE_ANY As Integer = 0 Private Const lngALG_TYPE_STREAM As Integer = 2048 Private Const lngCALG_MD5 As Integer = ((lngALG_CLASS_HASH Or lngALG_TYPE_ANY) Or lngALG_SID_MD5) Private Const lngCALG_RC4 As Integer = ((lngALG_CLASS_DATA_ENCRYPT Or lngALG_TYPE_STREAM) Or lngALG_SID_RC4) Private Const lngCRYPT_NEWKEYSET As Integer = 8 Private Const lngENCRYPT_ALGORITHM As Integer = lngCALG_RC4 Private Const lngPROV_RSA_FULL As Integer = 1 Private Const strKEY_CONTAINER As String = "TestString" Private Const strSERVICE_PROVIDER As String = "Microsoft Base Cryptographic Provider v1.0" #End Region #Region "Declarations" Private Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Integer, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Integer, ByVal dwFlags As Integer) As Integer Private Declare Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Integer, ByVal Algid As Integer, ByVal hKey As Integer, ByVal dwFlags As Integer, ByRef phHash As Integer) As Integer Private Declare Function CryptHashData Lib "advapi32.dll" (ByVal hHash As Integer, ByVal pbData As String, ByVal dwDataLen As Integer, ByVal dwFlags As Integer) As Integer Private Declare Function CryptDeriveKey Lib "advapi32.dll" (ByVal hProv As Integer, ByVal Algid As Integer, ByVal hBaseData As Integer, ByVal dwFlags As Integer, ByRef phKey As Integer) As Integer Private Declare Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As Integer) As Integer Private Declare Function CryptEncrypt Lib "advapi32.dll" (ByVal hKey As Integer, ByVal hHash As Integer, ByVal Final As Integer, ByVal dwFlags As Integer, ByVal pbData As String, ByRef pdwDataLen As Integer, ByVal dwBufLen As Integer) As Integer Private Declare Function CryptDestroyKey Lib "advapi32.dll" (ByVal hKey As Integer) As Integer Private Declare Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As Integer, ByVal dwFlags As Integer) As Integer Private Declare Function CryptDecrypt Lib "advapi32.dll" (ByVal hKey As Integer, ByVal hHash As Integer, ByVal Final As Integer, ByVal dwFlags As Integer, ByVal pbData As String, ByRef pdwDataLen As Integer) As Integer #End Region Public Shared Function Encrypt(ByVal strValue As String, ByVal strPassword As String) As String Return Encrypt_Or_Decrypt(strValue, strPassword, True) End Function Public Shared Function Decrypt(ByVal strValue As String, ByVal strPassword As String) As String Return Encrypt_Or_Decrypt(strValue, strPassword, False) End Function Private Shared Function Encrypt_Or_Decrypt(ByVal strValue As String, ByVal strKey As String, ByVal blnEncrypt As Boolean) As String Dim lngCryptProv As Integer Dim lngHash As Integer Dim lngKey As Integer Dim lngLength As Integer Try If CryptAcquireContext(lngCryptProv, strKEY_CONTAINER, strSERVICE_PROVIDER, lngPROV_RSA_FULL, lngCRYPT_NEWKEYSET) = 0 Then If CryptAcquireContext(lngCryptProv, strKEY_CONTAINER, strSERVICE_PROVIDER, lngPROV_RSA_FULL, 0) = 0 Then Throw New Exception("Error during CryptAcquireContext for a new key container." & vbCrLf & "A container with this name probably already exists.") End If End If If CryptCreateHash(lngCryptProv, lngCALG_MD5, 0, 0, lngHash) = 0 Then Throw New Exception("Could not create a Hash Object (CryptCreateHash API)") End If If CryptHashData(lngHash, strKey, strKey.Length, 0) = 0 Then Throw New Exception("Could not calculate a Hash Value (CryptHashData API)") End If If CryptDeriveKey(lngCryptProv, lngENCRYPT_ALGORITHM, lngHash, 0, lngKey) = 0 Then Throw New Exception("Could not create a session key (CryptDeriveKey API)") End If lngLength = strValue.Length If blnEncrypt Then If CryptEncrypt(lngKey, 0, 1, 0, strValue, lngLength, lngLength) = 0 Then Throw New Exception("Error during CryptEncrypt.") End If Else If CryptDecrypt(lngKey, 0, 1, 0, strValue, lngLength) = 0 Then Throw New Exception("Error during CryptDecrypt.") End If End If Return strValue.Substring(0, lngLength) Finally ' Ignore errors here Try If lngKey <> 0 Then CryptDestroyKey(lngKey) End If If lngHash <> 0 Then CryptDestroyHash(lngHash) End If If lngCryptProv <> 0 Then CryptReleaseContext(lngCryptProv, 0) End If Catch End Try End Try End Function
用户当前尝试的MD5哈希代码
using (var md5 = MD5.Create()) { var hash = md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(key)); }
问题瓶颈
.NET 8的System.Security.Cryptography命名空间中没有内置的RC4算法实现类,需要手动实现RC4逻辑以匹配原advapi32.dll的行为。
解决方案
原算法核心逻辑拆解
- 用ASCII编码将密码转为字节数组,计算MD5哈希
- 通过
CryptDeriveKey从MD5哈希派生RC4密钥:对于RC4算法,该API直接使用MD5的16字节哈希作为128位RC4密钥 - 使用RC4流加密算法对数据进行加密/解密(原代码中直接操作ASCII字符串的字节,加密解密长度一致)
.NET 8完整实现代码
using System; using System.Security.Cryptography; using System.Text; public static class Rc4CryptoHelper { /// <summary> /// 解密字符串,完全兼容原advapi32.dll的RC4逻辑 /// </summary> /// <param name="encryptedValue">加密后的ASCII字符串</param> /// <param name="password">解密密码</param> /// <returns>解密后的明文</returns> public static string Decrypt(string encryptedValue, string password) { // 1. 计算密码的MD5哈希(ASCII编码,和原VB代码一致) byte[] passwordHash; using (var md5 = MD5.Create()) { passwordHash = md5.ComputeHash(Encoding.ASCII.GetBytes(password)); } // 2. 将加密字符串转为ASCII字节数组 byte[] encryptedBytes = Encoding.ASCII.GetBytes(encryptedValue); // 3. 执行RC4解密 byte[] decryptedBytes = Rc4Transform(encryptedBytes, passwordHash); // 4. 转为ASCII字符串返回 return Encoding.ASCII.GetString(decryptedBytes); } /// <summary> /// 加密字符串,完全兼容原advapi32.dll的RC4逻辑 /// </summary> /// <param name="plainValue">明文</param> /// <param name="password">加密密码</param> /// <returns>加密后的字符串</returns> public static string Encrypt(string plainValue, string password) { byte[] passwordHash; using (var md5 = MD5.Create()) { passwordHash = md5.ComputeHash(Encoding.ASCII.GetBytes(password)); } byte[] plainBytes = Encoding.ASCII.GetBytes(plainValue); byte[] encryptedBytes = Rc4Transform(plainBytes, passwordHash); return Encoding.ASCII.GetString(encryptedBytes); } /// <summary> /// 通用RC4加密/解密方法(流加密,加密解密逻辑相同) /// </summary> private static byte[] Rc4Transform(byte[] data, byte[] key) { int[] sBox = new int[256]; int[] keyBox = new int[256]; int i, j, temp; // 初始化S盒和密钥盒 for (i = 0; i < 256; i++) { sBox[i] = i; keyBox[i] = key[i % key.Length]; } j = 0; for (i = 0; i < 256; i++) { j = (j + sBox[i] + keyBox[i]) % 256; temp = sBox[i]; sBox[i] = sBox[j]; sBox[j] = temp; } // 生成密钥流并执行异或操作 i = j = 0; byte[] result = new byte[data.Length]; for (int m = 0; m < data.Length; m++) { i = (i + 1) % 256; j = (j + sBox[i]) % 256; temp = sBox[i]; sBox[i] = sBox[j]; sBox[j] = temp; int t = (sBox[i] + sBox[j]) % 256; result[m] = (byte)(data[m] ^ sBox[t]); } return result; } }
关键注意事项
- 编码一致性:必须全程使用
Encoding.ASCII,原VB代码调用的是advapi32的ANSI版本API,所有字符串均以ASCII编码处理,使用UTF-8会导致密钥不匹配。 - 性能优化:RC4实现为纯内存操作,性能优异,适合频繁调用场景;MD5实例每次创建是安全的(线程不安全,避免共享实例),若需极致性能可使用
ThreadLocal<MD5>缓存实例。 - 密钥容器无关性:原VB代码中的
strKEY_CONTAINER参数在该逻辑中无实际作用,因为密钥是从密码哈希派生,而非从密钥容器读取,可忽略。
内容的提问来源于stack exchange,提问作者Rob Chelton
相关产品推荐
相关产品推荐

