CryptographicException периодически возникает при шифровании / дешифровании с помощью RSA - PullRequest
6 голосов
/ 30 июня 2010

Я пытаюсь зашифровать и расшифровать данные с помощью 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);
}

Сбой с этим исключением:

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. Этот был из-за шифрования с помощью закрытого ключа и попытки расшифровки с помощью открытого ключа, но я не думаю, что я делаю это. Этот вопрос имеет то же исключение, что и я, но выбранный ответ должен был использовать OpenSSL.NET, чего я бы предпочел не делать.

Что я делаю не так?

Ответы [ 3 ]

8 голосов
/ 30 июня 2010

Не могли бы вы заменить 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);
  }
3 голосов
/ 01 июля 2010

Ваша проблема с преобразованием байтов в строку. Не все последовательности байтов являются допустимой кодировкой UTF-16, и вы используете кодировку UnicodeEncoding, которая молча игнорирует недействительные байты. Если вы использовали

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

Вместо этого ваш код потерпел бы неудачу при попытке преобразовать байты вместо того, чтобы молча заменить неправильные пары байтов на 0xFFFD .

То, что тест работал во время отладки, было совпадением. Вы используете случайную пару ключей RSA, поэтому иногда вы получаете шифрование, которое является допустимой кодировкой UTF-16.

Исправлено, как предполагает SwDevMan81, использование кодировки, которая может преобразовывать все возможные байтовые массивы. F.x. Base64-кодирование.

1 голос
/ 30 июня 2010

Я бы порекомендовал использовать этот класс, к сожалению, я не помню оригинального автора, хотя ..

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, чтобы убедиться, что данные поступают из того места, где они говорят, что это ..

...