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

Posted

技术标签:

【中文标题】Base-64 字符数组的长度无效【英文标题】:Invalid length for a Base-64 char array 【发布时间】:2011-02-24 22:13:46 【问题描述】:

正如标题所说,我得到:

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

这是 html 在 hotmail 中的样子:

请点击以下链接或 将其粘贴到浏览器中以验证您的 电子邮件帐户。

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 存储在数据库中吗?

【问题讨论】:

【参考方案1】:

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

编辑 1

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

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

这应该得到正确的长度;

int mod4 = a.Length % 4;
if (mod4 > 0 )

    a += new string('=', 4 - mod4);

当然,致电UrlEncode(如 LukeH 的回答)应该会让这一切变得毫无意义。

【讨论】:

谢谢布拉德 - 实际上是这点代码完成了这项工作:a = a.Replace(" ","+"); @Code Sherpa:如果是这种情况,您最好的选择是在发送字符串之前进行 urlencode,并在收到时进行 urldecode。否则,如果另一个 url 重要字符进入您的字符串,那么您将不得不添加另一个 Replace 语句。编码是一种保护你的工作服。 您不要在收到时对您的字符串进行 UrlDecode,因为请求参数已经由 ASP.Net 进行 UrlDecoded。但是,您应该在发送时进行 UrlEncode。 或者如果你想要一个内联版本:a = a + new string('=', (4 - a.Length % 4) % 4)。解码RFC 4648 URL-safe 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))); "你不要去 UrlDecode" - 这个!单步执行我的代码,我可以看到参数已经解码,问题是我通过 UrlDecode 运行它,它删除了字符。谢谢@MattEllen【参考方案2】:

我的猜测是,当您将 Base64 字符串包含在查询字符串中时,您只需要 URL-encode 即可。

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

【讨论】:

谢谢。刚刚尝试了你的建议卢克,但没有奏效:(。 @Sherpa - 继续努力吧,问题几乎肯定出在后面的 = 字符上。 卢克 - 我觉得你是对的。会在家里试试这个。谢谢一捆。仅供参考 - 我在我的原始帖子中的 hotmail 收件箱中添加了字符串的样子。 布拉德叔叔是对的,我上周遇到了同样的问题,问题是尾随的“=”字符._.【参考方案3】:

我还没有足够的信誉来投票或发表评论,但 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));

【讨论】:

【参考方案4】:

在不知道数据的情况下,我最初的猜测是 UserNameToVerify 的长度不是 4 的倍数。查看 msdn 上的FromBase64String。

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

【讨论】:

感谢 SwDevMan81。刚刚下班,但今晚晚些时候会试试这个。感谢您的帮助。 没问题,解决方法是用一个字符填充以获得一个 4 的倍数的字符串。 再次感谢 SwDevMan81。我会看看那个。我在原始帖子(仅供参考)中发布了 UserNameToVeryify。好吧……现在我真的需要走了,否则我要和真正的老板有麻烦了:) 看起来这篇文章也可能有帮助:***.com/questions/1392970/…【参考方案5】:

加密后的字符串有两个特殊字符,+=

'+' 符号给出了错误,所以下面的解决方案运行良好:

//replace + sign

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

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

//encode special charactes 

encryted_string = HttpUtility.UrlEncode(encryted_string);

//then pass it to the decryption process
...

【讨论】:

【参考方案6】:
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 

【讨论】:

【参考方案7】:

同时加密使用

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 = 

然后发送这个值进行解密

【讨论】:

以上是关于Base-64 字符数组的长度无效的主要内容,如果未能解决你的问题,请参考以下文章

我在前端有字符串 base64 数组。我如何发送后端这个字符串数组?

将 Base64 字符串解码为字节数组

将 UIImages 数组转换为 base64 字符串

字符串转base64加密

如何将图像的字节数组转换为表示 jpg 的 base64 编码字符串

如何在 JavaScript 中使用字节数组将字符串转换为 base64 编码?