我被赋予了一项任务,可以放弃我们产品的一个dll,并用纯C#替换它。旧的DLL是一个.NET 2.0托管C ++(C ++ \ CLI),它包含对Win32本机Crypto API的调用。新的DLL应该公开一个具有相同名称的新对象。方法,但应该用C#(.NET 4.0)编写。当然,新的DLL应该以与旧的DLL相同的方式加密(和解密) - 否则,所有保存的加密密码都会在数据库或文件中的持久存储中被解析!
这是本机(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 - 如果我错了,请纠正我。)
谢谢!
欧米
答案 0 :(得分:6)
根据CryptDeriveKey的MSDN页面,3DES的默认密码模式似乎不是EBC而是CBC - “当为对称分组密码生成密钥时,密钥默认设置为密码块初始化向量为零的链接(CBC)模式。这种密码模式为批量加密数据提供了一种很好的默认方法。要更改这些参数,请使用CryptSetKeyParam函数。 .Net TripleDES提供程序的默认模式也是CBC。尝试删除您将其设置为EBC的行,看看是否有帮助。
重要提示,您需要知道初始化向量才能成功解密。 CryptDeriveKey函数默认使用零IV,这意味着为了在纯C#代码中匹配,您需要确保使用零IV。
答案 1 :(得分:0)
只需发布正确的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'!!!!加密\解密结果现在相同=>方法是等价的: - )
欧米