首页 > 解决方案 > 如何创建 SHA-2 和 AES 加密字符串以在 .NET 的 HTTP 请求标头中使用?

问题描述

我正在尝试编写通过网络从外部 API 请求信息的客户端代码。

除了如何生成授权密钥的规定外,API 很简单(对我来说)。首先是一些上下文:开始需要 6 个字符串值:

现在是加密的东西。首先是 SHA2。

hashedString = SHA2(token + password + devId)

紧随其后的是 AES。

authKey = AES(salt + orgId + "=" + hashedString)

AES 参数指定如下:

我的问题是我对密码学几乎一无所知。

下面是我试图完成上述操作的代码。

// Generate Authorisation key
            byte[] fieldsBytes = Encoding.ASCII.GetBytes(token + password + devId);
            byte[] keyBytes = Encoding.ASCII.GetBytes(secretKey);
            SHA512 shaM = new SHA512Managed();
            string hashedFields = Encoding.ASCII.GetString(shaM.ComputeHash(fieldsBytes));
            byte[] encryptedBytes = EncryptStringToBytes_Aes(salt + orgId + "=" + hashedfields,
                keyBytes, keyBytes);
            string encryptedString = Encoding.ASCII.GetString(encryptedBytes);


private byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;
                aesAlg.Padding = PaddingMode.PKCS7;
                aesAlg.Mode = CipherMode.ECB;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor();

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }

此代码从外部服务获得“401”。

我的第一个问题是似乎没有名为SHA2的 NET 方法。我能找到的最接近的是 SHA512,我不确定 SHA512 是否是 SHA2 的 .NET 实现。

其次,AES 的填充已被指定为 PKCS5Padding 但我能找到的最接近(命名方式)的是PKCS7,我不确定它与PKCS5有多么相似。

还有一个初始化向量 (IV)的问题,AES 参数没有指定,但我看到 C# AES 示例包括。在代码中,出于绝望,我已将其设置为与 Key 具有相同的值(我相信这是 API 所称的“秘密密钥”),但我尝试在不将 IV 设置为任何值的情况下发出请求,但仍然得到回一个401。

我可能还应该提到我正在使用ASCII编码来转换字节,因为我第一次尝试使用UTF8,但是当实际发出 HTTP 请求时,我收到一个异常,说头值(记住我们正在生成HTTP 请求标头中的授权密钥)只能以 ASCII 编码。

任何为我指明正确方向的帮助都将不胜感激,因为我对这些密码学的东西感到非常遗憾。

标签: c#.netaessha

解决方案


别担心,加密货币会让人感觉非常复杂。我想你很接近。

  1. SHA2是一系列哈希函数。在实践中,“SHA2”通常表示 SHA2-256 或偶尔表示 SHA2-512。我的猜测是您的外部 API 可能使用的是更常见的 SHA2-256。

  2. crypto.stackexchange 上的这个答案解释说 PKCS#5 本质上是 PKCS#7 的一个子集。我敢打赌,您调用的 API 犯了与该答案中描述的相同的错误,并且应该真正调用它PKCS7Padding。你的代码很好!

  3. IV 与密钥(或只是 AES 的“密钥”)不同。对于每次加密运行,IV应该是随机的。您不应该从输入明文或输入密钥中派生它。幸运的是,AesCryptoServiceProvider.GenerateIV()会为您生成一个。不过,您可以将添加到您的输出流中。

  4. 用于Encoding.ASCII.GetBytes()获取明文和密钥字节对我来说很有意义。我不认为这会造成问题。

从这个对类似问题的出色答案中窃取(去给他们投票!),我会尝试这样的代码:

static byte[] AesEncrypt(byte[] data, byte[] key)
{
    if (data == null || data.Length <= 0)
    {
        throw new ArgumentNullException($"{nameof(data)} cannot be empty");
    }

    if (key == null || key.Length != AesKeySize)
    {
        throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
    }

    using (var aes = new AesCryptoServiceProvider
    {
        Key = key,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7
    })
    {
        aes.GenerateIV();
        var iv = aes.IV;
        using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
        using (var cipherStream = new MemoryStream())
        {
            using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
            using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
            {
                // prepend IV to data
                cipherStream.Write(iv);
                tBinaryWriter.Write(data);
                tCryptoStream.FlushFinalBlock();
            }
            var cipherBytes = cipherStream.ToArray();

            return cipherBytes;
        }
    }
}

除非这个 API 发生了其他奇怪的事情,否则我猜可能是上面的 #3 导致您的请求失败。


推荐阅读