1. ภาพรวม
1.Base64 คืออะไร:
Base64 เป็นหนึ่งในวิธีการเข้ารหัสที่ใช้กันทั่วไปในการส่งรหัสไบต์ 8 บิตบนอินเทอร์เน็ต คุณสามารถตรวจสอบ RFC2045~RFC2049 ซึ่งมีข้อกำหนดเฉพาะโดยละเอียดสำหรับ MIME การเข้ารหัส Base64 สามารถใช้เพื่อส่งข้อมูลการระบุตัวตนที่ยาวขึ้นในสภาพแวดล้อม HTTP ตัวอย่างเช่น ในระบบ Java Persistence Hibernate นั้น Base64 ใช้เพื่อเข้ารหัสตัวระบุที่ไม่ซ้ำกันแบบยาว (โดยปกติคือ UUID 128 บิต) ลงในสตริง ซึ่งใช้เป็นพารามิเตอร์ในรูปแบบ HTTP และ HTTP GET URL ในแอปพลิเคชันอื่นๆ มักจำเป็นต้องเข้ารหัสข้อมูลไบนารี่ให้อยู่ในรูปแบบที่เหมาะสมสำหรับการวางตำแหน่งใน URL (รวมถึงฟิลด์แบบฟอร์มที่ซ่อนอยู่) ในขณะนี้ การใช้การเข้ารหัส Base64 ไม่เพียงแต่สั้นลงเท่านั้น แต่ยังอ่านไม่ได้ด้วย กล่าวคือ ข้อมูลที่เข้ารหัสจะไม่สามารถมองเห็นได้ด้วยตาเปล่าโดยตรง
2. บทนำ:
Standard Base64 ไม่เหมาะสำหรับการส่งสัญญาณโดยตรงใน URL เนื่องจากตัวเข้ารหัส URL จะเปลี่ยนอักขระ "/" และ "+" ใน Base64 มาตรฐานเป็นรูปแบบ "%XX" และอักขระ "%" เหล่านี้จะมีอยู่เมื่อเข้าสู่ฐานข้อมูล จำเป็นต้องมีการแปลงเพิ่มเติมเนื่องจากมีการใช้เครื่องหมาย "%" เป็นอักขระตัวแทนใน ANSI SQL
เพื่อแก้ไขปัญหานี้ คุณสามารถใช้การเข้ารหัส Base64 ที่ปรับปรุงแล้วสำหรับ URL ได้ ซึ่งไม่มีเครื่องหมาย '=' ที่ส่วนท้าย และเปลี่ยน "+" และ "/" ใน Base64 มาตรฐานเป็น "*" และ "-" ตามลำดับ ซึ่งช่วยลดความจำเป็นในการแปลงระหว่างการเข้ารหัส URL การถอดรหัส และการจัดเก็บฐานข้อมูล หลีกเลี่ยงการเพิ่มความยาวของข้อมูลที่เข้ารหัสในกระบวนการ และรวมรูปแบบของตัวระบุออบเจ็กต์ในฐานข้อมูล แบบฟอร์ม ฯลฯ
นอกจากนี้ยังมีตัวแปร Base64 ที่ปรับปรุงแล้วสำหรับนิพจน์ทั่วไป ซึ่งเปลี่ยน "+" และ "/" เป็น "!" และ "-" เนื่องจาก "+", "*" และ " Both [" และ "]" อาจมีความหมายพิเศษ การแสดงออกปกติ
นอกจากนี้ยังมีตัวแปรที่เปลี่ยน "+/" เป็น "_-" หรือ "._" (ใช้เป็นชื่อตัวระบุในภาษาการเขียนโปรแกรม) หรือ ".-" (ใช้สำหรับ Nmtokens ใน XML) หรือแม้แต่ "_ :" (สำหรับชื่อ ในรูปแบบ XML)
Base64 จำเป็นต้องแปลงทุก ๆ สามไบต์ 8Bit เป็นสี่ไบต์ 6Bit (3*8 = 4*6 = 24) จากนั้นเพิ่ม 0 บิตสูงสองตัวลงใน 6Bit เพื่อสร้างไบต์ 8Bit สี่ไบต์ กล่าวคือ สตริงที่แปลงแล้วตามทฤษฎีจะเป็น ยาวกว่าเดิม 1/3
3. กฎ:
กฎเกี่ยวกับการเข้ารหัสนี้:
1.แปลง 3 ตัวอักษรเป็น 4 ตัวอักษร..
②.เพิ่มอักขระขึ้นบรรทัดใหม่ทุกๆ 76 ตัวอักษร..
3. ตัวยุติสุดท้ายจะต้องได้รับการประมวลผลด้วย
นั่นมันไม่เป็นนามธรรมเกินไปเหรอ? ไม่ต้องกังวล ลองดูตัวอย่าง:
ก่อนการแปลง: aaaaaabb ccccdddd eeffffff
หลังการแปลง: 00aaaaaa 00bbcccc 00ddddee 00ffffff
มันควรจะชัดเจนใช่ไหม? สามไบต์บนเป็นข้อความต้นฉบับ และสี่ไบต์ล่างเป็นการเข้ารหัส Base64 ที่แปลงแล้ว โดยสองบิตแรกเป็น 0
หลังจากการแปลง เราใช้ตารางโค้ดเพื่อรับสตริงที่เราต้องการ (นั่นคือการเข้ารหัส Base64 สุดท้าย)
2. ตัวอย่างโค้ดการใช้งาน Java:
คลาสสุดท้ายสาธารณะ { int สุดท้ายคงที่ BASELENGTH = 255; int ส่วนตัวสุดท้ายแบบคงที่ SIXTEENBIT = 16; = 6; int ส่วนตัวสุดท้ายคงที่ = 4; int SIGN = -128; PAD ส่วนตัวแบบคงที่สุดท้าย = '='; บูลีนส่วนตัวแบบคงที่สุดท้าย fDebug = false; ไบต์ส่วนตัวแบบคงที่สุดท้าย [LOOKUPLENGTH]; ]; คงที่ { สำหรับ (int i = 0; i < BASELENGTH; i++) { base64Alphabet[i] = -1; } สำหรับ (int i = 'Z'; i >= 'A'; i--) { base64Alphabet[i] = (ไบต์) (i - 'A' } สำหรับ (int i = 'z'; i >= 'a'; i--) { base64Alphabet[i] = (ไบต์) (i - 'a' + 26) สำหรับ (int i = '9'; >= '0'; i--) { base64Alphabet['/'] = 63; int i = 0; i <= 25; i++) lookUpBase64Alphabet[i] = (ถ่าน) ('A' + i); สำหรับ (int i = 26, j = 0; i <= 51; i++, j++) lookUpBase64Alphabet[i] = (char) ('a' + j); สำหรับ (int i = 52, j = 0; i <= 61; i++, j++ ) lookUpBase64Alphabet[i] = (ถ่าน) ('0' + j); lookUpBase64Alphabet[62] = (ถ่าน) '+'; lookUpBase64Alphabet[63] = (char) '/'; } บูลีนแบบคงที่ที่ได้รับการป้องกัน isWhiteSpace (octect ถ่าน) { return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); } isPad บูลีนแบบคงที่ (char octect) { return (octect == PAD); } บูลีนคงที่ที่ได้รับการป้องกัน isData (ถ่าน octect) { กลับ (base64Alphabet [octect] ! = -1); } บูลีนคงที่ที่ได้รับการป้องกัน isBase64 (ถ่าน octect) { กลับ (isWhiteSpace (octect) || isPad (octect) || isData (octect) } /** * เข้ารหัสเลขฐานสิบหกลงใน Base64 * * @param binaryData * อาร์เรย์ที่มี binaryData * @return เข้ารหัส Base64 */ เข้ารหัสสตริงสาธารณะ (ไบต์ [] binaryData) { ถ้า (binaryData == null) ส่งคืน null; int lengthDataBits = binaryData.length * EIGHTBIT; if (lengthDataBits == 0) { return "" ; } int น้อยกว่า 24 บิต = lengthDataBits % TWENTYFOURBITGROUP; TWENTYFOURBITGROUP; int numberQuartet = น้อยกว่า 24 บิต != 0 ? numberTriplets; int numberLines = (numberQuartet - 1) / 19 + 1; char encodedData = null; = 0, ลิตร = 0, b1 = 0, b2 = 0, b3 = 0; int encodedIndex = 0; int dataIndex = 0; if (fDebug) { System.out.println("จำนวน triplets = " + numberTriplets); 0; line < numberLines - 1; line++) { สำหรับ (int quartet = 0; quartet < 19; quartet++) { b1 = binaryData[dataIndex++]; b2 = binaryData[dataIndex++]; + b3); } l = (ไบต์) (b2 & 0x0f); k = (ไบต์) (b1 & 0x03); ((b1 & ลงชื่อเข้าใช้) == 0) ? (ไบต์) (b1 >> 2) : (ไบต์) ((b1) >> 2 ^ 0xc0); ไบต์ val2 = ((b2 & ลงชื่อเข้าใช้) == 0) ? ไบต์) (b2 >> 4) : (ไบต์) ((b2) >> 4 ^ 0xf0); ไบต์ val3 = ((b3 & SIGN) == 0) ? (ไบต์) (b3 >> 6) : (ไบต์) ((b3) >> 6 ^ 0xfc); if (fDebug) { System.out.println("val2 = " + val2); println("k4 = " + (k << 4))); System.out.println("vak = " + (val2 | (k << 4))); encodedData [encodedIndex ++] = lookUpBase64Alphabet [val1]; encodedData [encodedIndex ++] = lookUpBase64Alphabet [val2 |. (k << 4)]; encodedData [encodedIndex ++] = lookUpBase64Alphabet[b3 & 0x3f]; i++; } encodedData[dataIndex++]; b3 = binaryData[dataIndex++]; (fDebug) { System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3); } l = (ไบต์) (b2 & 0x0f); k = (ไบต์) (b1 & 0x03); ไบต์ val1 = ((b1 & SIGN) = = 0) ? (ไบต์) (b1 >> 2) : (ไบต์) ((b1) >> 2 ^ 0xc0); ไบต์ val2 = ((b2 & ลงชื่อเข้าใช้) == 0) ? (ไบต์) (b2 >> 4) : (ไบต์) ((b2) >> 4 ^ 0xf0); ไบต์ val3 = ((b3 & ลงชื่อเข้าใช้) == 0) ? ไบต์) (b3 >> 6) : (ไบต์) ((b3) >> 6 ^ 0xfc); if (fDebug) { System.out.println("val2 = " + val2); System.out.println("k4 = " + (k << 4)); System.out.println("vak = " + (val2 | (k < < 4))); } encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; lookUpBase64Alphabet[val2 |. (k << 4)]; lookUpBase64Alphabet[(l << 2) | val3]; กลุ่มบิตถ้า (น้อยกว่า 24 บิต == EIGHTBIT) { b1 = binaryData[dataIndex]; k = (ไบต์) (b1 & 0x03); if (fDebug) { System.out.println("b1=" + b1); System.out.println("b1<<2 = " + (b1 >> 2)); } ไบต์ val1 = ((b1 & SIGN) == 0) ? (ไบต์) (b1 >> 2) : (ไบต์) ((b1) >> 2 ^ 0xc0); encodedData[encodedIndex++] = PAD; encodedData[encodedIndex++] = PAD; ถ้า (น้อยกว่า 24 บิต == SIXTEENBIT) { b1 = binaryData[dataIndex]; b2 = binaryData[dataIndex + 1]; l = (ไบต์) (b2 & 0x0f); k = (ไบต์) (b1 & 0x03); == 0) ? (ไบต์) (b1 >> 2) : (ไบต์) ((b1) >> 2 ^ 0xc0); ไบต์ val2 = ((b2 & SIGN) == 0) ? (ไบต์) (b2 >> 4) : (ไบต์) ((b2) >> 4 ^ 0xf0); ; encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | 4)]; encodedData[encodedIndex] = lookUpBase64Alphabet[l << 2]; encodedData[encodedIndex++] = PAD; } encodedData[encodedIndex] = 0xa; ส่งคืนสตริงใหม่ (encodedData); } /** * @param binaryData * อาร์เรย์ไบต์ที่มีข้อมูล Base64 * @return Array บรรจุ ถอดรหัสข้อมูล */ ไบต์สาธารณะ [] ถอดรหัส (เข้ารหัสสตริง) { ถ้า (เข้ารหัส == null) ส่งคืน null; char [] base64Data = encoded.toCharArray (); // ลบช่องว่าง int len = ลบ WhiteSpace (base64Data ); if (len % FOURBYTE != 0) { return null;// ควรหารด้วยสี่ลงตัว } int numberQuadruple = (len / สี่ไบต์); ถ้า (numberQuadruple == 0) ส่งคืนไบต์ใหม่[0]; ไบต์ถอดรหัสข้อมูล[] = null; ไบต์ b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, อักขระ d1 = 0, d2 = 0, d3 = 0, d4 = 0; int i = 0; = 0; int dataIndex = 0; decodedData = ไบต์ใหม่ [(numberQuadruple) * 3]; สำหรับ (; i < numberQuadruple - 1; i++) { if (!isData((d1 = base64Data[dataIndex++])) || !isData ((d2 = base64Data[dataIndex++])) ||. !isData((d3 = base64Data[dataIndex++])) ||. !isData((d4 = base64Data[dataIndex++]))) คืนค่า null;// หากพบว่า "ไม่มีข้อมูล" ให้คืนค่า null b1 = base64Alphabet[d1]; = base64Alphabet[d3]; b4 = base64Alphabet[d4]; = (ไบต์) (b1 << 2 | b2 >> 4); decodedData[encodedIndex++] = (ไบต์) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); encodedIndex++] = (ไบต์) (b3 << 6 | b4); } ถ้า (!isData((d1 = base64Data[dataIndex++])) ||. !isData((d2 = base64Data[dataIndex++]))) { return null;// ถ้าพบว่า "ไม่มีข้อมูล" ให้คืนค่า null } b1 = base64Alphabet[d1]; ; d3 = base64Data[dataIndex++]; (!isData((d3)) || !isData((d4))) {// ตรวจสอบว่าเป็นอักขระ PAD หรือไม่ ถ้า (isPad(d3) && isPad(d4)) { // สอง PAD เช่น 3c[Pad][ Pad] if ((b2 & 0xf) != 0)// 4 บิตสุดท้ายควรเป็นศูนย์ส่งคืน null; byte[] tmp = new byte[i * 3 + 1]; System.arraycopy(decodedData, 0, tmp, 0, i * 3); tmp[encodedIndex] = (ไบต์) (b1 << 2 | b2 >> } อื่น ๆ ถ้า (!isPad( d3) && isPad(d4)) { // หนึ่ง PAD เช่น 3cQ[Pad] b3 = base64Alphabet[d3]; if ((b3 & 0x3) != 0)// 2 บิตสุดท้ายควรเป็นศูนย์ส่งคืน null; ไบต์ [] tmp = ไบต์ใหม่ [i * 3 + 2]; tmp, 0, i * 3); tmp[encodedIndex++] = (ไบต์) (b1 << 2 | b2 >> 4); tmp[encodedIndex] = (ไบต์) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); return tmp; } else { return null;// ข้อผิดพลาดเช่น " 3c[Pad]r", "3cdX", "3cXd", "3cXX" // โดยที่ X ไม่ใช่ข้อมูล } } else { // ไม่มี PAD เช่น 3cQl b3 = base64Alphabet[d3]; b4 = base64Alphabet[d4]; decodedData[encodedIndex++] = (ไบต์) (b1 << 2 | b2 >> 4); << 4) |. ((b3 >> 2) & 0xf)); decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); } return decodedData; } /** * ลบ WhiteSpace ออกจาก MIME ที่มีข้อมูล Base64 ที่เข้ารหัส * อาร์เรย์ไบต์ของข้อมูล base64 (พร้อม WS ) * @return ความยาวใหม่ */ protected static int RemoveWhiteSpace(char[] data) { if (data == null) return 0; // นับตัวอักษรที่ไม่ใช่ ช่องว่าง int newSize = 0; int len = data.length; for (int i = 0; i < len; i++) { if (!isWhiteSpace(data[i])) data[newSize++] = data[i]; } ส่งคืน newSize; } โมฆะสาธารณะ main(String[] args) { System.out.println(encode("People's Republic of China".getBytes()) }}