CryptographicException периодически происходит при шифровании/дешифровании с RSA

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

Что я делаю неправильно?

6
задан Community 23 May 2017 в 11:45
поделиться

3 ответа

Не могли бы вы заменить 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);
  }
8
ответ дан 9 December 2019 в 20:39
поделиться

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

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

1
ответ дан 9 December 2019 в 20:39
поделиться

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

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

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

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

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

3
ответ дан 9 December 2019 в 20:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: