7

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();

.NET を使用した RSA 暗号化と復号化に関する StackOverflow に関するいくつかの質問を見てきました。 これは、秘密鍵で暗号化し、公開鍵で復号化しようとしたことが原因でしたが、私はそうしているとは思いません。 この質問には私と同じ例外がありますが、選択した回答は OpenSSL.NET を使用することでした。

私は何を間違っていますか?

4

3 に答える 3

9

と置き換えてByteConverter.GetBytes、それが役立つかどうかを確認 してください。例外は通常、データに無効な文字が含まれていること、または長さが復号化に適した長さではないことを意味します。Convert関数を使用すると、問題が解決する可能性があると思います。Convert.FromBase64StringByteConverter.GetStringConvert.ToBase64StringBad 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);
  }
于 2010-06-30T19:47:19.270 に答える
4

あなたの問題は、バイトから文字列への変換にあります。すべてのバイト シーケンスが有効な UTF-16 エンコーディングであるとは限らず、無効なバイトを黙って無視する UnicodeEncoding を使用しています。使用した場合

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

代わりに、無効なバイトペアをサイレントに0xFFFDに置き換えるのではなく、バイトを変換しようとしたときにコードが失敗していました。

デバッグ中にテストが機能したという事実は偶然でした。ランダムな RSA キー ペアを使用しているため、有効な UTF-16 エンコーディングである暗号化を取得することがあります。

修正方法は、SwDevMan81 が示唆するように、考えられるすべてのバイト配列を変換できるエンコーディングを使用することです。Fx Base64 エンコーディング。

于 2010-07-01T08:05:47.047 に答える
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署名で、データがどこから来ているかを確認する..

于 2010-06-30T19:58:19.810 に答える