“Bad Data” CryptographicException

2019-01-23 17:11发布

问题:

First, I have only written the code below for academic purposes. The reason I say this is because I am not putting this in a production environment, and therefor am "bypassing" some of the overhead that I would need to do if I was, I simply need to be able to encrypt/decrypt a string using the code below. I was able to do it a few time, but for some reason, I started receiving "CryptographicException Bad Data" and am not sure what might be causing the problem.

   private string RSAEncrypt(string value)
    {
        byte[] encryptedData = Encoding.Unicode.GetBytes(value);

        CspParameters cspParams = new CspParameters();
        cspParams.KeyContainerName = _rsaContainerName;
        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048,cspParams))
        {
            encryptedData = RSA.Encrypt(encryptedData, false);
            return Convert.ToBase64String(encryptedData);

        }

    }



    private string RSADecrypt(string value)
    {

        byte[] encryptedData = Encoding.Unicode.GetBytes(value);

        CspParameters cspParams = new CspParameters();
        cspParams.KeyContainerName = _rsaContainerName;
        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048,cspParams))
        { 
            encryptedData = RSA.Decrypt(encryptedData,false);
            return Convert.ToBase64String(encryptedData);

        }
    }

It is only throwing this exception on the RSADecrypt call.

Any ideas? I read somewhere it might have to do with the expected size of encryptedData that is passed into RSA.Decrypt.

Thanks }

回答1:

  • Convert the plaintext back and forth using a string-encoding (i.e. Encoding.Unicode).

  • Convert the encrypted data back and forth using Base-64 (i.e. Convert.[To/From]Base64String);

Like this:

private string RSAEncrypt(string value)
{
    byte[] plaintext = Encoding.Unicode.GetBytes(value);

    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = _rsaContainerName;
    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048,cspParams))
    {
        byte[] encryptedData = RSA.Encrypt(plaintext, false);
        return Convert.ToBase64String(encryptedData);
    }
}

private string RSADecrypt(string value)
{
    byte[] encryptedData = Convert.FromBase64String(value);

    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = _rsaContainerName;
    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048,cspParams))
    { 
        byte[] decryptedData = RSA.Decrypt(encryptedData,false);
        return Encoding.Unicode.GetString(decryptedData);
    }
}


回答2:

http://blogs.msdn.com/b/shawnfa/archive/2005/11/10/491431.aspx

Don't Roundtrip Ciphertext Via a String Encoding

One common mistake that people make when using managed encryption classes is that they attempt to store the result of an encryption operation in a string by using one of the Encoding classes. That seems to make sense right? After all, Encoding.ToString() takes a byte[] and converts it to a string which is exactly what they were looking for.

...

Instead if you want to convert the ciphertext into a string, use Base64 encoding.

...

Results in code that works every time, since base 64 encoding is guaranteed to be able to accurately represent any input byte sequence.

There is a good, correct example here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx



回答3:

RSA is not meant to encrypt large objects. You'll get exceptions is you go over the padding limit. The actual limit is based on the padding itself (using false means you're using the old PKCS#1 v1.5 padding) and the length of your public key (2048 bits).

The right way to use RSA with large object is to encrypt the large object with a symmetric key (e.g. a 256 bits AES secret key) and the encrypt this small key with your RSA public key.

You can find code to do such things on my blog.



回答4:

I had same problem this was because I put this in my Global.asax file to convert Url into lowercase.

 protected void Application_BeginRequest(object sender, EventArgs e)
    {
      //   Don't rewrite requests for content (.png, .css) or scripts (.js)
        if (Request.Url.AbsolutePath.Contains("/Content/") ||
            Request.Url.AbsolutePath.Contains("/Scripts/"))
            return;

        If uppercase chars exist, redirect to a lowercase version
        var url = Request.Url.ToString();
       if (Regex.IsMatch(url, @"[A-Z]"))
        {
            Response.Clear();
           Response.Status = "301 Moved Permanently";
           Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
            Response.AddHeader("Location", url.ToLower());
            Response.End();
       }
    }

So

Example

If encryption is done with base64 string encode as

http://localhost:11430/orderpreview?orderID=t4RZrnefb2a%253d&price=27.9

Then on hitting the action the "Application_BeginRequest" converting it into

http://localhost:11430/orderpreview?orderID=t4rzrnefb2a%253d&price=27.9

You can see the RZ was captial converted into small rz this will fail the decryption.

Solution

Inside RouteConfig class RegisterRoutes method put this if you want the url in lowercase

        routes.Canonicalize().Www();
        routes.Canonicalize().Lowercase();
        routes.Canonicalize().NoTrailingSlash();