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

我被赋予了一个任务,放弃我们的产品的DLL之一,并用一个纯粹的C#replace它。 旧的DLL是.NET 2.0托pipeC ++(C ++ \ CLI),它将对Win32本地Crypto API的调用进行封装。 新的DLL应该公开一个具有相同名称和方法的新对象,但应该用C#(.NET 4.0)编写。 当然,新的DLL应该像旧的一样encryption(和解密),否则,所有保存在一个持久性存储器,如数据库或文件中的encryption密码将不会被解决!

这是本机(Win32)API调用的(伪)代码(注意input总是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; } } 

在这里,我正面临一个烦人的问题 – encryption数组(结果encryption的缓冲区)是不一样的使用两种方法! 每个arrays的前8个字节(64位)是相同的,但接下来的字节不是。 这会导致使用两种方法对短string(最多3个字符)进行相同的encryption,但更长的string会导致不同的encryption数据。

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

谢谢!

奥马里

Solutions Collecting From Web of "使用System.Security.Cryptography将Win32 Crypto API调用转换为C#"

根据CryptDeriveKey的MSDN页面,似乎3DES的默认密码模式不是EBC,而是CBC – “当为对称分组密码生成密钥时,默认密钥在密码块链接(CBC)模式下设置,初始化向量为零,这种加密模式为批量加密数据提供了一个很好的默认方法,要改变这些参数,使用CryptSetKeyParam函数。 .Net TripleDES提供程序的默认模式也是CBC。 尝试删除您设置到EBC的行,看看是否有帮助。

这里的重要注意事项,您将需要知道初始化向量能够成功解密。 CryptDeriveKey函数将默认使用零IV,这意味着为了在纯粹的C#代码中有一个匹配,您需要确保您也使用零IV。

只需发布正确的C#代码以备将来参考,请注意设置IV&CBC模式的更改:

 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.CBC; prov3des.Padding = PaddingMode.PKCS7; prov3des.IV = new byte[]{0,0,0,0,0,0,0,0}; //8 bytes, zero-ed using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null)) //No slat needed here { prov3des.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", prov3des.KeySize, prov3des.IV); } ICryptoTransform cTransform = prov3des.CreateEncryptor(); resultArray = cTransform.TransformFinalBlock(streamToEncrypt, 0, streamToEncrypt.Length); } return resultArray; } 

}

非常感谢“pstrjds”! 加密\解密结果现在是相同的=>方法是相同的:-)

奥马里