Die Base64-Codierung

Die Base64-Codierung dient der Darstellung binärer Daten als Text. Während in Binärdaten alle 256 in einem Byte darstellbaren Zeichen anzutreffen sind, dürfen in vielen Medien nur ein Teil dieser Zeichen vorkommen, beispielsweise nur druckbare Zeichen aus dem regulären (7-Bit) ASCII-Alphabet. Wenn eine Datei via E-Mail versendet werden soll, muß zum Beispiel häufig eine solche Codierung vorgenommen werden (wenn die Gegenstelle nicht ankündigt 8BITMIME-fähig zu sein).

Der Kerngedanke hinter der Base64-Codierung ist, jeweils 3 Byte (24 Bit) aus dem Eingabestrom in 4 Zeichen des Base64-Alphabets umzusetzen. Das Base64-Alphabet umfasst dabei genau 64 Zeichen, d.h. jedes Zeichen ist in der Lage 6 Bit Information darzustellen (womit die 24 Bit der Eingabe in 4 Zeichen der Ausgabe abbildbar sind). Diese Zeichen umfassen die Großbuchstaben A-Z, die Kleinbuchstaben a-z, die Ziffern 0 bis 9 sowie das Plus-Zeichen und den Schrägstrich. Für den Fall, daß die Eingabe kein Vielfaches von 3 Zeichen aufweist, und damit die Ausgabe kein Vielfaches von 4 Zeichen umfasst, gibt es das Padding-Zeichen '=', mit dem die Ausgabe wieder auf ein Vielfaches von 4 Zeichen aufgefüllt wird. Je nach Anzahl von Eingabezeichen bleiben entweder 8 Bit oder 16 Bit der Eingabe übrig, jedoch sind nur 6, 12, 18 und 24 Bit durch komplette Ausgabezeichen darstellbar. Damit die "angeknabberten" Zeichen richtig decodiert werden können, werden in der Eingabe fehlende Bits durch 0-Bits ersetzt. Die Anzahl der eingefügten Padding-Zeichen zeigt beim Decodieren an, ob in der Eingabe 8 (zwei Padding-Zeichen) oder 16 Bit (ein Padding-Zeichen) überzählig waren. Dadurch, daß 3 Byte in 4 Zeichen dargestellt werden, sind mit Base64 codierte Daten um 33% größer als die Rohdaten (um genau zu sein sogar etwas mehr, man muß wegen der Padding-Zeichen aufrunden, und es handelt sich um 4/3, also 33.33333...% mehr).

Die folgende Tabelle gibt das Base64-Alphabet wieder. In den Klammern steht der jeweilige Zeichenwert in hexadezimaler Schreibweise. In den Spalten- bzw. Zeilenbeschriftungen ist die Darstellung in binärer Form, wobei die Spalte durch die unteren drei Bits und die Zeile durch die oberen drei Bits definiert wird. Das Zeichen für die Bitfolge 010101 ist demnach in Zeile 2 (010) und Spalte 5 (101) zu finden (wobei die Zählung jeweils mit 0 beginnt!), und ist ein großes V. Rückwärts betrachtet stellt ein kleines p den Zeichenwert 29h dar, welcher der Bitfolge 101001 entspricht.

xxx000 xxx001 xxx010 xxx011 xxx100 xxx101 xxx110 xxx111
000xxx A (00) B (01) C (02) D (03) E (04) F (05) G (06) H (07)
001xxx I (08) J (09) K (0a) L (0b) M (0c) N (0d) O (0e) P (0f)
010xxx Q (10) R (11) S (12) T (13) U (14) V (15) W (16) X (17)
011xxx Y (18) Z (19) a (1a) b (1b) c (1c) d (1d) e (1e) f (1f)
100xxx g (20) h (21) i (22) j (23) k (24) l (25) m (26) n (27)
101xxx o (28) p (29) q (2a) r (2b) s (2c) t (2d) u (2e) v (2f)
110xxx w (30) x (31) y (32) z (33) 0 (34) 1 (35) 2 (36) 3 (37)
111xxx 4 (38) 5 (39) 6 (3a) 7 (3b) 8 (3c) 9 (3d) + (3e) / (3f)

