【问题标题】:C# AES CFB compatibility with 3rd party C implementationC# AES CFB 与第 3 方 C 实现的兼容性
【发布时间】:2019-09-10 17:00:28
【问题描述】:

我有一个用于 C 的第 3 方 AES 库(来自 Lantronix)。我在 C# 的托管代码中封装了他们的 API,如下所示,它可以工作:

    [DllImport("cbx_enc.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    static extern unsafe void VC_blockEncrypt(char* iv, char* key, int length, char* text, int RDkeyLen);

    /// <summary>
    /// Managed Encrypt Wrapper     
    /// </summary>
    /// <param name="buffer">provides the plain text and receives the same length cipher text</param>
    static readonly string key = "abcdef0123456789";
    static readonly string iv = "0123456789ABCDEF";
    public static void Encrypt(ref byte[] buffer)
    {
        var keyPtr = Marshal.StringToHGlobalAnsi(key);
        var ivPtr = Marshal.StringToHGlobalAnsi(iv);

        byte[] temp = new byte[16];
        Marshal.Copy(ivPtr, temp, 0, 16);
        int index = 0; 
        for (int i = 0; i < buffer.Length; i++)
        {
            if (index == 0)
            {
                Marshal.Copy(temp, 0, ivPtr, 16);
                unsafe
                {
                    VC_blockEncrypt((char*) ivPtr, (char*) keyPtr, 0, (char*) ivPtr, 128);
                }
                Marshal.Copy(ivPtr, temp, 0, 16);
                index = 16;
            }

            temp[16 - index] ^= buffer[i];
            buffer[i] = temp[16 - index];
            index--;
        }

        Marshal.FreeHGlobal(ivPtr);
        Marshal.FreeHGlobal(keyPtr);
    }

现在,当我自己编写时,使用System.Security.Cryptography 完全避免使用他们的非托管DLL,我最终的密文似乎与他们不同!我使用相同的模式、相同的密钥、相同的 iv 和相同的纯文本,但算法不兼容。下图是RijndaelManaged对象的属性设置和代码;我错过了导致这种不兼容的东西吗?

    /// <summary>
    /// Managed Encrypt     
    /// </summary>
    /// <param name="buffer">provides the plain text and receives the same length cipher text</param>
    static readonly string key = "abcdef0123456789";
    static readonly string iv = "0123456789ABCDEF";
    public static void Encrypt(ref byte[] buffer)
    {
        using (RijndaelManaged cipher = new RijndaelManaged())
        {
            cipher.Mode = CipherMode.CFB;
            cipher.Key = Encoding.ASCII.GetBytes(key);
            cipher.IV = Encoding.ASCII.GetBytes(iv);
            cipher.Padding = PaddingMode.None;
            cipher.FeedbackSize = 128;

            ICryptoTransform encryptor = cipher.CreateEncryptor(cipher.Key, cipher.IV);
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
                    {
                        swEncrypt.Write(buffer);
                    }
                    buffer = msEncrypt.ToArray();
                }
            }
        }
    }

另外,我从 Lantronix 架构中阐明的算法看起来非常简单 - API 进行加密,并在调用方法中将输出与纯文本进行异或。然而,使用 .NET 库,我无法访问中间加密输出(或者有吗?),因此我可以像 Lantronix 在加密后手动执行的方式一样异或...

最终目标是停止使用非托管代码,但应该能够使用完全托管的 .NET 代码生成相同的密文。

提前感谢您的帮助。

附言如果您需要,我可以提供第 3 方 C 库 cbx_enc.dll。

编辑:@Topaco,这里有一些根据要求提供的示例数据。还没有收到供应商关于分发 DLL 的消息;正在努力……

CFB 的公共输入:

byte[] buffer = Encoding.ASCII.GetBytes("AAAAAAAAAAAAAABBBBBBBBBBBBBBBBBD"); //plain text string key = "abcdef0123456789"; string iv = "0123456789ABCDEF";

从包装器到非托管 DLL 的 I/O:

纯文本十六进制:4141414141414141414141414141424242424242424242424242424242424244 密文十六进制:C9094F820428E07AE035B6749E18546C62F9D5FD4A78480215DA3625D376A271

来自带有FeedbackSize = 128; //CFB128的托管代码的I/O:

纯文本十六进制:4141414141414141414141414141424242424242424242424242424242424244 密文十六进制:6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83

来自带有FeedbackSize = 8 //CFB8的托管代码的I/O:

纯文本十六进制:4141414141414141414141414141424242424242424242424242424242424244 密文十六进制:6ACA3B1159D38568504248CDFF159C87BB2D3850EDAEAD89493BD91087ED7507

我还使用 ECB 进行了额外的测试,以查看他们的 API 是否像 ECB 一样(因此需要外部 XORing)。因此,我将 IV 作为纯文本传递给我的 ECB 代码,如下所示,并将其与第一次 XOR 之前的输出进行比较——它们也不匹配!

