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

108
正如标题所述,我遇到了以下问题:
无效的 Base-64 字符数组长度。
我在这里阅读了相关问题,似乎建议如果 ViewState 很大,则将其存储在 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);
}

而且decrypt方法看起来像:

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

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

这个错误能够通过代码修复吗,还是我必须将ViewState存储在数据库中?

7个回答

242

Base64编码的字符串长度始终是4的倍数。如果不是4的倍数,则会附加=字符,直到成为4的倍数。形式为?name=value的查询字符串在value包含=字符时会出现问题(其中一些字符将被删除,我不记得确切的行为)。您可以尝试在进行base64解码之前附加正确数量的=字符。

编辑1

您可能会发现UserNameToVerify的值已经将"+"更改为" ",因此您可能需要执行以下操作:

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

这应该可以正确获取长度;

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

当然,调用UrlEncode(如LukeH的答案中所示)应该使这一切都无关紧要。

9
谢谢Brad - 实际上是这段代码起了作用:a = a.Replace(" ","+"); - Peter
1
@Code Sherpa:如果是这种情况,你最好在发送字符串之前进行urlencode,然后在接收时进行urldecode。否则,如果另一个URL重要字符进入了你的字符串,那么你就必须添加另一个“Replace”语句。编码是一种全面保护你的方法。 - Matt Ellen
6
在接收请求时,您无需对字符串进行UrlDecode操作,因为ASP.Net已经对请求参数进行了UrlDecode操作。但是在发送时,您应该进行UrlEncode操作。 - bleeeah
或者如果你想要一个内联版本:a = a + new string('=', (4 - a.Length % 4) % 4)。解码RFC 4648 URL安全的Base64的示例:public string base64urlDecode(string encoded) { return System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(encoded.Replace("_","/").Replace("-","+") + new string('=', (4 - encoded.Length % 4) % 4))); } - gregmac
1
你不需要进行UrlDecode - 这个!通过我的代码调试,我发现参数已经被解码了,问题在于我运行了UrlDecode,这导致一些字符被删除。感谢@MattEllen。 - GJKH
第二部分使用“=”附加任何缺失的字符对我很有效。 - neildt

33

我猜你只需要在查询字符串中包含Base64字符串时对其进行URL编码即可。

Base64编码使用了一些字符,如果它们是查询字符串的一部分,则这些字符必须进行编码(具体来说是 +/,也可能包括 =)。如果该字符串没有被正确编码,那么在另一端就无法成功解码,因此会出现错误。

你可以使用HttpUtility.UrlEncode方法来对Base64字符串进行编码:

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>";

谢谢。刚尝试了你的建议Luke,但还是不起作用 :(。 - Peter
@Sherpa - 继续努力,问题几乎肯定是由于尾随的=字符引起的。 - user287466
卢克 - 我有一种感觉你是对的。我会在家里试试这个。非常感谢。顺便说一下 - 我在原帖中添加了我的Hotmail收件箱中字符串的外观。 - Peter
布拉德叔叔是对的,我上周也遇到了同样的问题,问题在于一个结尾处的 "=" 字符。 -_- - Marcote

12

我还没有足够的声望来点赞或评论,但LukeH的答案对我非常准确。

由于AES加密是现在使用的标准,它会生成一个Base64字符串(至少我见过的所有加密/解密实现都是如此)。这个字符串的长度是4的倍数(string.length%4 = 0)。

我得到的字符串包含在开头或结尾的+和=,当你把它们连接成URL的查询字符串时,它看起来没问题(例如,在你生成的电子邮件中),但当链接被点击,.NET页面接收该查询字符串并将其放入this.Page.Request.QueryString中时,那些特殊字符就会消失,并且你的字符串长度不会是4的倍数。

由于这些特殊字符在字符串的前面(例如:+),以及在字符串结尾处的=,所以你不能只添加一些=来弥补差距,因为你会改变密文,而这并不符合原始查询字符串中实际存在的内容。

因此,用HttpUtility.URLEncode(而不是HtmlEncode)将密文进行编码,可以以一种方式转换非字母数字字符,从而确保.NET在将其解释为查询字符串集合时将它们解析回原始状态。

好消息是,我们只需要在为URL生成查询字符串时进行URLEncode处理。在接收端,它会自动转换回原始字符串值。

这是一些示例代码:

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

8

如果没有数据,我最初的猜测可能是UserNameToVerify的长度不是4的倍数。请查看msdn上的FromBase64String

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

感谢SwDevMan81。我现在要离开工作了,但今晚会尝试这个。感谢你的帮助。 - Peter
没问题,解决方法是用一个字符填充字符串,使其成为4的倍数。 - SwDevMan81
再次感谢SwDevMan81。我会看一下的。我在原帖中发布了UserNameToVeryify(供参考)。好的...现在我真的需要走了,否则我会得罪真正的老板 :) - Peter
看起来这篇帖子也许有所帮助:http://stackoverflow.com/questions/1392970/httpserverutility-urltokendecode-fails-sometimes-invalid-length-for-a-base-64 - SwDevMan81

2

加密字符串中有两个特殊字符,分别是+=

由于'+'符号引起了错误,因此下面的解决方案可以很好地解决问题:

最初的回答:

//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
...

0

在进行加密时使用

HttpUtility.UrlEncode(Encryptedtext));

在解密时,

使用

        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 = 

然后将此值发送以进行解密


0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接