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?

Peter
fonte

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.

Vadim Ovchinnikov
fonte
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>";
LukeH
fonte
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));
Ken Forslund
fonte
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");
SwDevMan81
fonte
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
...
Vikrant
fonte
0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
Código
fonte