ความยาวไม่ถูกต้องสำหรับอาร์เรย์ถ่าน Base-64


93

ตามชื่อเรื่องฉันได้รับ:

ความยาวไม่ถูกต้องสำหรับอาร์เรย์ถ่าน 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 / บัญชี / VerifyEmail.aspx? a = YOHY57xYRENEOu3H + FGq1Rf09AZAI56EPjfwuK8XWKg =

ในปลายทางการรับหน้า VerifyEmail.aspx.cs มีบรรทัด:

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

นี่คือ getter สำหรับ 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 ไว้ในฐานข้อมูลหรือไม่

คำตอบ:


209

ความยาวของสตริงที่เข้ารหัส base64 เป็นผลคูณของ 4 เสมอหากไม่ใช่ผลคูณของ 4 =อักขระจะถูกต่อท้ายจนกว่าจะเป็น สตริงข้อความค้นหาของแบบฟอร์ม?name=valueมีปัญหาเมื่อvalueมีอักขระ (อักขระ=บางตัวจะหายไปฉันจำพฤติกรรมที่แน่นอนไม่ได้) คุณอาจสามารถต่อท้ายจำนวน=อักขระที่ถูกต้องก่อนทำการถอดรหัส base64

แก้ไข 1

คุณอาจพบว่าค่าของUserNameToVerifyhas "+"เปลี่ยนเป็น" "ของดังนั้นคุณอาจต้องทำสิ่งต่อไปนี้:

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

สิ่งนี้ควรได้รับความยาวที่เหมาะสม

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

แน่นอนการโทรUrlEncode(เช่นเดียวกับคำตอบของ LukeH) ควรทำให้ทั้งหมดนี้เป็นที่สงสัย


9
ขอบคุณแบรด - จริงๆแล้วมันเป็นโค้ดเล็กน้อยที่ใช้งานได้: a = a.Replace ("", "+");
ปีเตอร์

1
@Code Sherpa: หากเป็นเช่นนั้นทางเลือกที่ดีที่สุดของคุณคือ urlencode ก่อนส่งสตริงและ urldecode ในใบเสร็จรับเงิน มิฉะนั้นหากอักขระที่มีนัยสำคัญของ URL อื่นเข้าไปในสตริงของคุณคุณจะต้องเพิ่มReplaceคำสั่งอื่น การเข้ารหัสเป็นสิ่งที่ปกป้องคุณโดยไม่คำนึงถึง
Matt Ellen

6
คุณไม่ต้อง UrlDecode สตริงของคุณเมื่อได้รับเนื่องจากพารามิเตอร์คำขอเป็น UrlDecoded โดย ASP.Net แล้ว อย่างไรก็ตามคุณควร UrlEncode เมื่อส่ง
bleeeah

หรือหากคุณต้องการเวอร์ชันอินไลน์: a = a + new string('=', (4 - a.Length % 4) % 4). ตัวอย่างการถอดรหัส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
"คุณไม่ต้อง 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>";

ขอบคุณ. เพิ่งลองใช้คำแนะนำของคุณลุค แต่ไม่ได้ผล :(
ปีเตอร์

@ เชอร์ปา - ใช้งานได้ต่อไปปัญหาเกือบจะแน่นอนกับ=อักขระที่ต่อท้าย

ลุค - ฉันรู้สึกว่าคุณพูดถูก จะลองทำที่บ้าน ขอบคุณชุด FYI - ฉันได้เพิ่มลักษณะของสตริงในกล่องจดหมายของ Hotmail ในโพสต์เดิมของฉัน
ปีเตอร์

ลุงแบรดพูดถูกฉันมีปัญหาเดียวกันเมื่อสัปดาห์ที่แล้วและปัญหาคืออักขระต่อท้าย "=" ._
Marcote

10

ฉันยังไม่มีชื่อเสียงพอที่จะโหวตหรือแสดงความคิดเห็น แต่คำตอบของ LukeH นั้นตรงจุดสำหรับฉัน

เนื่องจากการเข้ารหัส AES เป็นมาตรฐานที่จะใช้ในขณะนี้จึงสร้างสตริง base64 (อย่างน้อยการใช้งานเข้ารหัส / ถอดรหัสทั้งหมดที่ฉันเคยเห็น) สตริงนี้มีความยาวเป็นทวีคูณของ 4 (string.length% 4 = 0)

สตริงที่ฉันได้รับมี + และ = ที่จุดเริ่มต้นหรือจุดสิ้นสุดและเมื่อคุณเชื่อมต่อสิ่งนั้นเข้ากับสตริงการสืบค้นของ URL ก็จะดูถูกต้อง (เช่นในอีเมลที่คุณสร้าง) แต่เมื่อลิงก์ถูกตามและ หน้า. NET รับมันและใส่ลงในสิ่งนี้ Page.Request.QueryString อักขระพิเศษเหล่านั้นจะหายไปและความยาวสตริงของคุณจะไม่อยู่ในผลคูณของ 4

เนื่องจากเป็นอักขระพิเศษที่ด้านหน้าของสตริง (เช่น: +) เช่นเดียวกับ = ในตอนท้ายคุณจึงไม่สามารถเพิ่ม = เพื่อสร้างความแตกต่างได้ในขณะที่คุณกำลังแก้ไขข้อความไซเฟอร์ในลักษณะที่ไม่ ไม่ตรงกับสิ่งที่เป็นจริงในสตริงการสืบค้นดั้งเดิม

ดังนั้นการตัดข้อความ cypher ด้วย HttpUtility.URLEncode (ไม่ใช่ HtmlEncode) จะแปลงอักขระที่ไม่ใช่ตัวอักษรและตัวเลขด้วยวิธีที่ทำให้มั่นใจได้ว่า. NET จะแยกวิเคราะห์กลับเข้าสู่สถานะเดิมเมื่อถูกแทรกเข้าไปในคอลเล็กชันสตริงการสืบค้น

สิ่งที่ดีคือเราต้องทำ URLEncode เมื่อสร้างสตริงการสืบค้นสำหรับ 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 ตอนนี้กำลังออกจากงาน แต่จะพยายามต่อไปในคืนนี้ ขอบคุณสำหรับความช่วยเหลือของคุณ.
ปีเตอร์

ไม่มีปัญหาการแก้ไขคือการเติมอักขระเพื่อให้ได้สตริงที่เป็นผลคูณของ 4
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
...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.