Base-64 char数组的长度无效

时间:2010-05-27 23:28:43

标签: c# asp.net viewstate base64

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

  

Base-64 char的长度无效   阵列。

我已经在这里看到了这个问题,似乎就是这样 建议是将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);
}

加密方法如下所示:

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的获取者:

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

6 个答案:

答案 0 :(得分:172)

base64编码字符串的长度始终是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的答案)应该让这一切都没有用。

答案 1 :(得分:28)

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

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

答案 2 :(得分:9)

我还没有足够的声誉来评价或评论,但是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));

答案 3 :(得分:4)

我不知道数据的初步猜测是UserNameToVerify不是4的倍数。查看msdn上的FromBase64String

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

答案 4 :(得分:0)

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

'+'符号给出了错误,因此以下解决方案效果很好:

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

答案 5 :(得分:0)

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