Lunghezza non valida per un array di caratteri Base-64


93

Come dice il titolo, sto ottenendo:

Lunghezza non valida per un array di caratteri Base-64.

Ho letto di questo problema qui e sembra che il suggerimento sia di memorizzare ViewState in SQL se è grande. Sto usando una procedura guidata con una buona quantità di raccolta dati, quindi è probabile che il mio ViewState sia grande. Ma, prima di passare alla soluzione "store-in-DB", forse qualcuno può dare un'occhiata e dirmi se ho altre opzioni?

Costruisco l'e-mail per la consegna utilizzando il metodo seguente:

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

Il metodo Encrypt ha questo aspetto:

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

Ecco come appare l'HTML in hotmail:

Fare clic sul collegamento sottostante o incollarlo in un browser per verificare il proprio account di posta elettronica.

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

Sul lato ricevente, la pagina VerifyEmail.aspx.cs ha la riga:

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

Ecco il getter per UserNameToVerify:

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

Ed ecco il metodo GetQueryStringValue:

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

E il metodo di decrittografia ha il seguente aspetto:

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

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

Questo errore può essere risolto con una correzione del codice o devo memorizzare ViewState nel database?

Risposte:


209

La lunghezza di una stringa con codifica base64 è sempre un multiplo di 4. Se non è un multiplo di 4, i =caratteri vengono aggiunti finché non lo è. Una stringa di query del modulo ?name=valueha problemi quando valuecontiene =caratteri (alcuni di essi verranno eliminati, non ricordo il comportamento esatto). Potresti riuscire a farla franca aggiungendo il giusto numero di =caratteri prima di eseguire la decodifica base64.

Modifica 1

Potresti scoprire che il valore di UserNameToVerifyè stato "+"cambiato in " "'s, quindi potresti dover fare qualcosa del genere:

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

Questo dovrebbe avere la lunghezza giusta;

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

Ovviamente chiamare UrlEncode(come nella risposta di LukeH) dovrebbe rendere tutto discutibile.


9
Grazie Brad - In realtà è stato questo pezzetto di codice che ha fatto il lavoro: a = a.Replace ("", "+");
Peter

1
@Code Sherpa: in questo caso, la scelta migliore è urlencode prima di inviare la stringa e urldecode al ricevimento. Altrimenti se un altro carattere significativo dell'URL entra nella tua stringa, dovrai aggiungere un'altra Replacedichiarazione. La codifica è una tuta che ti protegge a prescindere.
Matt Ellen

6
Non è necessario UrlDecode la stringa al ricevimento poiché i parametri della richiesta sono già UrlDecoded da ASP.Net. Dovresti comunque UrlEncode durante l'invio.
bleeeah

Oppure, se volete una versione in linea: a = a + new string('=', (4 - a.Length % 4) % 4). Esempio per decodificare 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
"Non devi UrlDecode" - QUESTO! Passando attraverso il mio codice ho potuto vedere che il parametro era già decodificato, il problema ero io che lo UrlDecodeeseguivo attraverso la rimozione dei caratteri. Grazie @ MattEllen
GJKH

30

La mia ipotesi è che devi semplicemente codificare in URL la tua stringa Base64 quando la includi nella querystring.

La codifica Base64 utilizza alcuni caratteri che devono essere codificati se fanno parte di una stringa di query (vale a dire +e /, e forse =anche). Se la stringa non è codificata correttamente, non sarai in grado di decodificarla correttamente all'altra estremità, da qui gli errori.

Puoi utilizzare il HttpUtility.UrlEncodemetodo per codificare la tua stringa 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>";

Grazie. Ho appena provato il tuo suggerimento Luke ma non ha funzionato :(.
Peter

@ Sherpa - Continua a lavorarci, il problema è quasi certamente con i =caratteri finali .

Luke - Ho la sensazione che tu abbia ragione. Lo proverò a casa. Grazie mille. Cordiali saluti, ho aggiunto l'aspetto della stringa nella mia casella di posta hotmail nel mio post originale.
Peter

zio Brad ha ragione, ho avuto lo stesso problema la scorsa settimana e il problema era un carattere "=" finale ._.
Marcote

10

Non sono abbastanza rispettabile per votare o commentare ancora, ma la risposta di LukeH è stata perfetta per me.

Poiché la crittografia AES è lo standard da utilizzare ora, produce una stringa base64 (almeno tutte le implementazioni di crittografia / decrittografia che ho visto). Questa stringa ha una lunghezza in multipli di 4 (string.length% 4 = 0)

Le stringhe che stavo ottenendo contenevano + e = all'inizio o alla fine, e quando lo concateni nella stringa di query di un URL, sembrerà corretto (ad esempio, in un'e-mail che generi), ma quando il collegamento è seguito e il La pagina .NET lo riceve e lo inserisce in this.Page.Request.QueryString, quei caratteri speciali saranno spariti e la lunghezza della stringa non sarà in un multiplo di 4.

Poiché sono caratteri speciali nella PARTE ANTERIORE della stringa (es: +), così come = alla fine, non puoi semplicemente aggiungere un po 'di = per compensare la differenza mentre stai alterando il testo cifrato in un modo che non non corrisponde a ciò che era effettivamente nella stringa di query originale.

Quindi, avvolgere il testo cifrato con HttpUtility.URLEncode (non HtmlEncode) trasforma i caratteri non alfanumerici in modo da garantire che .NET li analizzi nuovamente nel loro stato originale quando viene interpretato nella raccolta di stringhe di query.

La cosa buona è che dobbiamo solo eseguire l'URLEncode durante la generazione della stringa di query per l'URL. Sul lato in entrata, viene automaticamente ritradotto nel valore di stringa originale.

Ecco un po 'di codice di esempio

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

6

La mia ipotesi iniziale senza conoscere i dati sarebbe che UserNameToVerify non è un multiplo di 4 di lunghezza. Controlla FromBase64String su msdn .

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

Grazie SwDevMan81. Esco solo ora dal lavoro ma ci proverò più tardi stasera Grazie per l'aiuto.
Peter

Nessun problema, la soluzione sarebbe
riempire

Grazie ancora SwDevMan81. Lo darò un'occhiata. Ho pubblicato UserNameToVeryify nel mio post originale (FYI). OK ... ora devo davvero andare o finirò nei guai con il vero capo :)
Peter

Sembra che questo post potrebbe aiutare pure: stackoverflow.com/questions/1392970/...
SwDevMan81

1

La stringa crittografata aveva due caratteri speciali +e= .

Il segno '+' dava l'errore, quindi la soluzione di seguito ha funzionato bene:

//replace + sign

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

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

O

//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); 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.