简体   繁体   English

在C#中使用具有正确私钥和公钥对的RSA解密时,数据异常异常

[英]Bad data exception when decrypting using RSA with correct private and public key pair in C#

This is my code for decryption process: 这是我的解密过程代码:

    private RSACryptoServiceProvider _rsa;
    private string _privateKey;
    private string _publicKey;

    public RsaLibrary()
    {
        //initialsing the RSA object taking the option of a 1024 key size
        _rsa = new RSACryptoServiceProvider(1024);
        _privateKey = _rsa.ToXmlString(true);
        _publicKey = _rsa.ToXmlString(false);

    }

 public string Decrypt(string ciphertext, string privateKey_ = null)
    {
        if (String.IsNullOrEmpty(privateKey_))
        {
            return DecryptToBytes(ciphertext, _privateKey);
        }
        else
        {
            return DecryptToBytes(ciphertext, privateKey_);
        }
    }

    private string DecryptToBytes(string ciphertext, string privateKey)
    {
        if (String.IsNullOrEmpty(privateKey))
        {
            throw new ArgumentNullException("Error: No key provided.");
        }
        if (ciphertext.Length<=0)
        {
            throw new ArgumentNullException("Error: No message to decrypt.");
        }

        byte[] plaintext;
        byte[] ciphertext_Bytes = Encoding.Unicode.GetBytes(ciphertext);
        _rsa.FromXmlString(privateKey);

        plaintext = _rsa.Decrypt(ciphertext_Bytes, false);

        return Encoding.Unicode.GetString(plaintext);
    }

The encryption code: 加密代码:

        private string EncryptToByte(string plaintext, string publicKey)
    {
        if (String.IsNullOrEmpty(publicKey))
        {
            throw new ArgumentNullException("Error: No key provided.");
        }
        if (plaintext.Length<=0)
        {
            throw new ArgumentNullException("Error: No message to incrypt");
        }


        byte[] ciphertext;
        byte[] plaintext_Bytes = Encoding.Unicode.GetBytes(plaintext);
        _rsa.FromXmlString(publicKey);

        ciphertext = _rsa.Encrypt(plaintext_Bytes, false);
        return Convert.ToBase64String(ciphertext);
    }

I can not see where I am going wrong. 我看不到我要去哪里错了。 I have made sure that the keys are correct. 我已确保密钥正确。 The public one which i extracted using this line in the constructor: _publicKey = _rsa.ToXmlString(false); 我在构造函数中使用此行提取的公共代码:_publicKey = _rsa.ToXmlString(false); This public key is displayed on the form that I created. 此公共密钥显示在我创建的表单上。 The private i used the "true" instead of false. 私人我使用“ true”代替false。

Any ideas? 有任何想法吗?

Ciphertext is very unlikely to be genuinely UTF-16-encoded text. 密文不太可能是真正的UTF-16编码文本。 Assuming that the encryption side had something like: 假设加密端具有以下内容:

string encryptedText = Encoding.Unicode.GetString(encryptedBytes);

you've basically lost data. 您基本上已经丢失了数据。 The result of encryption is not text - it's arbitrary binary data. 加密的结果不是文本-​​它是任意二进制数据。 If you want to convert that to text for some transport reason, you should use Base64, eg 如果出于某种运输原因要将其转换为文本,则应使用Base64,例如

string base64EncryptedText = Convert.ToBase64String(encryptedBytes);

Then use Convert.FromBase64String to recover the original encrypted binary data which is ready to decrypt. 然后使用Convert.FromBase64String恢复准备解密的原始加密二进制数据。

Here is your RsaLibrary class using a base-64 string as Jon suggested: 这是Jon建议的使用base-64字符串的RsaLibrary类:

    class RsaLibrary
    {
        //initializing the RSA object taking the option of a 1024 key size
        readonly RSACryptoServiceProvider _rsa = new RSACryptoServiceProvider(1024);
        readonly string _privateKey;
        readonly string _publicKey;

        public RsaLibrary()
        {
            _privateKey = _rsa.ToXmlString(true);
            _publicKey = _rsa.ToXmlString(false);
        }

        public string Decrypt(string ciphertext, string privateKey_ = null)
        {
            if (ciphertext.Length <= 0) throw new ArgumentNullException("ciphertext");

            string key = String.IsNullOrEmpty(privateKey_) ? _privateKey : privateKey_;
            return DecryptToBytes(ciphertext, key);
        }
        private string DecryptToBytes(string ciphertext, string privateKey)
        {
            if (String.IsNullOrEmpty(privateKey)) throw new ArgumentNullException("privateKey");

            byte[] ciphertext_Bytes = Convert.FromBase64String(ciphertext);
            _rsa.FromXmlString(privateKey);

            byte[] plaintext = _rsa.Decrypt(ciphertext_Bytes, false);
            return Encoding.Unicode.GetString(plaintext);
        }

        public string Encrypt(string plaintext, string publicKey_ = null)
        {
            if (plaintext.Length <= 0) throw new ArgumentNullException("plaintext");

            string key = String.IsNullOrEmpty(publicKey_) ? _publicKey : publicKey_;
            return EncryptToBytes(plaintext, key);
        }
        private string EncryptToBytes(string plaintext, string publicKey)
        {
            if (String.IsNullOrEmpty(publicKey)) throw new ArgumentNullException("publicKey");

            byte[] plaintext_Bytes = Encoding.Unicode.GetBytes(plaintext);
            _rsa.FromXmlString(publicKey);

            byte[] ciphertext = _rsa.Encrypt(plaintext_Bytes, false);
            return Convert.ToBase64String(ciphertext);
        }
    }

