Nieprawidłowa długość tablicy znaków Base-64

91

Jak mówi tytuł, otrzymuję:

Nieprawidłowa długość tablicy znaków Base-64.

Czytałem o tym problemie tutaj i wydaje się, że sugestia jest taka, aby przechowywać ViewState w SQL, jeśli jest duży. Używam kreatora z dużą ilością danych, więc prawdopodobnie mój ViewState jest duży. Ale zanim przejdę do rozwiązania „przechowuj w bazie danych”, może ktoś może rzucić okiem i powiedzieć mi, czy mam inne opcje?

Konstruuję wiadomość e-mail do dostarczenia za pomocą poniższej metody:

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

Metoda Encrypt wygląda następująco:

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

Oto jak wygląda HTML w hotmailu:

Kliknij poniższy link lub wklej go do przeglądarki, aby zweryfikować swoje konto e-mail.

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

Po stronie odbierającej strona VerifyEmail.aspx.cs zawiera wiersz:

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

Oto metoda pobierająca UserNameToVerify:

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

A oto metoda GetQueryStringValue:

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

A metoda deszyfrowania wygląda następująco:

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

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

Czy ten błąd można naprawić za pomocą poprawki kodu, czy też muszę przechowywać ViewState w bazie danych?

Piotr
źródło

Odpowiedzi:

205

Długość ciągu zakodowanego algorytmem base64 jest zawsze wielokrotnością liczby 4. Jeśli nie jest wielokrotnością liczby 4, to =znaki są dodawane, aż tak się stanie. Ciąg zapytania formularza ?name=valuema problemy, gdy valuezawiera =znaki (niektóre z nich zostaną usunięte, nie pamiętam dokładnego zachowania). Możesz być w stanie uciec z dołączeniem odpowiedniej liczby =znaków przed wykonaniem dekodowania base64.

Edytuj 1

Może się okazać, że wartość UserNameToVerifybyła "+"„s zmieniono na " "” s więc może trzeba coś zrobić tak:

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

Powinno to uzyskać odpowiednią długość;

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

Oczywiście dzwonienie UrlEncode(jak w odpowiedzi LukeHa) powinno sprawić, że to wszystko będzie dyskusyjne.

Vadim Ovchinnikov
źródło
9
Dzięki Brad - Właściwie to ten mały fragment kodu wykonał zadanie: a = a.Replace ("", "+");
Peter
1
@Code Sherpa: w takim przypadku najlepszym wyborem jest kod urlencode przed wysłaniem ciągu i kod urldecode przy odbiorze. W przeciwnym razie, jeśli inny znaczący znak adresu URL dostanie się do twojego ciągu, będziesz musiał dodać kolejną Replaceinstrukcję. Encoding to kombinezon, który chroni Cię niezależnie.
Matt Ellen,
5
Nie należy kodować UrlDecode swojego ciągu przy odbiorze, ponieważ parametry żądania są już UrlDecoded przez ASP.Net. Podczas wysyłania należy jednak UrlEncode.
bleeeah
Lub jeśli chcesz wersję inline: a = a + new string('=', (4 - a.Length % 4) % 4). Przykład dekodowania RFC 4648 bezpiecznego adresu URL 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
„Nie musisz do UrlDecode” - TO! Przechodząc przez mój kod, widziałem, że parametr został już zdekodowany, problem polegał na tym, że uruchomiłem go, przez UrlDecodeco usunąłem znaki. Dzięki @MattEllen
GJKH
30

Domyślam się, że wystarczy zakodować adres URL ciąg Base64, gdy umieścisz go w kwerendzie.

Kodowanie Base64 wykorzystuje pewne znaki, które muszą być zakodowane, jeśli są częścią kwerendy (mianowicie +i /, a może =też). Jeśli ciąg nie jest poprawnie zakodowany, nie będzie można go pomyślnie zdekodować na drugim końcu, stąd błędy.

Możesz użyć HttpUtility.UrlEncodemetody do zakodowania ciągu 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
źródło
Dzięki. Właśnie wypróbowałem twoją sugestię Luke, ale to nie zadziałało :(.
Peter,
@Sherpa - Pracuj dalej, problem prawie na pewno dotyczy =znaków końcowych .
Luke - mam wrażenie, że masz rację. Spróbuję tego w domu. Dzięki pakietowi. Do Twojej wiadomości - dodałem, jak wygląda ciąg w mojej skrzynce odbiorczej Hotmail w moim oryginalnym poście.
Peter
wujek Brad ma rację, miałem ten sam problem w zeszłym tygodniu i problemem był końcowy znak „=” ._.
Marcote
10

Nie mam jeszcze wystarczającej reputacji, aby głosować za lub komentować, ale odpowiedź LukeHa była dla mnie trafna.

Ponieważ szyfrowanie AES jest obecnie standardem, tworzy ciąg base64 (przynajmniej wszystkie implementacje szyfrowania / deszyfrowania, które widziałem). Ten łańcuch ma długość będącą wielokrotnością 4 (string.length% 4 = 0)

Ciągi, które otrzymałem, zawierały + i = na początku lub na końcu, a kiedy po prostu połączysz je w kwerendę adresu URL, będzie wyglądać dobrze (na przykład w wygenerowanej wiadomości e-mail), ale gdy podążasz za linkiem i Strona .NET odbiera go i umieszcza w this.Page.Request.QueryString, te znaki specjalne zostaną usunięte, a długość ciągu nie będzie wielokrotnością 4.

Ponieważ znaki specjalne znajdują się na PRZODU ciągu (np. +), A także = na końcu, nie możesz po prostu dodać znaku =, aby nadrobić różnicę, ponieważ zmieniasz szyfrogram w sposób, który tego nie robi nie pasuje do tego, co faktycznie było w oryginalnym querystring.

Tak więc zawijanie szyfrowanego tekstu za pomocą HttpUtility.URLEncode (nie HtmlEncode) przekształca znaki niealfanumeryczne w sposób, który zapewnia, że ​​.NET analizuje je z powrotem do ich pierwotnego stanu, gdy zostanie zinterpretowany w kolekcji querystring.

Dobrą rzeczą jest to, że musimy wykonać kod URLEncode tylko podczas generowania zapytania dotyczącego adresu URL. Po stronie przychodzącej jest automatycznie tłumaczona z powrotem na oryginalną wartość ciągu.

Oto przykładowy kod

string cryptostring = MyAESEncrypt(MySecretString);
string URL = WebFunctions.ToAbsoluteUrl("~/ResetPassword.aspx?RPC=" + HttpUtility.UrlEncode(cryptostring));
Ken Forslund
źródło
6

Moje początkowe przypuszczenie bez znajomości danych byłoby takie, że UserNameToVerify nie jest wielokrotnością 4 długości. Sprawdź FromBase64String na msdn.

// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");
SwDevMan81
źródło
Dzięki SwDevMan81. Właśnie wychodzę z pracy, ale spróbuję tego później wieczorem. Dzięki za pomoc.
Peter
Nie ma problemu, rozwiązaniem byłoby
uzupełnienie
Jeszcze raz dziękuję SwDevMan81. Rzucę okiem na to. Umieściłem UserNameToVeryify w moim oryginalnym poście (FYI). OK ... teraz naprawdę muszę już iść lub będę miał kłopoty z prawdziwym szefem :)
Peter
Wygląda na to, że ten post również może pomóc: stackoverflow.com/questions/1392970/…
SwDevMan81
1

Zaszyfrowany ciąg miał dwa znaki specjalne +i= .

Znak '+' podawał błąd, więc poniższe rozwiązanie działało dobrze:

//replace + sign

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

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

LUB

//encode special charactes 

encryted_string = HttpUtility.UrlEncode(encryted_string);

//then pass it to the decryption process
...
Vikrant
źródło
0
    string stringToDecrypt = CypherText.Replace(" ", "+");
    int len = stringToDecrypt.Length;
    byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt); 
Kod
źródło