Base-64字符数组的长度无效

正如标题所说,我得到了:

Base-64字符的长度无效 数组。

我在这里读到过这个问题 建议是存储在 SQL 查看状态,如果它是大的 使用向导进行大量的数据收集 是我的 ViewState 是大的。但是,在我转向“存储在数据库中”之前, 解决办法,也许有人可以看看,并告诉我,如果我有 其他选择?

我使用以下方法建立电子邮件发送:

public void SendEmailAddressVerificationEmail(string userName, string to)
{
string msg = "Please click on the link below or paste it into a browser to verify your email account.<BR><BR>" +
"<a href=\"" + _configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
userName.Encrypt("verify") + "\">" +
_configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
userName.Encrypt("verify") + "</a>";


SendEmail(to, "", "", "Account created! Email verification required.", msg);
}

Encrypt 方法如下所示:

public static string Encrypt(string clearText, string Password)
{


byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);


PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });




byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));


return Convert.ToBase64String(encryptedData);
}

下面是 hotmail 中的 HTML:

请按以下连结或 粘贴到浏览器中以验证您的 电邮帐户。

Http://localhost:1563/accounts/verifyemail.aspx?a=yohy57xyreneou3h+fgq1rf09azai56epjfwuk8xwkg=

在接收端, verifyemail.aspx.cs 页面上有这样一行字:

 string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");

下面是 UserNameToVerify 的 getter:

public string UserNameToVerify
{
get
{
return GetQueryStringValue("a").ToString();
}
}

下面是 GetQueryStringValue 方法:

private static string GetQueryStringValue(string key)
{
return HttpContext.Current.Request.QueryString.Get(key);
}

解密方法是这样的:

public static string Decrypt(string cipherText, string password)
{


**// THE ERROR IS THROWN HERE!!**
byte[] cipherBytes = Convert.FromBase64String(cipherText);

这个错误是否可以通过代码修复来补救,还是必须将 ViewState 存储在数据库中?

279802 次浏览

My initial guess without knowing the data would be that the UserNameToVerify is not a multiple of 4 in length. Check out the FromBase64String on msdn.

// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");

My guess is that you simply need to URL-encode your Base64 string when you include it in the querystring.

Base64 encoding uses some characters which must be encoded if they're part of a querystring (namely + and /, and maybe = too). If the string isn't correctly encoded then you won't be able to decode it successfully at the other end, hence the errors.

You can use the HttpUtility.UrlEncode method to encode your Base64 string:

string msg = "Please click on the link below or paste it into a browser "
+ "to verify your email account.<br /><br /><a href=\""
+ _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
+ HttpUtility.UrlEncode(userName.Encrypt("verify")) + "\">"
+ _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
+ HttpUtility.UrlEncode(userName.Encrypt("verify")) + "</a>";

The length of a base64 encoded string is always a multiple of 4. If it is not a multiple of 4, then = characters are appended until it is. A query string of the form ?name=value has problems when the value contains = charaters (some of them will be dropped, I don't recall the exact behavior). You may be able to get away with appending the right number of = characters before doing the base64 decode.

Edit 1

You may find that the value of UserNameToVerify has had "+"'s changed to " "'s so you may need to do something like so:

a = a.Replace(" ", "+");

This should get the length right;

int mod4 = a.Length % 4;
if (mod4 > 0 )
{
a += new string('=', 4 - mod4);
}

Of course calling UrlEncode (as in LukeH's answer) should make this all moot.

I'm not Reputable enough to upvote or comment yet, but LukeH's answer was spot on for me.

As AES encryption is the standard to use now, it produces a base64 string (at least all the encrypt/decrypt implementations I've seen). This string has a length in multiples of 4 (string.length % 4 = 0)

The strings I was getting contained + and = on the beginning or end, and when you just concatenate that into a URL's querystring, it will look right (for instance, in an email you generate), but when the the link is followed and the .NET page recieves it and puts it into this.Page.Request.QueryString, those special characters will be gone and your string length will not be in a multiple of 4.

As the are special characters at the FRONT of the string (ex: +), as well as = at the end, you can't just add some = to make up the difference as you are altering the cypher text in a way that doesn't match what was actually in the original querystring.

So, wrapping the cypher text with HttpUtility.URLEncode (not HtmlEncode) transforms the non-alphanumeric characters in a way that ensures .NET parses them back into their original state when it is intepreted into the querystring collection.

The good thing is, we only need to do the URLEncode when generating the querystring for the URL. On the incoming side, it's automatically translated back into the original string value.

Here's some example code

string cryptostring = MyAESEncrypt(MySecretString);
string URL = WebFunctions.ToAbsoluteUrl("~/ResetPassword.aspx?RPC=" + HttpUtility.UrlEncode(cryptostring));

The encrypted string had two special characters, + and =.

'+' sign was giving the error, so below solution worked well:

//replace + sign


encryted_string = encryted_string.Replace("+", "%2b");


//`%2b` is HTTP encoded string for **+** sign

OR

//encode special charactes


encryted_string = HttpUtility.UrlEncode(encryted_string);


//then pass it to the decryption process
...
    string stringToDecrypt = CypherText.Replace(" ", "+");
int len = stringToDecrypt.Length;
byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt);

While Encrypting use

HttpUtility.UrlEncode(Encryptedtext));

While Decrypting,

use

        value = HttpUtility.UrlDecode(value);
value = value.Replace(" ", "+");//to remove any empty spaces
value = value.Replace('-', '+').Replace('_', '/');//replace special char
while (value.Length % 4 != 0) value += '='; //it should be divisible by 4 or append =

Then send this value for decryption