Comprimento inválido para uma matriz de caracteres Base-64


93

Como o título diz, estou recebendo:

Comprimento inválido para uma matriz de caracteres Base-64.

Eu li sobre esse problema aqui e parece que a sugestão é armazenar ViewState em SQL se ele for grande. Estou usando um assistente com uma grande quantidade de coleta de dados, portanto, é provável que meu ViewState seja grande. Mas, antes de me voltar para a solução "store-in-DB", talvez alguém possa dar uma olhada e me dizer se eu tenho outras opções?

Eu construo o e-mail para entrega usando o método abaixo:

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

O método Encrypt é semelhante a este:

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

Aqui está a aparência do HTML no hotmail:

Clique no link abaixo ou cole em um navegador para verificar sua conta de e-mail.

http: // localhost: 1563 / Accounts / VerifyEmail.aspx? a = YOHY57xYRENEOu3H + FGq1Rf09AZAI56EPjfwuK8XWKg =

Na extremidade de recebimento, a página VerifyEmail.aspx.cs tem a linha:

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

Aqui está o getter para UserNameToVerify:

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

E aqui está o método GetQueryStringValue:

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

E o método de descriptografia se parece com:

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

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

Esse erro pode ser corrigido com uma correção de código ou devo armazenar ViewState no banco de dados?

Respostas:


209

O comprimento de uma string codificada em base64 é sempre um múltiplo de 4. Se não for um múltiplo de 4, os =caracteres são acrescentados até que seja. Uma string de consulta do formulário ?name=valuetem problemas quando valuecontém =caracteres (alguns deles serão descartados, não me lembro do comportamento exato). Você pode conseguir acrescentar o número certo de =caracteres antes de fazer a decodificação em base64.

Editar 1

Você pode descobrir que o valor de UserNameToVerifyhad foi "+"alterado para de " ", então pode ser necessário fazer algo como:

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

Isso deve ter o comprimento certo;

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

É claro que ligar UrlEncode(como na resposta de LukeH) deve tornar tudo discutível.


9
Obrigado Brad - Na verdade, foi esse pequeno código que fez o trabalho: a = a.Replace ("", "+");
— Peter

1
@Code Sherpa: se for esse o caso, sua melhor escolha é urlencode antes de enviar a string e urldecode no recebimento. Caso contrário, se outro caractere significativo do url entrar em sua string, você terá que adicionar outra Replaceinstrução. A codificação é um macacão que o protege de qualquer maneira.
— Matt Ellen de

6
Você não precisa fazer o UrlDecode de sua string no recebimento, pois os parâmetros de solicitação já são UrlDecoded por ASP.Net. No entanto, você deve UrlEncode ao enviar.
— bleeeah

Ou se você quiser uma versão em linha: a = a + new string('=', (4 - a.Length % 4) % 4). Exemplo para decodificar 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))); }
— gregmac

1
"Você não precisa para UrlDecode" - ISTO! Percorrendo meu código, pude ver que o parâmetro já estava decodificado, o problema era eu examiná-lo através do UrlDecodequal estava removendo caracteres. Obrigado @MattEllen
— GJKH

30

Meu palpite é que você simplesmente precisa codificar em URL sua string Base64 ao incluí-la na string de consulta.

Codificação Base64 usa alguns caracteres que devem ser codificadas se eles são parte de uma querystring (nomeadamente +e /, e talvez =também). Se a string não estiver codificada corretamente, você não conseguirá decodificá-la com sucesso na outra extremidade, daí os erros.

Você pode usar o HttpUtility.UrlEncodemétodo para codificar sua string 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>";

Obrigado. Tentei sua sugestão, Lucas, mas não funcionou :(.
— Pedro

@Sherpa - Continue trabalhando, o problema está quase certamente com os =caracteres finais .

Luke - Tenho a sensação de que você está certo. Vou tentar isso em casa. Muito obrigado. Para sua informação - adicionei a aparência da string na minha caixa de entrada do hotmail no meu post original.
— Peter

tio brad está certo, eu tive o mesmo problema na semana passada e o problema era um caractere "=" à direita ._.
— Marcote

10

Não tenho reputação o suficiente para votar ou comentar ainda, mas a resposta de LukeH foi certa para mim.

Como a criptografia AES é o padrão a ser usado agora, ela produz uma string base64 (pelo menos todas as implementações de criptografar / descriptografar que vi). Esta string tem um comprimento em múltiplos de 4 (string.length% 4 = 0)

As strings que eu estava recebendo continham + e = no início ou no final, e quando você apenas concatená-las em uma string de consulta de URL, parecerá correto (por exemplo, em um e-mail que você gerar), mas quando o link for seguido e A página .NET o recebe e coloca em this.Page.Request.QueryString, esses caracteres especiais desaparecerão e o comprimento da string não será um múltiplo de 4.

Como são caracteres especiais na FRENTE da string (ex: +), bem como = no final, você não pode simplesmente adicionar alguns = para compensar a diferença, pois você está alterando o texto cifrado de uma forma que não não corresponde ao que estava realmente na string de consulta original.

Portanto, envolver o texto cifrado com HttpUtility.URLEncode (não HtmlEncode) transforma os caracteres não alfanuméricos de uma maneira que garante que o .NET os analise de volta ao seu estado original quando é interpretado na coleção de querystring.

O bom é que só precisamos fazer o URLEncode ao gerar a string de consulta para a URL. No lado de entrada, ele é automaticamente convertido de volta para o valor da string original.

Aqui está um exemplo de código

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

6

Meu palpite inicial, sem saber os dados, seria que UserNameToVerify não é um múltiplo de 4 de comprimento. Verifique FromBase64String no msdn .

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

Obrigado SwDevMan81. Estou saindo do trabalho agora, mas tentarei isso mais tarde esta noite Obrigado pela ajuda.
— Pedro

Sem problemas, a solução seria preencher com um caractere para obter uma string que é um múltiplo de 4.
— SwDevMan81

Obrigado novamente SwDevMan81. Vou dar uma olhada nisso. Eu postei UserNameToVeryify em minha postagem original (FYI). OK ... agora eu realmente preciso ir ou terei problemas com o verdadeiro chefe :)
— Peter

Parece que esta postagem também pode ajudar: stackoverflow.com/questions/1392970/…
— SwDevMan81

1

A string criptografada tinha dois caracteres especiais +e =.

O sinal '+' estava dando o erro, então a solução abaixo funcionou bem:

//replace + sign

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

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

OU

//encode special charactes 

encryted_string = HttpUtility.UrlEncode(encryted_string);

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

0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.