【问题标题】:CryptographicException intermittently occurs when encrypting/decrypting with RSA使用 RSA 加密/解密时间歇性发生 CryptographicException
【发布时间】:2011-03-10 07:46:08
【问题描述】:

我正在尝试在 C# 中使用 RSA 加密和解密数据。我有以下 MSTest 单元测试:

const string rawPassword = "mypass";

// Encrypt
string publicKey, privateKey;
string encryptedPassword = RSAUtils.Encrypt(rawPassword, out publicKey, out privateKey);
Assert.AreNotEqual(rawPassword, encryptedPassword,
    "Raw password and encrypted password should not be equal");

// Decrypt
string decryptedPassword = RSAUtils.Decrypt(encryptedPassword, privateKey);
Assert.AreEqual(rawPassword, decryptedPassword,
    "Did not get expected decrypted password");

它在解密过程中失败,但只是有时。似乎每当我设置断点并逐步完成测试时,它都会通过。这让我觉得也许有些事情没有及时完成以使解密成功发生,并且我在调试时放慢了逐步完成它的速度,这给了它足够的时间来完成。当它失败时,它似乎失败的行是decryptedBytes = rsa.Decrypt(bytesToDecrypt, false);,方法如下:

public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
    if (string.IsNullOrEmpty(textToDecrypt))
    {
        throw new ArgumentException(
            "Cannot decrypt null or blank string"
        );
    }
    if (string.IsNullOrEmpty(privateKeyXml))
    {
        throw new ArgumentException("Invalid private key XML given");
    }
    byte[] bytesToDecrypt = ByteConverter.GetBytes(textToDecrypt);
    byte[] decryptedBytes;
    using (var rsa = new RSACryptoServiceProvider())
    {
        rsa.FromXmlString(privateKeyXml);
        decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
    }
    return ByteConverter.GetString(decryptedBytes);
}

此异常失败:

System.Security.Cryptography.CryptographicException:错误数据

我的Encrypt方法如下:

public static string Encrypt(string textToEncrypt, out string publicKey,
    out string privateKey)
{
    byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
    byte[] encryptedBytes;
    using (var rsa = new RSACryptoServiceProvider())
    {
        encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
        publicKey = rsa.ToXmlString(false);
        privateKey = rsa.ToXmlString(true);
    }
    return ByteConverter.GetString(encryptedBytes);
}

通篇使用的ByteConverter 如下:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();

我在 StackOverflow 上看到了一些关于使用 .NET 进行 RSA 加密和解密的问题。 This one 是由于使用私钥加密并尝试使用公钥解密,但我认为我没有这样做。 This question 和我有同样的例外,但选择的答案是使用 OpenSSL.NET,我不想这样做。

我做错了什么?

