web-dev-qa-db-fra.com

Longueur non valide pour un tableau de caractères base 64

Comme le titre l'indique, je reçois: 

Longueur non valide pour un caractère en base 64 tableau.

J'ai lu sur ce problème ici et il semble que la suggestion Est de stocker ViewState en SQL s'il est volumineux. J'utilise un assistant avec une grande quantité de données, donc il y a de fortes chances que mon ViewState soit grand. Mais, avant de passer à la solution "store-in-DB" , Peut-être que quelqu'un pourra jeter un coup d'œil et me dire si j'ai d'autres options?

Je construis l'e-mail pour la livraison en utilisant la méthode ci-dessous:

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

La méthode Encrypt ressemble à ceci: 

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

Voici à quoi ressemble le code HTML dans hotmail:

S'il vous plaît cliquer sur le lien ci-dessous ou collez-le dans un navigateur pour vérifier votre compte email.

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

À la réception, la page VerifyEmail.aspx.cs a la ligne:

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

Voici le getter pour UserNameToVerify:

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

Et voici la méthode GetQueryStringValue:

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

Et la méthode de déchiffrement ressemble à:

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

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

Peut-on remédier à cette erreur avec un correctif de code ou dois-je stocker ViewState dans la base de données?

70
Peter

La longueur d'une chaîne codée en base64 est toujours un multiple de 4. Si ce n'est pas un multiple de 4, les caractères = sont ajoutés jusqu'à ce qu'il le soit. Une chaîne de requête de la forme ?name=value a des problèmes lorsque la value contient des caractères = (certains d'entre eux seront supprimés, je ne me rappelle pas le comportement exact). Vous pourrez peut-être vous en tirer en ajoutant le bon nombre de caractères = avant de procéder au décodage en base64.

Modifier 1

Vous pouvez constater que la valeur de UserNameToVerify a eu "+" changé en " " et vous devrez peut-être faire quelque chose comme ceci:

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

Cela devrait avoir la bonne longueur;

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

Bien sûr, appeler UrlEncode (comme dans la réponse de LukeH) devrait rendre tout cela discutable.

170
user287466

Je suppose que vous avez simplement besoin de URL-encoder votre chaîne Base64 lorsque vous l'incluez dans la chaîne de requête.

L'encodage Base64 utilise certains caractères qui doivent être encodés s'ils font partie d'une chaîne de requête (à savoir + et /, et peut-être = également). Si la chaîne n'est pas correctement encodée, vous ne pourrez pas la décoder avec succès à l'autre extrémité, d'où les erreurs.

Vous pouvez utiliser la méthode HttpUtility.UrlEncode pour encoder votre chaîne 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>";
27
LukeH

Je ne suis pas assez digne de confiance pour faire un vote ou un commentaire, mais la réponse de LukeH était parfaite pour moi.

Comme le cryptage AES est la norme à utiliser maintenant, il génère une chaîne base64 (au moins toutes les implémentations de cryptage/décryptage que j'ai vues). Cette chaîne a une longueur en multiples de 4 (string.length% 4 = 0)

Les chaînes que je devenais contenues + et = au début ou à la fin, et lorsque vous venez de concaténer cela dans la chaîne de requête d'une URL, cela semble correct (par exemple, dans un courrier électronique que vous générez), mais lorsque le lien est suivi et le La page .NET le reçoit et le met dans this.Page.Request.QueryString, ces caractères spéciaux disparaîtront et votre longueur de chaîne ne sera pas dans un multiple de 4.

Comme il y a des caractères spéciaux à l'avant de la chaîne (ex: +), de même que = à la fin, vous ne pouvez pas ajouter un peu de = pour compenser la différence, car vous modifiez le texte chiffré de manière à ne correspond pas à ce qui était réellement dans la chaîne de requête d'origine.

Ainsi, en enveloppant le texte chiffré avec HttpUtility.URLEncode (et non HtmlEncode), les caractères non alphanumériques sont transformés de manière à ce que .NET les réanalyse dans leur état d'origine lorsqu'il est interprété dans la collection de chaînes de requête.

La bonne chose à faire est que nous n'avons besoin que de faire l'URLEncode lors de la génération de la chaîne de requête pour l'URL. Du côté entrant, il est automatiquement traduit dans la valeur de chaîne d'origine.

Voici un exemple de code

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

Mon hypothèse initiale sans connaître les données serait que UserNameToVerify n’est pas un multiple de 4. Découvrez le FromBase64String sur msdn.

// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");
4
SwDevMan81
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
0
JIYAUL MUSTAPHA

La chaîne cryptée avait deux caractères spéciaux, + et =.

Le signe '+' donnait l'erreur, donc la solution ci-dessous a bien fonctionné:

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