Ratgeber · Algorithmen & Encodings
Eingabe-Encodings beim Hashen: UTF-8, UTF-16, Hex, Base64
Eine Hash-Funktion sieht keinen Text, sie sieht Bytes. Wer dasselbe Wort in UTF-8 und UTF-16LE eingibt, bekommt zwei unterschiedliche Ergebnisse. Wer Hex oder Base64 als Input vorsieht, muss das vorher dekodieren. Der Ratgeber klärt, wann welches Encoding richtig ist.
Hash-Funktionen sehen Bytes, nicht Zeichen
Eine Hash-Funktion ist eine Byte-Maschine. Sie nimmt einen Strom von 8-Bit-Bausteinen entgegen und produziert daraus einen Fingerabdruck fester Länge. Was diese Bytes “bedeuten” (ein Text, ein Bild, ein Schlüssel), ist der Funktion egal. Genau hier liegt die häufigste Verwirrung beim Hashen.
Wer “Müller” tippt und einen SHA-256 will, muss der Funktion erst einmal sagen, wie der String in Bytes verwandelt werden soll. Das ist die Aufgabe des Encodings. Verschiedene Encodings produzieren verschiedene Byte-Folgen für denselben Text, und jede Byte-Folge hashed zu einem anderen Wert.
Ein konkretes Beispiel mit “Müller”:
- UTF-8:
4D C3 BC 6C 6C 65 72(7 Byte) - UTF-16LE ohne BOM:
4D 00 FC 00 6C 00 6C 00 65 00 72 00(12 Byte) - UTF-16LE mit BOM:
FF FE 4D 00 FC 00 6C 00 6C 00 65 00 72 00(14 Byte) - ISO-8859-1:
4D FC 6C 6C 65 72(6 Byte)
Vier Encodings, vier Byte-Folgen, vier Hashes. Wer einen Hash zwischen zwei Systemen vergleicht, muss das Encoding explizit machen. Bei reinen ASCII-Inputs (englische Texte ohne Sonderzeichen) ist das egal, weil ASCII in UTF-8, ISO-8859-1 und vielen anderen Encodings identisch ist. Sobald ein Umlaut, ein Akzent oder ein Emoji vorkommt, divergiert es.
UTF-8 als Default-Empfehlung
In der heutigen Web-Landschaft ist UTF-8 der unumstrittene Standard. Es ist plattformunabhängig, abwärtskompatibel zu ASCII und in W3C-Standards (HTML5, JSON, HTTP) als Default festgelegt. Wenn Sie keine konkrete Vorgabe haben, ist UTF-8 die richtige Wahl.
UTF-8 codiert Zeichen variabel: ASCII-Zeichen in einem Byte, lateinische Akzente und Umlaute in zwei Byte, chinesische und japanische Zeichen in drei Byte, Emojis und seltene Zeichen in vier Byte. Der Vorteil: kein Padding, kein Verschnitt, und ein UTF-8-Text mit ausschließlich ASCII-Zeichen ist Byte-für-Byte identisch zu einem reinen ASCII-Text.
Im Browser produziert TextEncoder per Default UTF-8: new TextEncoder().encode('Müller') liefert ein Uint8Array mit den sieben UTF-8-Bytes. Dieses Array geht direkt in crypto.subtle.digest('SHA-256', bytes). Eine andere Encoder-Variante als UTF-8 sieht der WHATWG-Encoding-Standard für TextEncoder bewusst nicht vor.
In Node.js sieht das ähnlich aus: Buffer.from('Müller', 'utf-8') liefert die UTF-8-Bytes, Buffer.from('Müller', 'utf-16le') die UTF-16LE-Bytes. Wer in Python arbeitet, schreibt 'Müller'.encode('utf-8') bzw. .encode('utf-16-le').
UTF-16LE: das Windows-Erbe
UTF-16LE taucht in zwei Kontexten auf. Erstens als internes String-Format von Windows (alle W-Suffix-API-Funktionen wie CreateFileW, MessageBoxW), zweitens als internes Format von Java und JavaScript für String-Speicherung.
Beim Hashen ist UTF-16LE selten die richtige Wahl. Eine Ausnahme: Wenn Sie einen Hash gegen ein Windows-Tool reproduzieren wollen, das intern UTF-16 verwendet. PowerShell zeigt das Verhalten: [System.Text.Encoding]::Unicode.GetBytes('Müller') liefert UTF-16LE ohne BOM, der daraus berechnete Hash unterscheidet sich vom UTF-8-Hash desselben Strings.
Das BOM (Byte Order Mark, FF FE für UTF-16LE) ist ein zusätzlicher Stolperstein. Manche Tools schreiben es, manche nicht. Der Hash ändert sich entsprechend. Wer reproduzierbare Ergebnisse braucht, fixiert die BOM-Frage explizit.
Hex und Base64: für binäre Daten
Hex und Base64 sind keine Text-Encodings, sondern Repräsentationen von Binärdaten als ASCII-Text. Wer einen 32-Byte-Schlüssel hat, kann ihn als 64 Hex-Zeichen oder 44 Base64-Zeichen aufschreiben, ohne Information zu verlieren.
Beim Hashen ist die Frage: Soll die Hex/Base64-Repräsentation gehashed werden, oder die ursprünglichen Bytes? In den meisten Anwendungen ist es das letztere. Wenn Sie auf hash-generieren.de “Hex” als Eingabe-Format wählen und 48656c6c6f eintragen, wird der String zunächst zu den fünf Byte 48 65 6c 6c 6f dekodiert (das ist “Hello” in ASCII), und dann gehashed. Der Hash entspricht dem von “Hello”, nicht dem von 48656c6c6f.
Diese Unterscheidung ist wichtig, wenn Sie einen Datei-Hash vergleichen. Eine Datei-Checksum ist immer der Hash der Datei-Bytes. Wenn das Tool eine andere Eingabe-Interpretation anbietet (z.B. den Hex-String mitsamt Anführungszeichen hashen), gibt es eine Diskrepanz.
Ausgabe-Encoding: ebenfalls eine Wahl
Der Hash selbst ist immer eine Byte-Folge fester Länge (32 Byte für SHA-256). Wie er dargestellt wird, ist eine getrennte Entscheidung. In der Praxis sind drei Formate üblich:
- Hex (lowercase):
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855(64 Zeichen für SHA-256). Standard bei sha256sum, GnuPG, Git, OpenSSL. - Hex (uppercase): derselbe Wert, große Buchstaben. Üblich in Windows-Tools (CertUtil, Get-FileHash) und in manchen Hardware-Dokumentationen.
- Base64:
47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=(44 Zeichen mit Padding). Üblich bei JWT, OAuth, S3-Object-Hashes, Subresource Integrity in HTML.
Beim Vergleich von Hashes müssen Sie beide Seiten auf dieselbe Darstellung normalisieren. Ein lowercase-Hex-Hash und ein uppercase-Hex-Hash sind als String-Vergleich verschieden, als Byte-Vergleich identisch. Wer Hashes in einer Datenbank speichert, fixiert die Darstellung einmalig (üblicherweise lowercase Hex) und ist beim Lookup auf der sicheren Seite.
Konkrete Beispiele aus der Praxis
Drei wiederkehrende Stolperfallen:
SSH-Keys: ein SSH-Public-Key-Fingerprint wird als Base64-Hash dargestellt: SHA256:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU. Wer den Fingerprint gegen einen anderen Tool-Output vergleicht, der Hex liefert, muss umrechnen. ssh-keygen -l -f key.pub zeigt Base64, ssh-keygen -l -f key.pub -E md5 zeigt Hex.
HTML Subresource Integrity: das integrity-Attribut in <script>-Tags erwartet sha256- oder sha384- Präfix gefolgt von Base64-Hash. Beispiel: <script src="..." integrity="sha384-Q6E9RHvbIyZFJoFt4O7bIVc+jKf3ZG4FxJ7v8OZb..."></script>. Wer den Hash in Hex hat, muss konvertieren.
Datei-Checksums: sha256sum auf Linux liefert lowercase Hex. Get-FileHash -Algorithm SHA256 auf Windows liefert uppercase Hex. Der Hash-Wert ist derselbe, die Darstellung unterscheidet sich. Ein simpler String-Vergleich schlägt fehl, ein case-insensitive Vergleich oder eine Normalisierung löst es.
Empfehlung für die hash-generieren.de-Praxis
Wenn Sie unsicher sind, welches Encoding richtig ist: UTF-8 als Input, lowercase Hex als Output. Das ist die Konvention der meisten Tools (sha256sum, OpenSSL, GnuPG, Git) und produziert Werte, die sich gegen Standard-Tools verifizieren lassen.
Hex-Input und Base64-Input brauchen Sie, wenn die ursprünglichen Daten binär sind (Schlüssel, Nonces, Datei-Fragmente). UTF-16LE brauchen Sie bei Windows-Altlasten oder wenn ein bestehender Hash mit UTF-16-Input erzeugt wurde. Die Methodik der Tool-Implementierung dokumentiert die Validierungs-Regeln (Hex-Pattern ^[0-9a-fA-F]+$, Base64 mit oder ohne Padding). Falls eine Encoding-Erwartung in der Oberfläche unklar bleibt, ist die Korrekturen-Adresse der schnellste Weg.
Was hängenbleibt
Ein Hash ist immer ein Hash über Bytes. Was die Bytes “bedeuten”, muss vor dem Hashen geklärt sein: UTF-8 für Standard-Text, UTF-16LE für Windows-Altlasten, Hex und Base64 für binäre Daten. Der Output ist eine Byte-Folge, die wiederum als Hex (lowercase, uppercase) oder Base64 dargestellt werden kann. Wer Hashes zwischen zwei Systemen vergleicht, fixiert beide Seiten auf dasselbe Eingabe- und Ausgabe-Encoding. Bei Diskrepanz liegt der Fehler fast immer im Encoding, nicht im Algorithmus.
FAQ
Häufige Fragen
Warum bekomme ich für 'Müller' zwei verschiedene Hashes?
Weil 'Müller' in UTF-8 sieben Byte ist (das ü kostet zwei Byte: 0xC3 0xBC) und in UTF-16LE zwölf Byte (jedes Zeichen zwei Byte, das ü liegt bei 0xFC 0x00). Eine Hash-Funktion bekommt die Bytes, nicht den Text. Andere Byte-Folge bedeutet anderer Hash. Wer Hashes zwischen Systemen vergleicht, muss das Encoding fixieren. Standard ist UTF-8, weil es plattformunabhängig und für ASCII identisch ist.
Welches Encoding nutzt die Web Crypto API?
Die Web Crypto API arbeitet auf ArrayBuffern, also rohen Bytes. Welche Bytes Sie reinstecken, ist Ihre Entscheidung. Der typische Code im Browser nutzt TextEncoder, der per Default UTF-8 produziert: new TextEncoder().encode('Müller') liefert die sieben UTF-8-Bytes. Eine TextEncoder-Variante für UTF-16 gibt es bewusst nicht, weil das WHATWG-Encoding-Standard UTF-8 als einzige Encoder-Variante zulässt. UTF-16-Eingaben müssten Sie manuell konstruieren.
Warum bietet hash-generieren.de Hex und Base64 als Eingabe-Format?
Weil viele Hash-Anwendungen binäre Daten verarbeiten, die als Text nicht darstellbar sind: ein Datei-Stream, ein Schlüssel, ein Zufalls-Nonce. Diese binären Daten werden üblicherweise als Hex (zwei Zeichen pro Byte) oder Base64 (vier Zeichen pro drei Byte) hingeschrieben. Die Eingabe wird vor dem Hashen wieder in die ursprünglichen Bytes dekodiert, sodass der Hash der originalen Binärdaten entsteht, nicht der Hex/Base64-Repräsentation.
Was ist der Unterschied zwischen Base64 und Base64URL?
Beide kodieren drei Byte in vier ASCII-Zeichen. Klassisches Base64 (RFC 4648 Abschnitt 4) verwendet die Zeichen A-Z, a-z, 0-9, + und /, gepolstert mit =. Base64URL (RFC 4648 Abschnitt 5) ersetzt + durch - und / durch _, weil + und / in URLs eine Sonderbedeutung haben. JWT, JWS, JOSE und viele Web-APIs nutzen Base64URL. Beim Dekodieren muss der Decoder wissen, welche Variante vorliegt. hash-generieren.de akzeptiert beide und normalisiert intern.
Wann brauche ich UTF-16LE als Eingabe-Encoding?
Selten, aber bei Windows-Altlasten. PowerShell- und .NET-Strings sind intern UTF-16LE, viele Windows-API-Funktionen erwarten W-Suffix-Varianten mit UTF-16. Wer einen Hash gegen ein Windows-Tool nachstellen will (z.B. CertUtil oder Get-FileHash auf einem String), muss UTF-16LE wählen. Beim Hashen von Dateien spielt das Encoding keine Rolle, weil dort ohnehin die Datei-Bytes gehasht werden, nicht der Text-Inhalt.
Quellen