Die Beschäftigung mit Kryptographie führt unweigerlich auch zum Thema „Padding“. Was ist nun das Padding ? Ganz einfach ausgedrückt füllt das Padding einen String (bzw. ein Byte Array) auf eine definierte Länge auf. Bei Nutzung der verschiedenen Kryptographie-Verfahren werdet Ihr immer stets darauf achten müssen, ob „Eurer“ Algorithsmus ein Padding unterstützt, ansonsten werdet Ihr unschöne Fehlermeldungen erhalten.
Als Beispiel soll der CBC-Algorithmus im Beispiel B06 dienen – hier gibt es Fehler, wenn der plaintextString nicht exakt 16 Zeichen (oder ein Vielfaches davon) lang ist.
Beim Auffüllen der Zeichen ist das verwendete Verfahren sehr wichtig, denn die Auffüllung sollte reversibel, also wieder entfernbar sein. So gibt es das sogenannte Null-Padding, bei dem der String durch 0-Bytes aufgefüllt wird.
Bei Nutzung der Java-Kryptographie werdet Ihr immer wieder mit dem PKCS5-Paddingverfahren in Berührung kommen (bei Nutzung von Bouncy Castle werdet Ihr die PKCS7-Variante kennen lernen, welche aber kompatibel ist). Beim AES PKCS5-Verfahren wird das Byte-Array stets auf die Blocklänge von 16 Zeichen erweitert und so gearbeitet: Bei einer Länge von 15 Zeichen wird ein 01 Byte angehängt, bei einer Länge von 14 Zeichen werden zwei 02-Bytes angefügt und so weiter. Bei einer Länge von 1 Zeichen werden die letzten 15 Stellen mit „0F“ (hexadezimal) bzw „15“ (dezimal) aufgefüllt. Was gilt aber wenn die Länge exakt der Blocklänge (16 Zeichen) entspricht ? Dann wird ein zusätzlicher Block angehangen, der nur aus (16 Stück) 00-Bytes besteht.
Nachfolgend findet Ihr ein kleines Java-Programm, welches das PKCS-Padding abbildet und Euch eine anschauliche Tabelle ausgibt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
package net.bplaced.javacrypto.general; import javax.xml.bind.DatatypeConverter; /* * Herkunft/Origin: http://javacrypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Copyright/Copyright: frei verwendbares Programm (Public Domain) * Copyright: This is free and unencumbered software released into the public domain. * Lizenttext/Licence: <http://unlicense.org> * getestet mit/tested with: Java Runtime Environment 8 Update 191 x64 * Datum/Date (dd.mm.jjjj): 13.11.2018 * Funktion: Demonstriert das PKCS5-Padding * Function: shows the PKCS5-Padding * * Sicherheitshinweis/Security notice * Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine * korrekte Funktion, insbesondere mit Blick auf die Sicherheit ! * Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird. * The program routines just show the function but please be aware of the security part - * check yourself before using in the real world ! */ public class A07_Pkcs5Padding { public static void main(String[] args) { System.out.println("A07 PKCS5 Padding"); System.out.println(""); String plaintextOriginalString = "1234567890123456"; // testdaten mit 16 zeichen int blocklengthInt = 16; // bei aes-verfahren ist die blocklänge immer 16 byte String plaintextString = ""; byte[] plaintextByte = new byte[16]; // in dieses byte array kopieren wir später teile plaintextOriginalString int plaintextLengthInt = 0; // länge des jeweils kopierten plaintextes int paddingcountInt = 0; // anzahl der fehlenden zeichen = padding-zeichen bis zur blocklengthInt byte[] paddingtextByte = null; // nimmt den plaintext nach padding auf String ausgabezeileString = ""; // zur formatierten ausgabe der daten System.out.println("blocklengthInt :" + blocklengthInt); System.out.println("plaintextOriginalString:" + plaintextOriginalString); System.out.println(""); System.out.println("Padding plaintextByte Länge Anz Hex paddingtextByte"); // kopfzeile for (int copyInt = 1; copyInt < 17; copyInt++) { // wir kopieren nur einen teil des plaintextOriginalString plaintextString = plaintextOriginalString.substring(0, copyInt); plaintextLengthInt = plaintextString.length(); // die länge unseres plaintextes plaintextByte = plaintextString.getBytes(); // ab hier wird das padding hinzugeführt paddingtextByte = new byte[blocklengthInt]; if (plaintextLengthInt >= blocklengthInt) { paddingtextByte = new byte[(2 * blocklengthInt)]; } System.arraycopy(plaintextByte, 0, paddingtextByte, 0, plaintextLengthInt); paddingcountInt = blocklengthInt - plaintextLengthInt; for (int i = plaintextLengthInt; i < blocklengthInt; i++) { paddingtextByte[i] = (byte) paddingcountInt; } // zusammenbau der ausgabezeile // plaintextByte plaintextLengthInt paddingcountInt paddingcountHex // paddingtextByte ausgabezeileString = formatMitLeerzeichenRechts(DatatypeConverter.printHexBinary(plaintextByte), 33); ausgabezeileString = ausgabezeileString + formatMitLeerzeichenRechts(Integer.toString(plaintextLengthInt), 6); ausgabezeileString = ausgabezeileString + formatMitLeerzeichenRechts(Integer.toString(paddingcountInt), 5); ausgabezeileString = ausgabezeileString + formatMitLeerzeichenRechts(Integer.toHexString(paddingcountInt), 3); ausgabezeileString = ausgabezeileString + formatMitLeerzeichenRechts(DatatypeConverter.printHexBinary(paddingtextByte), 33); // ausgabe System.out.println("Ergebnis: " + ausgabezeileString); } } // Sehr einfache, aber bei grossen Datenmengen auch ineffiziente // Implementierung: public static String formatMitLeerzeichenRechts(String value, int len) { while (value.length() < len) { value += " "; } return value; } } |
Hier die versprochene Ausgabe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
A07 PKCS5 Padding blocklengthInt :16 plaintextOriginalString:1234567890123456 Padding plaintextByte Länge Anz Hex paddingtextByte Ergebnis: 31 1 15 f 310F0F0F0F0F0F0F0F0F0F0F0F0F0F0F Ergebnis: 3132 2 14 e 31320E0E0E0E0E0E0E0E0E0E0E0E0E0E Ergebnis: 313233 3 13 d 3132330D0D0D0D0D0D0D0D0D0D0D0D0D Ergebnis: 31323334 4 12 c 313233340C0C0C0C0C0C0C0C0C0C0C0C Ergebnis: 3132333435 5 11 b 31323334350B0B0B0B0B0B0B0B0B0B0B Ergebnis: 313233343536 6 10 a 3132333435360A0A0A0A0A0A0A0A0A0A Ergebnis: 31323334353637 7 9 9 31323334353637090909090909090909 Ergebnis: 3132333435363738 8 8 8 31323334353637380808080808080808 Ergebnis: 313233343536373839 9 7 7 31323334353637383907070707070707 Ergebnis: 31323334353637383930 10 6 6 31323334353637383930060606060606 Ergebnis: 3132333435363738393031 11 5 5 31323334353637383930310505050505 Ergebnis: 313233343536373839303132 12 4 4 31323334353637383930313204040404 Ergebnis: 31323334353637383930313233 13 3 3 31323334353637383930313233030303 Ergebnis: 3132333435363738393031323334 14 2 2 31323334353637383930313233340202 Ergebnis: 313233343536373839303132333435 15 1 1 31323334353637383930313233343501 Ergebnis: 31323334353637383930313233343536 16 0 0 3132333435363738393031323334353600000000000000000000000000000000 |
Den Quellcode zum Programm findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/A-Allgemeine-Programme. Dort findet Ihr auch die Versionen für Java 11.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 26.01.2019