[英]base64.decode: Invalid encoding before padding
我正在做一個 flutter 項目,目前我嘗試使用 base64.decode() 方法解碼的一些字符串出現錯誤。 我創建了一個簡短的飛鏢代碼,可以重現我在使用特定字符串時遇到的問題:
import 'dart:convert';
void main() {
final message = 'RU5UUkVHQUdSQVRJU1==';
print(utf8.decode(base64.decode(message)));
}
我收到以下錯誤消息:
Uncaught Error: FormatException: Invalid encoding before padding (at character 19)
RU5UUkVHQUdSQVRJU1==
我試過用 JavaScript 解碼相同的字符串並且它工作正常。 如果有人能解釋為什么我會收到此錯誤,並可能向我展示解決方案,我會很高興。 謝謝。
Base64 編碼將二進制數據分解為 3 個完整字節的 6 位段,並將這些段表示為 ASCII 標准中的可打印字符。 它基本上分兩步完成。
第一步是將二進制字符串分解為 6 位塊。 Base64 僅使用 6 位(對應於 2^6 = 64 個字符)來確保編碼數據可打印且可讀。 沒有使用 ASCII 中可用的特殊字符。
64 個字符(因此命名為 Base64)是 10 個數字、26 個小寫字符、26 個大寫字符以及加號 (+) 和正斜杠 (/)。 還有一個第 65 個字符稱為填充,即等號 (=)。 當二進制數據的最后一段不包含完整的 6 位時使用此字符
所以 RU5UUkVHQUdSQVRJU1== 不遵循編碼模式。
出於某種原因, dart:convert
的base64.decode
在用=
填充的字符串上阻塞,並出現“填充錯誤前的無效編碼”。 即使您使用包自己的填充方法base64.normalize
會使用正確的填充字符=
填充字符串,也會發生這種情況。
=
確實是 base64 編碼的正確填充字符。 當輸入組中可用的位數少於 24 位時,它用於填充 base64 字符串。 請參閱RFC 4648,第 4 節。
但是, RFC 4648 第 5 節是 Urls 的 base64 編碼方案,它使用下划線字符_
作為填充而不是=
來確保 Url 安全。
使用_
作為填充字符將導致base64.decode
解碼無誤。
為了進一步將生成的字節列表解碼為 Utf8,您需要刪除填充字節,否則您將收到“無效的 UTF-8 字節”錯誤。
請參閱下面的代碼。 這是與工作 dartpad.dev 示例相同的代碼。
import 'dart:convert';
void main() {
//String message = 'RU5UUkVHQUdSQVRJU1=='; //as of dart 2.18.2 this will generate an "invalid encoding before padding" error
//String message = base64.normalize('RU5UUkVHQUdSQVRJU1'); // will also generate same error
String message = 'RU5UUkVHQUdSQVRJU1';
print("Encoded String: $message");
print("Decoded String: ${decodeB64ToUtf8(message)}");
}
decodeB64ToUtf8(String message) {
message =
padBase64(message); // pad with underline => ('RU5UUkVHQUdSQVRJU1__')
List<int> dec = base64.decode(message);
//remove padding bytes
dec = dec.sublist(0, dec.length - RegExp(r'_').allMatches(message).length);
return utf8.decode(dec);
}
String padBase64(String rawBase64) {
return (rawBase64.length % 4 > 0)
? rawBase64 += List.filled(4 - (rawBase64.length % 4), "_").join("")
: rawBase64;
}
根據RFC 4648 ,字符串RU5UUkVHQUdSQVRJU1==
不是兼容的 base 64 編碼,在第 3.5 節“規范編碼”中指出:
base 64 和 base 32 編碼中的填充步驟如果實施不當,可能會導致編碼數據發生不重要的更改。 例如,如果輸入只是 base 64 編碼的一個八位位組,則使用第一個符號的所有六位,但僅使用下一個符號的前兩位。 這些填充位必須由符合標准的編碼器設置為零,這在下面的填充描述中有描述。 如果此屬性不成立,則不存在基本編碼數據的規范表示,並且多個基本編碼字符串可以解碼為相同的二進制數據。 如果此屬性(以及本文檔中討論的其他屬性)成立,則可以保證規范編碼。
在某些環境中,更改是關鍵的,因此如果填充位未設置為零,解碼器可以選擇拒絕編碼。 引用此的規范可能會強制執行特定行為。
(強調已添加。)
在這里,我們將手動完成 base 64 解碼過程。
獲取編碼字符串RU5UUkVHQUdSQVRJU1==
並從 base 64 字符集執行映射(在上述 RFC 的“表 1:The Base 64 Alphabet”中給出),我們有:
R U 5 U U k V H Q U d S Q V R J U 1 = =
010001 010100 111001 010100 010100 100100 010101 000111 010000 010100 011101 010010 010000 010101 010001 001001 010100 110101 ______ ______
(使用__
來表示填充字符)。
現在,將這些按 8 個而不是 6 個分組,我們得到
01000101 01001110 01010100 01010010 01000101 01000111 01000001 01000111 01010010 01000001 01010100 01001001 01010011 0101____ ________
E N T R E G A G R A T I S P
重要的部分在最后,那里有一些非零位,然后是填充。 Dart 實現正確地確定提供的填充沒有意義,前提是前一個字符的最后四位不解碼為零。
因此, RU5UUkVHQUdSQVRJU1==
的解碼是不明確的。 是ENTREGAGRATIS
還是ENTREGAGRATISP
? 這正是 RFC 聲明“這些填充位必須由符合標准的編碼器設置為零”的原因。
事實上,正因為如此,我認為將RU5UUkVHQUdSQVRJU1==
解碼為ENTREGAGRATIS
而沒有抱怨的實現是有問題的,因為它會默默地丟棄非零位。
ENTREGAGRATIS 的 RFC 兼容編碼是ENTREGAGRATIS
RU5UUkVHQUdSQVRJUw==
。
ENTREGAGRATISP 的 RFC 兼容編碼是ENTREGAGRATISP
RU5UUkVHQUdSQVRJU1A=
。
這進一步突出了您輸入RU5UUkVHQUdSQVRJU1==
的歧義,兩者都不匹配。
我建議您檢查您的編碼器以確定它為什么向您提供不兼容的編碼,並確保您不會因此丟失信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.