And you really should use SecureString for your plaintext. 而且,您确实应该将SecureString用于纯文本。

class RsaLibrary2
{
    //initializing the RSA object taking the option of a 1024 key size
    readonly RSACryptoServiceProvider _rsa = new RSACryptoServiceProvider(1024);
    readonly string _privateKey;
    readonly string _publicKey;

    public RsaLibrary2()
    {
        _privateKey = _rsa.ToXmlString(true);
        _publicKey = _rsa.ToXmlString(false);
    }

    public SecureString Decrypt(string ciphertext, string privateKey_ = null)
    {
        if (ciphertext.Length <= 0) throw new ArgumentNullException("ciphertext");

        string key = String.IsNullOrEmpty(privateKey_) ? _privateKey : privateKey_;
        return DecryptToBytes(ciphertext, key);
    }
    private SecureString DecryptToBytes(string ciphertext, string privateKey)
    {
        if (String.IsNullOrEmpty(privateKey)) throw new ArgumentNullException("privateKey");

        byte[] ciphertext_Bytes = Convert.FromBase64String(ciphertext);
        _rsa.FromXmlString(privateKey);

        byte[] plainbytes = _rsa.Decrypt(ciphertext_Bytes, false);
        char[] plain = Encoding.Unicode.GetChars(plainbytes);
        var retval = new SecureString();
        FromArray(retval, ref plain);
        return retval;
    }

    public string Encrypt(SecureString plaintext, string publicKey_ = null)
    {
        if (plaintext == null) throw new ArgumentNullException("plaintext");
        if (plaintext.Length <= 0) throw new ArgumentOutOfRangeException("plaintext");

        string key = String.IsNullOrEmpty(publicKey_) ? _publicKey : publicKey_;
        return EncryptToBytes(plaintext, key);
    }
    private string EncryptToBytes(SecureString plaintext, string publicKey)
    {
        if (String.IsNullOrEmpty(publicKey)) throw new ArgumentNullException("publicKey");

        byte[] plaintext_Bytes = SecureStringToByteArray(plaintext);
        _rsa.FromXmlString(publicKey);

        byte[] ciphertext = _rsa.Encrypt(plaintext_Bytes, false);
        return Convert.ToBase64String(ciphertext);
    }

    // YIKES! Copies the SecureString to a byte[] array; use with caution!
    static byte[] SecureStringToByteArray(SecureString secureString)
    {
        if (secureString == null) throw new ArgumentNullException(/*MSG0*/"secureString");

        // copy plaintext from unmanaged memory into managed byte[]s
        int stringSize = secureString.Length * sizeof(char); // http://blogs.msdn.com/shawnfa/archive/2005/02/25/380592.aspx
        byte[] bytes = new byte[stringSize];
        IntPtr pString = Marshal.SecureStringToGlobalAllocUnicode(secureString); // do this last to minimize the availability of "pString"
        try // free "pString" even if Copy() throws
        {
            Marshal.Copy(pString, bytes, 0, stringSize);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(pString); // clear plaintext from unmanaged memory ASAP
        }

        return bytes;
    }

    static void ArrayZero<T>(ref T[] array)
    {
        Array.Clear(array, 0, array.Length); // clear from managed memory
        Array.Resize(ref array, 0); // set Length=0 ASAP; even the length can be valuable
        array = null; // just to "help" callers know there's nothing in the array anymore
    }

    static void FromArray(SecureString secureString, ref char[] array)
    {
        try // clear "array" even if AppendChar() throws
        {
            for (int i = 0; i < array.Length; i++)
            {
                secureString.AppendChar(array[i]);
                array[i] = default(char); // clear plaintext ASAP
            }
        }
        finally
        {
            ArrayZero(ref array); // clear plaintext from managed memory
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM