Недійсна довжина масиву символів Base-64


91

Як сказано в назві, я отримую:

Недійсна довжина масиву символів Base-64.

Я читав про цю проблему тут, і, схоже, пропонується зберігати ViewState у SQL, якщо вона велика. Я використовую майстра з великою кількістю збору даних, тому, швидше за все, мій ViewState великий. Але, перш ніж я звернуся до рішення "store-in-DB", можливо, хтось може поглянути і сказати мені, чи є у мене інші варіанти?

Я складаю електронний лист для доставки, використовуючи наведений нижче спосіб:

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

Метод шифрування виглядає так:

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

Ось як виглядає HTML у Hotmail:

Натисніть на посилання нижче або вставте його у браузер, щоб підтвердити свій рахунок електронної пошти.

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

На кінці отримання сторінка VerifyEmail.aspx.cs має рядок:

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

Ось спосіб отримання UserNameToVerify:

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

І ось метод GetQueryStringValue:

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

А метод розшифровки виглядає так:

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

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

Чи можна цю помилку виправити за допомогою виправлення коду, чи я повинен зберігати ViewState у базі даних?

Відповіді:


205

Довжина кодованого рядка base64 завжди кратна 4. Якщо вона не кратна 4, тоді =символи додаються, поки вона не буде. Рядок запиту форми ?name=valueмає проблеми, коли valueмістять =символи (деякі з них будуть видалені, я не пам’ятаю точної поведінки). Можливо, вам вдасться уникнути, додавши потрібну кількість =символів, перш ніж виконувати декодування base64.

Редагувати 1

Ви можете виявити, що значення UserNameToVerifyhas було "+"змінено на " "'s, тому вам може знадобитися зробити щось подібне:

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

Це має отримати правильну довжину;

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

Звичайно, дзвінок UrlEncode(як у відповіді Лукаха) повинен зробити це все спірним.


9
Дякую, Бред - насправді цю невелику частину коду зробив: a = a.Replace ("", "+");
Пітер

1
@Code Sherpa: якщо це так, найкращим вибором є urlencode перед відправкою рядка та urldecode при отриманні. В іншому випадку, якщо до вашого рядка потрапляє інший значущий символ url, вам доведеться додати ще одне Replaceтвердження. Кодування - це комбінезон, який захищає вас незалежно.
Matt Ellen,

5
Ви не повинні UrlDecode ваш рядок при отриманні, оскільки параметри запиту вже UrlDecoded ASP.Net. Однак слід надсилати UrlEncode.
bleeeah

Або , якщо ви хочете інлайн версії: a = a + new string('=', (4 - a.Length % 4) % 4). Приклад декодування RFC 4648, захищеного 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
"Ви не повинні UrlDecode" - ЦЕ! Перебираючи мій код, я бачив, що параметр вже розшифрований, проблема полягала в тому, що я його запустив, через UrlDecodeякий видаляв символи. Дякую @MattEllen
GJKH

30

Я припускаю, що вам просто потрібно закодувати URL-адресу рядка Base64, коли ви включаєте його в рядок запитів.

Base64 кодування використовує деякі символи , які повинні бути закодовані , якщо вони є частиною рядка запиту (а саме +та /, і , можливо , =теж). Якщо рядок неправильно закодований, ви не зможете його успішно декодувати на іншому кінці, звідси помилки.

Ви можете використовувати HttpUtility.UrlEncodeметод для кодування рядка 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>";

Дякую. Просто спробував вашу пропозицію Лука, але це не спрацювало :(.
Пітер,

@Sherpa - Продовжуйте працювати, проблема майже напевно полягає в кінцевих =символах.

Люк - У мене відчуття, що ти маєш рацію. Спробую це вдома. Дякую пачці. FYI - Я додав, як виглядає рядок, у моєму поштовому скриньці Hotmail у своєму початковому дописі.
Пітер

дядько Бред має рацію, у мене була та сама проблема минулого тижня, і проблема полягала в завершальному символі "=" ._.
Маркот

10

Я ще не є достатньо авторитетним, щоб проголосувати чи коментувати, але відповідь Лукаха для мене була спокійною.

Оскільки шифрування AES є стандартним для використання зараз, воно створює рядок base64 (принаймні всі реалізації шифрування / дешифрування, які я бачив). Цей рядок має довжину, кратну 4 (строка. Довжина% 4 = 0)

Рядки, які я отримував, містили + і = на початку або в кінці, і коли ви просто об'єднаєте це в рядок запиту URL-адреси, це буде виглядати правильно (наприклад, у електронному листі, який ви створюєте), але коли за посиланням переходить і Сторінка .NET отримує його і поміщає в this.Page.Request.QueryString, ці спеціальні символи зникнуть, а довжина вашого рядка не буде кратна 4.

Оскільки спеціальні символи є спереду рядка (наприклад: +), а також = в кінці, ви не можете просто додати деякі =, щоб заповнити різницю, оскільки ви змінюєте текст шифру таким чином, що не не відповідає тому, що насправді було у вихідному рядку запитів.

Отже, обгортання тексту шифрування за допомогою HttpUtility.URLEncode (а не HtmlEncode) перетворює нелітерально-цифрові символи таким чином, що забезпечує .NET аналізує їх назад у початковий стан при їх інтерпретації в колекцію запитів.

Хороша річ полягає в тому, що нам потрібно лише зробити код URLEn при генерації рядка запитів для URL-адреси. На вхідній стороні він автоматично перекладається назад у вихідне значення рядка.

Ось приклад коду

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

6

Моє початкове припущення, не знаючи даних, полягало б у тому, що UserNameToVerify не кратна 4 довжині. Перевірте FromBase64String на msdn .

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

Дякую SwDevMan81. Зараз покидаю роботу, але спробую це пізніше сьогодні ввечері. Спасибі за вашу допомогу.
Пітер

Немає проблем, виправлення
полягало б у набиванні

Ще раз спасибі SwDevMan81. Я подивлюсь на це. Я розмістив UserNameToVeryify у своєму початковому дописі (FYI). Добре ... тепер мені справді потрібно піти, або я зіткнусь із справжнім босом :)
Пітер,

Схоже , цей пост може допомогти також: stackoverflow.com/questions/1392970 / ...
SwDevMan81

1

Зашифрований рядок мав два спеціальні символи +та =.

Знак "+" давав помилку, тому рішення нижче працювало добре:

//replace + sign

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

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

АБО

//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); 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.