使用 Chilkat 3DES 提供程序加密和使用标准 .NET 提供程序的相同结果

Encryption using Chilkat 3DES provider and the same result using standard .NET provider

这里有个问题。有知道库'Chilkat Crypt'。它包含 3des 加密方法。

 public static void ChilkatEncryption(String cc, string tdesKey, string tdesIV)
    {
        Crypt2 crypt = new Chilkat.Crypt2();

        bool success = crypt.UnlockComponent("Anything for 30-day trial");
        if (success != true)
        {
            Console.WriteLine(crypt.LastErrorText);
            return;
        }

        //  Specify 3DES for the encryption algorithm:
        crypt.CryptAlgorithm = "3des";

        //  CipherMode may be "ecb" or "cbc"
        crypt.CipherMode = "cbc";

        //  KeyLength must be 192.  3DES is technically 168-bits;
        //  the most-significant bit of each key byte is a parity bit,
        //  so we must indicate a KeyLength of 192, which includes
        //  the parity bits.
        crypt.KeyLength = 192;

        //  The padding scheme determines the contents of the bytes
        //  that are added to pad the result to a multiple of the
        //  encryption algorithm's block size.  3DES has a block
        //  size of 8 bytes, so encrypted output is always
        //  a multiple of 8.
        crypt.PaddingScheme = 0;

        //  EncodingMode specifies the encoding of the output for
        //  encryption, and the input for decryption.
        //  It may be "hex", "url", "base64", or "quoted-printable".
        crypt.EncodingMode = "hex";

        //  An initialization vector is required if using CBC or CFB modes.
        //  ECB mode does not use an IV.
        //  The length of the IV is equal to the algorithm's block size.
        //  It is NOT equal to the length of the key.
        string ivHex = tdesIV;
        crypt.SetEncodedIV(ivHex, "hex");

        //  The secret key must equal the size of the key.  For
        //  3DES, the key must be 24 bytes (i.e. 192-bits).
        string keyHex = tdesKey;
        crypt.SetEncodedKey(keyHex, "hex");

        //  Encrypt a string...
        //  The input string is 44 ANSI characters (i.e. 44 bytes), so
        //  the output should be 48 bytes (a multiple of 8).
        //  Because the output is a hex string, it should
        //  be 96 characters long (2 chars per byte).
        string encStr = crypt.EncryptStringENC(cc);
        Console.WriteLine(encStr);

        //  Now decrypt:
        string decStr = crypt.DecryptStringENC(encStr);
        Console.WriteLine(decStr);
    }

当我尝试在没有使用标准提供程序的第三方库的情况下执行相同操作时,结果却大不相同:

    private static string EncryptData(String cc, byte[] tdesKey, byte[] tdesIV)
    {
        //Create the file streams to handle the input and output files.
        MemoryStream fin = new MemoryStream();
        MemoryStream fout = new MemoryStream();
        StreamWriter sw = new StreamWriter(fin);
        sw.Write(cc);
        sw.Flush();
        fin.Position = 0;
        fout.SetLength(0);

        //Create variables to help with read and write.
        byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
        long rdlen = 0;              //This is the total number of bytes written.
        long totlen = fin.Length;    //This is the total length of the input file.
        int len;                     //This is the number of bytes to be written at a time.

        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Mode=CipherMode.CBC;
        tdes.Padding = PaddingMode.None;
        CryptoStream encStream = new CryptoStream(fout, tdes.CreateEncryptor(tdesKey, tdesIV), CryptoStreamMode.Write);

        Console.WriteLine("Encrypting...");

        //Read from the input file, then encrypt and write to the output file.
        while (rdlen < totlen)
        {
            len = fin.Read(bin, 0, 100);
            encStream.Write(bin, 0, len);
            rdlen = rdlen + len;
            Console.WriteLine("{0} bytes processed", rdlen);
        }

        byte[] encBytes = fout.ToArray();

        return BitConverter.ToString(encBytes);


    }

有谁知道,要获得相同的 3DES 结果,标准 .NET 加密的参数集应该是多少?

谢谢!

填充不正确

根据 Chilkat 文档 herePaddingScheme 值 0 表示库将使用 PKCS#5 填充。 PKCS#5 本质上只是 PKCS#7 的一个特例,它仅指定用于大小为 8 字节的块密码,例如 Triple DES。使用 .NET 提供程序,您应该指定 PaddingMode.PKCS7 而不是上面的 PaddingMode.None

此外,您需要确保明确关闭 CryptoStream,以便它知道您已完成写入,以便它可以加密最后的(填充的)块:

encStream.Close();
byte[] encBytes = fout.ToArray();

编码不正确

另一个可能会或可能不会给您带来问题的问题是,这两个不同的示例使用不同的文本编码。 Chilkat 库看起来默认使用 "ANSI" 编码。但是,在第二个示例中,您没有在 StreamWriter 构造函数中明确指定编码,因此它默认为 UTF-8。

根据您要加密的数据,这可能会给您带来问题,也可能不会给您带来问题,但基本上如果您有任何超出普通旧 ASCII 范围的字符,您将在这两个函数之间得到不一致的结果,因为您赢了实际上加密的不是同一个东西。

快速修复是在 StreamWriter 构造函数中指定编码:

StreamWriter sw = new StreamWriter(fin, Encoding.Default);

这将为您提供一个 StreamWriter,它将根据您系统的默认 ANSI 代码页从字符串中写入字节。这样做的最大问题是,无论 "ANSI" 在你的系统上意味着什么,都不一定与其他人的系统相同(有关详细解释,请参阅 this question),因此这可能会导致问题如果你需要互操作。

出于这个原因,我强烈建议您指定更具体的编码,例如 UTF-8。

对于 Chilkat 库,您可以这样做:

crypt.Charset = "utf-8";    

对于 .NET 提供程序示例,您可以在 StreamWriter 构造函数中明确指定编码:

StreamWriter sw = new StreamWriter(fin, Encoding.UTF8);

您也可以省略参数,因为 UTF-8 是 StreamWriter class.

使用的默认编码


顺便说一句,而不是使用 StreamWriter 到 encode/write 你的输入字符串到内存流 fin 开始然后读回写到 CryptoStream 100 个字节时间,您可以直接编码为字节数组并一次性写入 CryptoStream

var buffer = Encoding.UTF8.GetBytes(cc);
encStream.Write(buffer, 0, buffer.Length);