【问题讨论】:

    标签: c# security encryption rsa rsacryptoserviceprovider


    【解决方案1】:

    您能否将ByteConverter.GetBytes 替换为Convert.FromBase64String 并将ByteConverter.GetString 替换为Convert.ToBase64String,看看是否有帮助。 Bad Data 异常通常意味着您的数据中有无效字符,或者长度不是解密的正确长度。我认为使用 Convert 函数可能会解决您的问题。

      public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
    
      public static string Encrypt(string textToEncrypt, out string publicKey,
        out string privateKey)
      {
         byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
         byte[] encryptedBytes;
         using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
         {
            encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
            publicKey = rsa.ToXmlString(false);
            privateKey = rsa.ToXmlString(true);
         }
         return Convert.ToBase64String(encryptedBytes);
      }
    
      public static string Decrypt(string textToDecrypt, string privateKeyXml)
      {
         if (string.IsNullOrEmpty(textToDecrypt))
         {
            throw new ArgumentException(
                "Cannot decrypt null or blank string"
            );
         }
         if (string.IsNullOrEmpty(privateKeyXml))
         {
            throw new ArgumentException("Invalid private key XML given");
         }
         byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
         byte[] decryptedBytes;
         using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
         {
            rsa.FromXmlString(privateKeyXml);
            decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
         }
         return ByteConverter.GetString(decryptedBytes);
      }
    

    【讨论】:

    • 嗯,尝试这给了我一个不同的例外:“System.FormatException:Base-64 字符数组的长度无效”。这发生在Encrypt 的第一行:byte[] bytesToEncrypt = Convert.FromBase64String(textToEncrypt);
    • @Sarah - 好的,我更新了你的例子。我测试了它,它看起来很有效。
    • 行得通!谢谢。我从没想过会使用Convert/UnicodeEncoding 的组合。
    【解决方案2】:

    您的问题在于从字节到字符串的转换。并非所有字节序列都是有效的 UTF-16 编码,并且您使用的 UnicodeEncoding 会默默地忽略无效字节。如果你用过

    public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true);
    

    相反,您的代码在尝试转换字节而不是用0xFFFD 静默替换无效字节对时会失败。

    测试在调试时工作的事实是巧合。您使用的是随机 RSA 密钥对,因此有时您会得到一个有效的 UTF-16 编码的加密。

    正如 SwDevMan81 建议的那样,修复方法是使用可以转换所有可能的字节数组的编码。 F.x. Base64 编码。

    【讨论】:

      【解决方案3】:

      我会推荐使用这个类,可惜我不记得原作者了..

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Security.Cryptography;
      
      namespace Encryption
      {
      
      class AsymmetricED
      {
          private static RSAParameters param = new RSAParameters();
          /// <summary>
          /// Get Parameters
          /// </summary>
          /// <param name="pp">Export private parameters?</param>
          /// <returns></returns>
          public static RSAParameters GenerateKeys(bool pp)
          {
              RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
              if (param.Equals(new RSAParameters()))
              {
                  param = RSA.ExportParameters(true);
              }
              RSA.ImportParameters(param);
              return RSA.ExportParameters(pp);
          }
          static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
          {
              try
              {
                  //Create a new instance of RSACryptoServiceProvider.
                  RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
      
                  //Import the RSA Key information. This only needs
                  //toinclude the public key information.
                  RSA.ImportParameters(RSAKeyInfo);
      
                  //Encrypt the passed byte array and specify OAEP padding.  
                  //OAEP padding is only available on Microsoft Windows XP or
                  //later.  
                  return RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
              }
              //Catch and display a CryptographicException  
              //to the console.
              catch (CryptographicException e)
              {
                  Console.WriteLine(e.Message);
      
                  return null;
              }
      
          }
      
          static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
          {
              try
              {
                  //Create a new instance of RSACryptoServiceProvider.
                  RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
      
                  //Import the RSA Key information. This needs
                  //to include the private key information.
                  RSA.ImportParameters(RSAKeyInfo);
      
                  //Decrypt the passed byte array and specify OAEP padding.  
                  //OAEP padding is only available on Microsoft Windows XP or
                  //later.  
                  return RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
              }
              //Catch and display a CryptographicException  
              //to the console.
              catch (CryptographicException e)
              {
                  ConsoleColor col = Console.BackgroundColor;
                  Console.BackgroundColor = ConsoleColor.Red;
                  Console.WriteLine(e.ToString());
                  Console.BackgroundColor = col;
                  return null;
              }
      
          }
      }
      }
      

      用作:

      Encryption.AsymmetricED.RSAEncrypt(Data, GenerateKeys(false), false);
      
      Encryption.AsymmetricED.RSADecrypt(Data, GenerateKeys(true), false);
      

      编辑: 我还建议您不要将其用于大数据加密。通常您会使用对称算法(AES 等)加密实际数据,然后使用 RSA 算法加密对称密钥(随机生成),然后发送 rsa 加密的对称密钥和对称密钥数据。 您还应该查看 RSA 签名,以确保数据来自它所说的位置..

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-17
        • 2012-03-13
        • 2015-03-16
        • 1970-01-01
        • 2011-07-18
        • 2017-05-24
        相关资源
        最近更新 更多