Ein Beispiel

Um das Verfahren zu verdeutlichen soll der Satz "Hans löst Münzen in Salpetersäure auf" mittels Base64 codiert werden. Dabei verschwinden die Umlaute und der Text könnte über eine Tastatur mit englischer Tastenbelegung abgetippt werden (ich gebe zu, diese Anwendung ist ein kleines bißchen an den Haaren herbeigezogen). Zunächst werden die Eingabe-Zeichen in 3er-Gruppen organisiert und ihre Binärcodes ermittelt, wobei diese aus Platzgründen zunächst hexadezimal dargestellt werden:

H a n s l ö s t M ü n z e n i n S a l p e t e r s ä u r e a u f
48 61 6e 73 20 6c f6 73 74 20 4d fc 6e 7a 65 6e 20 69 6e 20 53 61 6c 70 65 74 65 72 73 e4 75 72 65 20 61 75 66 00 00

Die Eingabe hat 37 Zeichen, was kein Vielfaches von 3 ist. Das nächste Vielfache ist 39, also müssen zwei Nullbytes ergänzt werden. Im Folgenden wird anhand der ersten sechs und letzten sechs Zeichen gezeigt, wie nun 3 8-Bit-Bytes in 4 6-Bit-Zeichen umgewandelt werden:

H a n s l
4 8 6 1 6 e 7 3 2 0 6 c
0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 1 0 1 1 0 0
S (12) G (06) F (05) u (2e) c (1c) y (32) B (01) s (2c)
a u f
2 0 6 1 7 5 6 6 0 0 0 0
0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 1 0 1 0 1 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I (08) G (06) F (05) 1 (35) Z (19) g (20) = =

Die vollständige Base64-Zeichenkette lautet: SGFucyBsw7ZzdCBNw7xuemVuIGluIFNhbHBldGVyc8OkdXJlIGF1Zg==.

Das Decodieren geht im Prinzip genauso, bloß rückwärts. Es werden immer 4 Zeichen aus der Eingabe gelesen und daraus 3 Byte für die Ausgabe erzeugt. Dabei muß darauf geachtet werden, daß beim letzten Block die Padding-Zeichen richtig behandelt werden. Auch dies soll an einem Beispiel verdeutlicht werden. Als Base64-Zeichenkette wird RXMgZ2VodCE= verwendet:

R (11) X (17) M (0c) g (20) Z (19) 2 (36) V (15) o (28) d (1d) C (02) E (04) =
0 1 0 0 0 1 0 1 0 1 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 1 0 0 1 1 1 0 1 1 0 0 1 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0
4 5 7 3 2 0 6 7 6 5 6 8 7 4 2 1 0 0
E s g e h t !

Der decodierte Text lautet also "Es geht!". In Base64 codierter Text wird meistens in Blöcken mit einer Zeilenlänge von 76 Zeichen verwendet. Es ist für einen Decoder also absolut sinnvoll, Whitespace (also Leerzeichen, Zeilenumbrüche etc.) zu ignorieren und sich davon nicht verwirren zu lassen. Der hier vorgestellte Beispielcode macht die Zeilenumbrüche übrigens bereits nach 72 Zeichen, was meinem damaligen Kenntnisstand entsprach.

72 Zeichen entspricht der empfohlenen Zeilenlänge für Usenet-Postings, weil hier in der Regel jede Zeile mehrmals gequotet wird, und man somit etwas "Luft" hat, bevor die magische Grenze von 80 Zeichen erreicht wird. Da aber base64-codierte Daten nicht gequotet werden, kann hier eine Zeile problemlos 76 Zeichen lang sein, was bei großen Datenmengen eine geringfügige Ersparnis bietet, denn die Zahl der Zeilenumbrüche (die bei CRLF je 2 Byte benötigen) kann um ca. 5% verringert werden.

Downloads

Version Datum Änderungen Datei Beschreibung
1.0 2005-07-09 Erstes Release libbase64-1.0.tar.gz Quellcode