将 IV 作为明文传递给 ECB:30313233343536373839414243444546 密文十六进制:2B5B11C9ED9B111A065861D29C478FDA

来自非托管 DLL 的 CipherText Hex,在第一个 XOR 之前:88480EC34569A13BA174F735DF59162E

最后,这是我对上述测试的 ECB 实现:

   static readonly string key = "abcdef0123456789";
    static readonly string iv = "0123456789ABCDEF";
    public static void Encrypt(ref byte[] buffer)
    {
        buffer = Encoding.ASCII.GetBytes(iv);
        Console.WriteLine($"PlainText:  {HexHelper.ToHexString(buffer)}");

        var aes = new AesManaged
        {
            KeySize = 128,
            Key = Encoding.ASCII.GetBytes(key),
            BlockSize = 128,
            Mode = CipherMode.ECB,
            Padding = PaddingMode.None,
            IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
        };

        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        buffer = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);

        Console.WriteLine($"CipherText: {HexHelper.ToHexString(buffer)}");
    } 

谢谢。

【问题讨论】:

  • 我不确定你的情况是怎么回事,但需要注意的一点是 CFB 模式有一个额外的参数以及 key 和 iv。我认为这可能是FeedbackSize 所指的。尝试将其设置为 8 或 1,看看结果是否与库匹配。
  • 这里有一个例子会很有帮助,因为由于缺少.dll,上层代码无法执行。因此,您应该为两个代码中的每一个加密一个明文并发布明文、两个密文(十六进制或 Base64 编码)以及使用的密钥和 IV(以及与已发布数据不同的所有参数,例如 FeedbackSize、Padding ETC。)。事实上,提供 .dll 是最有意义的,但前提是您被允许这样做。
  • 我根据您的样本数据尝试了一些猜测,但这里没有发现任何实质性内容。 cbx_enc.dll 的描述可以在网上找到。在第 22 页上描述了 VC_BlockEncrypt,在第 11 页上有一个与您的实现相对应的示例。我同意逻辑是 CFB128。参数在第10页有描述。值得注意的是,密钥应该作为十六进制字符串传递,即一个16字节的字符串用32个字符表示,所以字符串abcdef0123456789
  • 你使用的不应该只是简单的ASCII编码,而是应该用十六进制字符串61626364656630313233343536373839代替非托管dll。代码中似乎没有考虑到这一点,因此使用了不同的密钥。与密钥不同,IV 和明文缓冲区似乎是传统的字节数组。因此,我会给与适当键的比较另一个机会。还有一点是VC_BlockEncrypt 的未知实现。在这里,您可以检查它在原始 AES-128(带有一个块的 ECB)的情况下是否正常运行。
  • 如果明文缓冲区由一个块(16 字节)组成,只有0,那么结果应该对应于 IV 的 AES-128 加密。您已经在上一个示例中尝试过类似的操作,但可能使用了错误的键(见上文)。 VC_BlockEncrypt 的源代码也很有帮助,您可能可以访问它并且可能会给您带来新的见解。

标签: c# cryptography aes dllimport rijndaelmanaged


【解决方案1】:

感谢大家的帮助,特别是感谢@Topaco 非常感谢您在根据文档以十六进制编码纯文本方面的洞察力帮助! 这是修改后的包装代码;如您所见,它的密码现在与托管代码的密码匹配!完美!!

    static readonly string key = "61626364656630313233343536373839"; //"abcdef0123456789";
    static readonly string iv = "0123456789ABCDEF";
    public static void Encrypt(ref byte[] buffer)
    {
        Console.WriteLine($"PlainText: {HexHelper.ToHexString(buffer)}");

        var keyPtr = Marshal.StringToHGlobalAnsi(key);
        var ivPtr = Marshal.StringToHGlobalAnsi(iv);
        byte[] temp = new byte[16];
        Marshal.Copy(ivPtr, temp, 0, 16);
        int index = 0; 
        for (int i = 0; i < buffer.Length; i++)
        {
            if (index == 0)
            {
                Marshal.Copy(temp, 0, ivPtr, 16);
                unsafe
                {
                    VC_blockEncrypt((char*) ivPtr, (char*) keyPtr, 0, (char*) ivPtr, 128);
                }
                Marshal.Copy(ivPtr, temp, 0, 16);
                index = 16;
                Console.WriteLine($"CipherText BeforeXOR: {HexHelper.ToHexString(temp)}");
            }

            temp[16 - index] ^= buffer[i];
            buffer[i] = temp[16 - index];
            index--;
        }

        Marshal.FreeHGlobal(ivPtr);
        Marshal.FreeHGlobal(keyPtr);

        Console.WriteLine($"CipherText: {HexHelper.ToHexString(buffer)}");

    }

来自修改后的包装代码的 I/O: 纯文本:4141414141414141414141414141424242424242424242424242424242424244 密文:6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83

来自托管代码的 I/O: 纯文本:4141414141414141414141414141424242424242424242424242424242424244 密文:6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83

干杯!!

【讨论】:

    猜你喜欢
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多