首页 > 解决方案 > 需要在我的 c# web api 中修复错误的加密方法

问题描述

我需要更正用 C# 编写的加密方法。

首先,一点背景知识:我负责一个现有的 Web 应用程序,它有一个 ecma6 / html 前端和 c# web api .net 标准 4.6 后端。

它与不同的客户进行了许多集成,以进行用户识别。一些集成只需导航到客户 URL 以在客户的基础架构上执行登录过程,然后返回应用程序,并在 URL 的查询字符串中使用加密的用户令牌。

此令牌使用 AES256 加密进行加密。

后端正确解密了令牌,但是当我尝试使用加密例程构建单元测试时,我发现出了点问题。当我加密然后解密消息时,解密例程会引发以下错误:

Unhandled Exception:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.

输入消息是“key1=value1;key2=value2”(不带引号)

我得到的加密消息是 NzcrOTc3Kzk3Nys5NzcrOTc3Kzk3Nys5NzcrOVpsVHZ2NzF3NzcrOUZ6UVlRZ3Z2djcxSVlPKy92U0V6NzcrOVNqZFY3Nys5VHpBZA==

我需要更正加密方法中的实现错误。解密方法的实现显示了预期的行为,您会注意到对加密字符串进行了双重 Base64 解码:这是给定的,因为我们与客户在 PERL 中完成的已经开发的加密例程集成在一起,我们检测到该例程加倍编码。

我检查了操作顺序,发现加密和解密不匹配,我无法检测到不一致,所以需要寻求帮助。

我为此测试合成的代码是:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

class MainClass {
  public static void Main (string[] args) {
    var secretKey = "This is my secret key";
    var secretIV = "This is my secret iv";
    var originalMessage = "key1=value1;key2=value2";
    var userToken = Cryptography.EncryptAES256CBCBase64x2(originalMessage, secretKey, secretIV);
    Console.WriteLine(userToken);
    var unencryptedToken = Cryptography.DecryptAES256CBCBase64x2(userToken, secretKey, secretIV);
    if (originalMessage == unencryptedToken)
      Console.WriteLine("All fine!");
    else
      Console.WriteLine("Error!");
  }
}

public static class Cryptography
{
  public static string DecryptAES256CBCBase64x2(string base64EncryptedString, string secretKey, string secretIV)
  {
      base64EncryptedString = SaveBase64String(base64EncryptedString);
      var keyBytes = Encoding.UTF8.GetBytes(secretKey);
      var ivBytes = Encoding.UTF8.GetBytes(secretIV);
      var hash = SHA256.Create();
      var keyHash = hash.ComputeHash(keyBytes);
      Array.Resize(ref keyHash, 32);
      var keyHashString = string.Empty;
      foreach (byte x in keyHash)
        keyHashString += string.Format("{0:x2}", x);
      keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
      var ivHash = hash.ComputeHash(ivBytes);
      Array.Resize(ref ivHash, 16);
      var ivHashString = string.Empty;
      foreach (byte x in ivHash)
        ivHashString += string.Format("{0:x2}", x);
      ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
      // Create an RijndaelManaged object
      // with the specified key and IV.
      using (var rijAlg = new RijndaelManaged())
      {
        rijAlg.Padding = PaddingMode.PKCS7;
        rijAlg.Mode = CipherMode.CBC;
        rijAlg.Key = keyHash;
        rijAlg.IV = ivHash;
        var encryptedBytes =
          Convert.FromBase64String(
          Encoding.UTF8.GetString(
          Convert.FromBase64String(base64EncryptedString)));
        // Create a decryptor to perform the stream transform.
        var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
        // Create the streams used for decryption.
        using (var msDecrypt = new MemoryStream(encryptedBytes))
        {
            using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
              using (var srDecrypt = new StreamReader(csDecrypt))
              {
                  // Read the decrypted bytes from the decrypting stream
                  // and place them in a string.
                  return srDecrypt.ReadToEnd();
              }
            }
        }
      }
  }

  public static string EncryptAES256CBCBase64x2(string baseString, string secretKey, string secretIV)
  {
      var keyBytes = Encoding.UTF8.GetBytes(secretKey);
      var ivBytes = Encoding.UTF8.GetBytes(secretIV);
      var hash = SHA256.Create();
      var keyHash = hash.ComputeHash(keyBytes);
      Array.Resize(ref keyHash, 32);
      var keyHashString = string.Empty;
      foreach (byte x in keyHash)
        keyHashString += string.Format("{0:x2}", x);
      keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
      var ivHash = hash.ComputeHash(ivBytes);
      Array.Resize(ref ivHash, 16);
      var ivHashString = string.Empty;
      foreach (byte x in ivHash)
        ivHashString += string.Format("{0:x2}", x);
      ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
      // Create an RijndaelManaged object
      // with the specified key and IV.
      using (var rijAlg = new RijndaelManaged())
      {
        rijAlg.Padding = PaddingMode.PKCS7;
        rijAlg.Mode = CipherMode.CBC;
        rijAlg.Key = keyHash;
        rijAlg.IV = ivHash;
        var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
        // Create a encryptor to perform the stream transform.
        var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
        // Create the streams used for encryption.
        using (var msEncrypt = new MemoryStream(encryptedBytes))
        {
            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Read))
            {
              using (var srEncrypt = new StreamReader(csEncrypt))
              {
                  // Read the encrypted bytes from the encrypting stream
                  // and place them in a string.
                  var result = srEncrypt.ReadToEnd();

                  return Convert.ToBase64String(
                  Encoding.UTF8.GetBytes(
                  Convert.ToBase64String(
                  Encoding.UTF8.GetBytes(result))));
              }
            }
        }
      }
  }
  public static string SaveBase64String(string data)
  {
      data = data.Replace("-", "+").Replace("_", "/");
      var mod = data.Length % 4;
      if (mod > 2)
        mod = 1;
      return data + string.Empty.PadRight(mod, '=');
  }
}

在以下链接中,您可以尝试在线示例:https ://repl.it/@ormasoftchile/Test-encrypt-decrypt

谢谢大家。

标签: c#encryption

解决方案


在当前代码中,密文存储在字符串 ( StreamReader.ReadToEnd) 中,这通常不起作用,因为数据因此而损坏。相反,密文应该存储在字节数组中,如果需要,可以进行 Base64 编码。

解决问题

  • 删除行:

    var encryptedBytes = Encoding.UTF8.GetBytes(baseString);  
    
  • 并将整个 MemoryStream 块替换为:

    using (var msEncrypt = new MemoryStream())
    {
        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
            {
                swEncrypt.Write(baseString);
            }
            var encryptedBytes = msEncrypt.ToArray();
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(Convert.ToBase64String(encryptedBytes)));
        }
    }
    

另一点是双重 Base64 编码/解码。这没有任何意义,而且只是多余的并且会降低性能。如果可能的话,这应该改变。


推荐阅读