Longueur non valide pour un tableau de caractères Base-64

91

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

Longueur non valide pour un tableau de caractères Base-64.

J'ai lu ce problème ici et il semble que la suggestion soit de stocker ViewState dans SQL s'il est volumineux. J'utilise un assistant avec beaucoup de collecte 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 peut 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 HTML dans hotmail:

Veuillez cliquer sur le lien ci-dessous ou le coller dans un navigateur pour vérifier votre compte de messagerie.

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

Du côté de 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);

Cette erreur peut-elle être corrigée avec un correctif de code ou dois-je stocker ViewState dans la base de données?

Peter
la source

Réponses:

205

La longueur d'une chaîne encodé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 que ce soit le cas. Une chaîne de requête du formulaire ?name=valuepose des problèmes lorsque le valuecontient des =caractères (certains seront supprimés, je ne me souviens pas du comportement exact). Vous pourrez peut-être vous en sortir en ajoutant le bon nombre de= caractères avant de procéder au décodage base64.

Modifier 1

Vous constaterez peut-être que la valeur de UserNameToVerifya été "+"modifiée en " "s, 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 sans objet.

Vadim Ovchinnikov
la source
9
Merci Brad - C'est en fait ce petit bout de code qui a fait le travail: a = a.Replace ("", "+");
Peter
1
@Code Sherpa: si tel est le cas, votre meilleur choix est d'urlencode avant d'envoyer la chaîne, et d'urldecode à la réception. Sinon, si un autre caractère significatif d'url entre dans votre chaîne, vous devrez ajouter une autre Replaceinstruction. L'encodage est une combinaison qui vous protège malgré tout.
Matt Ellen
5
Vous ne devez pas UrlDecode votre chaîne à la réception car les paramètres de demande sont déjà UrlDecoded par ASP.Net. Vous devez cependant UrlEncode lors de l'envoi.
bleeeah
Ou si vous voulez une version en ligne: a = a + new string('=', (4 - a.Length % 4) % 4). Exemple pour décoder 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
"Vous ne devez pas UrlDecode" - CECI! En parcourant mon code, je pouvais voir que le paramètre était déjà décodé, le problème était que je l'exécutais à travers UrlDecodelequel nous supprimions des caractères. Merci @MattEllen
GJKH
30

Je suppose que vous devez simplement encoder l'URL 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 =aussi). 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 HttpUtility.UrlEncodeméthode 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>";
LukeH
la source
Merci. Je viens d'essayer votre suggestion Luke mais cela n'a pas fonctionné :(.
Peter
@Sherpa - Continuez à travailler, le problème vient presque certainement des =caractères de fin .
Luke - J'ai le sentiment que tu as raison. J'essaierai ça à la maison. Merci un paquet. FYI - J'ai ajouté à quoi ressemble la chaîne dans ma boîte de réception hotmail dans mon message d'origine.
Peter
oncle Brad a raison, j'ai eu le même problème la semaine dernière et le problème était un caractère "=" à la fin ._.
Marcote
10

Je ne suis pas encore assez réputé pour voter ou commenter, mais la réponse de LukeH était parfaite pour moi.

Comme le cryptage AES est la norme à utiliser maintenant, il produit 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 recevais contenaient + et = au début ou à la fin, et lorsque vous concaténez simplement cela dans la chaîne de requête d'une URL, cela aura l'air correct (par exemple, dans un e-mail que vous générez), mais lorsque le lien est suivi et le La page .NET la reçoit et la place dans this.Page.Request.QueryString, ces caractères spéciaux auront disparu et la longueur de votre chaîne ne sera pas un multiple de 4.

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

Ainsi, l'encapsulation du texte chiffré avec HttpUtility.URLEncode (et non HtmlEncode) transforme les caractères non alphanumériques d'une manière qui garantit que .NET les analyse à nouveau dans leur état d'origine lorsqu'il est interprété dans la collection de chaînes de requête.

La bonne chose est que nous n'avons besoin de faire le URLEncode que 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));
Ken Forslund
la source
6

Ma première supposition sans connaître les données serait que UserNameToVerify n'est pas un multiple de 4 de longueur. Découvrez la FromBase64String sur msdn.

// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");
SwDevMan81
la source
Merci SwDevMan81. Je viens de quitter le travail mais je vais essayer plus tard ce soir. Merci de votre aide.
Peter
Pas de problème, le correctif serait de compléter avec un caractère pour obtenir une chaîne multiple de 4.
SwDevMan81
Merci encore SwDevMan81. Je vais y jeter un œil. J'ai publié UserNameToVeryify dans mon message d'origine (FYI). OK ... maintenant j'ai vraiment besoin d'y aller ou je vais avoir des ennuis avec le vrai patron :)
Peter
On dirait que cet article pourrait également aider: stackoverflow.com/questions/1392970/…
SwDevMan81
1

La chaîne chiffrée contenait deux caractères spéciaux +et= .

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

//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
la source
0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
Code
la source