RSA Шифрование больших данных в C#
Это мой первый пост, так что надеюсь, я не пропустил ничего важного. Я делаю проект на C#, где мне нужно использовать шифрование с открытым / закрытым ключом, чтобы зашифровать сообщение, а затем отправить его через соединение SSL.
Я решил использовать RSACryptoService
Согласно документации, это была единственная асимметричная схема шифрования, используемая для шифрования данных. Проблема в том, что у меня много проблем с этим. (Я хотел сделать симметричное шифрование, но это не то, что мой учитель хочет, чтобы я делал, и, по его словам, должно быть просто определить размер блока, и тогда он должен сделать всю работу за вас.) Ну, пока что не повезло, и я попробовал несколько разных подходов, но теперь я возвращаюсь к основам и пытаюсь снова, это мой текущий код:
public string[] GenerateKeysToStrings(string uniqueIdentifier)
{
string[] keys;
using (var rsa = new RSACryptoServiceProvider(4096))
{
try
{
string privateKey = rsa.ToXmlString(true);
string publicKey = rsa.ToXmlString(false);
this.pki.StoreKey(publicKey, uniqueIdentifier);
keys = new string[2];
keys[0] = privateKey;
keys[1] = publicKey;
}
finally
{
//// Clear the RSA key container, deleting generated keys.
rsa.PersistKeyInCsp = false;
}
}
return keys;
}
Как вы можете видеть, я генерирую ключи и имитирую PKI, отправляя открытый ключ в простой класс, который его хранит, а затем закрытый ключ записывается в файл (обратите внимание, что у меня также есть другой метод, который делает то же самое, но вместо этого сохраняет его в массив, просто потому, что я хотел проверить и упростить вещи, как я получаю No such key exceptions
а иногда и криптографические исключения, когда я делаю это так, как показано в примере, поэтому я хотел упростить это, просто сохранив rsa.ToXmlString
строка, как строка в массиве, но не повезло.)
Теперь у меня есть метод шифрования и дешифрования следующим образом:
public string Encrypt(string keyString, string message)
{
string encryptedMessage;
using (var rsa = new RSACryptoServiceProvider())
{
try
{
//// Load the key from the specified path
var encryptKey = new XmlDocument();
encryptKey.Load(@"C:\Test\PrivateKeyInfo.xml");
rsa.FromXmlString(encryptKey.OuterXml);
//// Conver the string message to a byte array for encryption
//// var encoder = new UTF8Encoding();
ASCIIEncoding byteConverter = new ASCIIEncoding();
byte[] dataToEncrypt = byteConverter.GetBytes(message);
byte[] encryptedData = rsa.Encrypt(dataToEncrypt, false);
//// Convert the byte array back to a string message
encryptedMessage = byteConverter.GetString(encryptedData);
}
finally
{
//// Clear the RSA key container, deleting generated keys.
rsa.PersistKeyInCsp = false;
}
}
return encryptedMessage;
}
Дешифрирование:
public string Decrypt(string keyString, string message)
{
string decryptedText;
using (var rsa = new RSACryptoServiceProvider())
{
try
{
//// Loads the keyinfo into the rsa parameters from the keyfile
/*
var privateKey = new XmlDocument();
privateKey.Load(keyString);
*/
rsa.FromXmlString(keyString);
//// Convert the text from string to byte array for decryption
ASCIIEncoding byteConverter = new ASCIIEncoding();
var encryptedBytes = byteConverter.GetBytes(message);
//// Create an aux array to store all the encrypted bytes
byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, false);
decryptedText = byteConverter.GetString(decryptedBytes);
}
finally
{
//// Clear the RSA key container, deleting generated keys.
rsa.PersistKeyInCsp = false;
}
}
return decryptedText;
}
Я знаю, что это стена текста, но я надеюсь, что вы можете мне помочь, потому что я так долго бился головой о стену, что теперь это не смешно:)
Проблема в том, как мне шифровать сообщения RSA
(или любое другое шифрование с открытым / закрытым ключом)
Вот тестовый клиент:
public static void Main(string[] args)
{
PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
Cryptograph crypto = new Cryptograph();
string[] keys = crypto.GenerateKeysToStrings("simonlanghoff@gmail.com");
string plainText = "Hello play with me, please";
string publicKey = crypto.GetPublicKey("simonlanghoff@gmail.com");
string encryptedText = crypto.Encrypt(keys[0], plainText);
string decryptedText = crypto.Decrypt(keys[1], encryptedText);
}
Как я уже упоминал, строковые массивы существуют потому, что я хотел устранить ошибку синтаксического анализа в документах XML...
Когда я запускаю тестовый клиент, если я использую закрытый ключ для шифрования и открытый ключ для дешифрования, я получаю "Исключение ключа не существует", и если я делаю это наоборот, я получаю исключение для неверных данных.
Пожалуйста, помогите мне, ребята, если вы знаете какое-нибудь хорошее руководство или можете рассказать мне, как несколько проще реализовать шифрование с открытым / закрытым ключом для строковых сообщений, пожалуйста, помогите мне.
Я ценю любую помощь.
3 ответа
Это не то, как должно выполняться шифрование RSA.
RSA это все о математике. То, что вы шифруете, это число, поэтому оно должно быть конечной длины и соответствовать длине пары ключей RSA, которую вы используете. Дальнейшие ограничения по длине накладываются используемым дополнением (либо PKCS#1, либо OAEP).
Если вы хотите зашифровать большие данные с помощью RSA, вам нужно сделать это косвенно - то есть использовать симметричный ключ для шифрования больших данных и зашифровать этот ключ с помощью открытого ключа RSA.
Вы можете прочитать о реализации этого в моем блоге.
Хорошо, я наконец-то нашел решение проблемы, о которой говорил в своем первоначальном посте. Это то, что я не тщательно протестировал или что-то еще, но кое-что я понял из небольшого процесса проб и ошибок.
Вот текущий код у меня есть:
public static string Encrypt(string dataToEncrypt, RSAParameters publicKeyInfo)
{
//// Our bytearray to hold all of our data after the encryption
byte[] encryptedBytes = new byte[0];
using (var RSA = new RSACryptoServiceProvider())
{
try
{
//Create a new instance of RSACryptoServiceProvider.
UTF8Encoding encoder = new UTF8Encoding();
byte[] encryptThis = encoder.GetBytes(dataToEncrypt);
//// Importing the public key
RSA.ImportParameters(publicKeyInfo);
int blockSize = (RSA.KeySize / 8) - 32;
//// buffer to write byte sequence of the given block_size
byte[] buffer = new byte[blockSize];
byte[] encryptedBuffer = new byte[blockSize];
//// Initializing our encryptedBytes array to a suitable size, depending on the size of data to be encrypted
encryptedBytes = new byte[encryptThis.Length + blockSize - (encryptThis.Length % blockSize) + 32];
for (int i = 0; i < encryptThis.Length; i += blockSize)
{
//// If there is extra info to be parsed, but not enough to fill out a complete bytearray, fit array for last bit of data
if (2 * i > encryptThis.Length && ((encryptThis.Length - i) % blockSize != 0))
{
buffer = new byte[encryptThis.Length - i];
blockSize = encryptThis.Length - i;
}
//// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
if (encryptThis.Length < blockSize)
{
buffer = new byte[encryptThis.Length];
blockSize = encryptThis.Length;
}
//// encrypt the specified size of data, then add to final array.
Buffer.BlockCopy(encryptThis, i, buffer, 0, blockSize);
encryptedBuffer = RSA.Encrypt(buffer, false);
encryptedBuffer.CopyTo(encryptedBytes, i);
}
}
catch (CryptographicException e)
{
Console.Write(e);
}
finally
{
//// Clear the RSA key container, deleting generated keys.
RSA.PersistKeyInCsp = false;
}
}
//// Convert the byteArray using Base64 and returns as an encrypted string
return Convert.ToBase64String(encryptedBytes);
}
/// <summary>
/// Decrypt this message using this key
/// </summary>
/// <param name="dataToDecrypt">
/// The data To decrypt.
/// </param>
/// <param name="privateKeyInfo">
/// The private Key Info.
/// </param>
/// <returns>
/// The decrypted data.
/// </returns>
public static string Decrypt(string dataToDecrypt, RSAParameters privateKeyInfo)
{
//// The bytearray to hold all of our data after decryption
byte[] decryptedBytes;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
try
{
byte[] bytesToDecrypt = Convert.FromBase64String(dataToDecrypt);
//// Import the private key info
RSA.ImportParameters(privateKeyInfo);
//// No need to subtract padding size when decrypting (OR do I?)
int blockSize = RSA.KeySize / 8;
//// buffer to write byte sequence of the given block_size
byte[] buffer = new byte[blockSize];
//// buffer containing decrypted information
byte[] decryptedBuffer = new byte[blockSize];
//// Initializes our array to make sure it can hold at least the amount needed to decrypt.
decryptedBytes = new byte[dataToDecrypt.Length];
for (int i = 0; i < bytesToDecrypt.Length; i += blockSize)
{
if (2 * i > bytesToDecrypt.Length && ((bytesToDecrypt.Length - i) % blockSize != 0))
{
buffer = new byte[bytesToDecrypt.Length - i];
blockSize = bytesToDecrypt.Length - i;
}
//// If the amount of bytes we need to decrypt isn't enough to fill out a block, only decrypt part of it
if (bytesToDecrypt.Length < blockSize)
{
buffer = new byte[bytesToDecrypt.Length];
blockSize = bytesToDecrypt.Length;
}
Buffer.BlockCopy(bytesToDecrypt, i, buffer, 0, blockSize);
decryptedBuffer = RSA.Decrypt(buffer, false);
decryptedBuffer.CopyTo(decryptedBytes, i);
}
}
finally
{
//// Clear the RSA key container, deleting generated keys.
RSA.PersistKeyInCsp = false;
}
}
//// We encode each byte with UTF8 and then write to a string while trimming off the extra empty data created by the overhead.
var encoder = new UTF8Encoding();
return encoder.GetString(decryptedBytes).TrimEnd(new[] { '\0' });
}
Как я уже сказал, я не очень много тестировал, кроме размеров ниже, на уровне блока и выше, но, похоже, он делает то, что должен. Я все еще новичок, поэтому я бы очень хотел, чтобы вы внимательно изучили мой код:)
Может быть, я что-то упустил, но похоже, что ваша функция Encrypt() не использует ни keyString
параметр или содержание encryptKey
,