.net - 使用 System.Security.Cryptography 将 Win32 Crypto API 调用转换为 C#

标签 .net windows cryptography cryptoapi

我接到一项任务,要删除我们产品的一个 dll 并将其替换为纯 C# 的 dll。旧的 DLL 是一个 .NET 2.0 Managed C++ (C++\CLI),它包装了对 Win32 native Crypto API 的调用。新的 DLL 应公开一个具有相同名称和方法的新对象,但应使用 C# (.NET 4.0) 编写。当然,新 DLL 应该以与旧 DLL 相同的方式加密(和解密)- 否则,所有保存在永久存储(如数据库或文件)中的加密密码都不会被解析!

这是 native (Win32) API 调用的(伪)代码(注意输入始终采用 Unicode 编码):

//buffer_to_encrypt - Is the input to the following procedure and is the buffer
// to be encrypted using 3DES and the below password to generate a valid 3DES key
// The buffer is Unicode encoded!!!


HCRYPTPROV m_provider = NULL;
HCRYPTHASH m_hash = NULL;
HCRYPTKEY m_key = NULL;

static const unsigned char password[] = { 
                                                0xF1, 0x49, 0x4C, 0xD0, 0xC1, 
                                                0xE2, 0x1A, 0xEA, 0xFB, 0x34, 
                                                0x25, 0x5A, 0x63, 0xA5, 0x29, 
                                                0x09, 0x8E, 0xB6, 0x7B, 0x75 
                                            }; //20 BYTES password


CryptAcquireContextW( &m_provider, NULL, NULL, PROV_DH_SCHANNEL, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT);

CryptCreateHash( m_provider, CALG_SHA1, NULL, 0, &m_hash );

CryptHashData( m_hash, password, (DWORD)20, 0 ); //password is a 20Bytes buffer 

CryptDeriveKey(m_provider, CALG_3DES, m_hash, CRYPT_EXPORTABLE, &m_key);

CryptEncrypt( m_key.handle(), NULL, TRUE, 0, buffer_to_encrypt, &dwFilled, (DWORD)total );

return buffer_to_encrypt;

现在,我尝试使用 C#(System.Security.Cryptography 命名空间)和 .NET API 公开的新 Crypto 对象编写相同的过程:

class Encryptor
{

         private static byte[] password = { 
                                            0xF1, 0x49, 0x4C, 0xD0, 0xC1, 
                                            0xE2, 0x1A, 0xEA, 0xFB, 0x34, 
                                            0x25, 0x5A, 0x63, 0xA5, 0x29, 
                                            0x09, 0x8E, 0xB6, 0x7B, 0x75 
                                        }; //20 BYTES password, same as the above native code




        private static byte[] EncryptInternal(string source)
        {
            byte[] resultArray = null;
            byte[] streamToEncrypt = Encoding.Unicode.GetBytes(source);

            using (TripleDESCryptoServiceProvider prov3des = new TripleDESCryptoServiceProvider())
            {

                prov3des.Mode = CipherMode.ECB;
                prov3des.Padding = PaddingMode.PKCS7;

                using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null)) //No slat needed here
                {
                    prov3des.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", prov3des.KeySize, ZeroIV);
                }

                ICryptoTransform cTransform = prov3des.CreateEncryptor();

                resultArray = cTransform.TransformFinalBlock(streamToEncrypt, 0, streamToEncrypt.Length);

            }

            return resultArray;
        }
}

我遇到了一个恼人的问题 - 使用这两种方法时加密数组(结果加密缓冲区)并不相同!每个数组的前 8 个字节(64 位)相同,但接下来的字节不同。这会导致短字符串(最多 3 个字符)使用两种方法进行相同的加密,但较长的字符串会导致不同的加密数据。

如何强制这两种方法等效?那就是-以相同的方式加密和解密以使输出相同? 我在这里错过了什么? .NET 和 native (Win32) API 之间的默认值\行为是否有变化? (我认为 Win32 Crypto API 中的默认 3DES 密码模式是 EBC,而使用 C# 的默认模式是 CBC - 如果我错了请纠正我)。

谢谢!

暗利

最佳答案

根据 CryptDeriveKey 的 MSDN 页面似乎 3DES 的默认密码模式不是 EBC 而是 CBC -“当为对称 block 密码生成 key 时,默认情况下 key 设置为密码 block 链接 (CBC) 模式,初始化向量为零。此密码mode 为批量加密数据提供了一个很好的默认方法。要更改这些参数,请使用 CryptSetKeyParam 函数。” .Net TripleDES 提供程序的默认模式也是 CBC。尝试删除将其设置为 EBC 的行,看看是否有帮助。

重要说明,您需要知道初始化向量才能成功解密。默认情况下,CryptDeriveKey 函数将使用零 IV,这意味着为了在您的纯 C# 代码中进行匹配,您需要确保您也使用零 IV。

关于.net - 使用 System.Security.Cryptography 将 Win32 Crypto API 调用转换为 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6253363/

相关文章:

c# - 在 TeamCity 中运行 nUnit 测试时出现错误的 CurrentCulture

c# - 您可以通过 TcpClient 发送大于 SendBufferSize 的文件吗?

windows - WP CLI Shell 不适用于 Windows

c# - 使用带有私有(private) PEM 文件的 BouncyCaSTLe 的 RSA 解密不起作用

java - 带 Bouncy CaSTLe 的库 JAR

.net - Ansicon 和 cucumber : 'Ruby interpreter (CUI) 1.9.3p194 [1386-mingw] has stopped working'

.net - 查找重复模式的最佳算法

c++ - 如何以编程方式确定网卡是虚拟网卡?

windows - 如何将 Hudson/Jenkins 参数传递给 Windows 批处理命令

c++ - 使用公钥 X509 V3 (PKCS7) 使用 AES 128 模式 cbc 加密文件