我正在尝试使用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上看到了一些关于RSA加密和.NET解密的问题。 This one是由于使用私钥加密并尝试使用公钥解密,但我不认为我这样做。 This question与我有相同的例外,但选择的答案是使用OpenSSL.NET,我不愿意这样做。
我做错了什么?
答案 0 :(得分:8)
您可以将ByteConverter.GetBytes
替换为Convert.FromBase64String
并将ByteConverter.GetString
替换为Convert.ToBase64String
,看看是否有帮助。 Bad Data
异常通常表示数据中包含无效字符或者长度不是正确的解密长度。我认为使用转换功能可能会解决您的问题。
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);
}
答案 1 :(得分:3)
您的问题是从字节到字符串的转换。并非所有字节序列都是有效的UTF-16编码,并且您正在使用无提示忽略无效字节的UnicodeEncoding。如果您使用
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true);
相反,尝试转换字节时代码会失败,而不是用0xFFFD静默替换无效字节对。
在调试时测试工作的事实是巧合。您正在使用随机RSA密钥对,因此有时您将获得有效的UTF-16编码加密。
正如SwDevMan81建议的那样,修复程序使用可以转换所有可能的字节数组的编码。 F.X.编码的base64。
答案 2 :(得分:1)
我建议使用这个课,遗憾的是我不记得原作者了..
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签名,以确保数据来自它所说的位置。