Я пытаюсь зашифровать и дешифровать данные с помощью RSA в C#. У меня есть следующий модульный тест 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);
}
Это перестало работать за этим исключением:
Система. Безопасность. Криптография. 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. Этот происходил из-за шифрования с закрытым ключом и попытки дешифровать с открытым ключом, но я не думаю, что делаю это. Этот вопрос имеет то же исключение как я, но выбранный ответ должен был использовать OpenSSL.NET, который я предпочту не делать.
Что я делаю неправильно?
Не могли бы вы заменить ByteConverter.GetBytes
на Convert.FromBase64String
и заменить ByteConverter.GetString
на Convert.ToBase64String
и увидеть помогает. Исключение неверных данных
обычно означает, что у вас есть недопустимый символ в данных или что длина не является правильной длиной для дешифрования. Я думаю, что использование функций 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);
}
Я бы порекомендовал использовать этот класс, но, к сожалению, я не помню первоначального автора.
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);
EDIT : Я также рекомендую не использовать это для шифрования больших данных. Обычно вы шифруете фактические данные с помощью симметричного алгоритма (AES и т. Д.), Затем шифруете симметричный ключ (сгенерированный случайным образом) с помощью алгоритма RSA, затем отправляете зашифрованный симметричный ключ RSA и данные симметричного ключа. Вы также должны посмотреть на подпись RSA, чтобы убедиться, что данные поступают именно там, где они указаны.
Ваша проблема связана с преобразованием байтов в строку. Не все последовательности байтов являются допустимой кодировкой UTF-16, и вы используете UnicodeEncoding, который молча игнорирует недопустимые байты. Если бы вы использовали вместо этого
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true);
, ваш код не смог бы преобразовать байты вместо того, чтобы молча заменить недопустимые пары байтов на 0xFFFD .
Тот факт, что тест работал во время отладки, был совпадением. Вы используете случайную пару ключей RSA, поэтому иногда вы получаете шифрование, которое является допустимой кодировкой UTF-16.
Как предлагает SwDevMan81, исправление заключается в использовании кодировки, которая может преобразовывать все возможные байтовые массивы. F.x. Кодировка Base64.