Sichere Programmierung Lerneinheit 3: Kryptographie mit Java Prof. Dr. Christoph Karg Studiengang Informatik Hochschule Aalen Sommersemester 2019 17.1.2019
Einleitung Einleitung Diese Lerneinheit vermittelt Wissen zu Java Cryptography, einer Sammlung von APIs zur Bereitstellung kryptographischer Mechanismen für die Programmiersprache Java. Es werden folgende Themen behandelt: Aufbau und Konzepte der JCE Generierung von Zufallszahlen Kryptographische Hashfunktionen Symmetrische Verschlüsselungsverfahren Asymmetrische Verschlüsselungsverfahren Digitale Signaturen Die Lerneinheit orientiert sich an der Java 11 JDK Dokumentation [Ora18b]. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 2 / 36
Überblick Überblick Beim Design von Java wird großer Wert auf Sicherheit gelegt. Java besitzt ein automatisiertes Speichermanagement, einen Garbage Collector und eine Bereichsüberprüfung von Arrays. Java beinhaltet mehrere APIs, über die gängige kryptographische Verfahren bereitgestellt werden. Die Implementierung der Verfahren wird über sogenannte Security Provider bereitgestellt. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 3 / 36
Überblick Designziele Designziele Beim Entwurf von Java Security wurden folgende Ziele verfolgt: Plattformunabhängigkeit Interoperabilität Erweiterbarkeit Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 4 / 36
Überblick Designziele Plattformunabhängigkeit Die Sicherheitsmechanismen sind auf allen von Java unterstützten Betriebssystemen lauffähig. Die Sicherheitsmechanismen werden über Security Provider bereitgestellt. Die API liefert einen einheitlichen Weg, um die Sicherheitsmechanismen in eine Anwendung zu integrieren. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 5 / 36
Überblick Designziele Interoperabilität Die Nutzung der Sicherheitsmechanismen ist standardisiert und somit unabhängig von einer bestimmten Kryptographie-Bibliothek. Alle Implementierungen müssen sich an die vorgegebene Schnittstelle halten. Einschränkung: Der Umfang der bereitgestellten Sicherheitsmechanismen ist vom gewählten Security Provider abhängig. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 6 / 36
Überblick Designziele Erweiterbarkeit Das JDK beinhaltet Security Provider für gängige Sicherheitsmechanismen. Das JDK kann um externe Security Provider erweitert werden, die zusätzliche Funktionalitäten beinhalten. Bouncy Castle ist eine Open Source Entwicklung eines Security Providers mit einem großen Funktionsumfang. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 7 / 36
Überblick Zugriff auf einen Sicherheitsmechanismus Zugriff auf einen Sicherheitsmechanismus Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 8 / 36
Überblick Zugriff auf einen Sicherheitsmechanismus Zugriff auf einen Sicherheitsmechanismus (Forts.) Für jede Art von Sicherheitsmechanismus existiert eine Klasse, über die ein Verfahren ausgewählt wird. Beispiel: Die Klasse MessageDigest steht für kryptographische Hashfunktionen zur Berechnung von Prüfsummen. Es wird der Security Provider mit höchsten Priorität ausgewählt, der das Verfahren implementiert. Alternativ kann explizit ein Provider bei der Auswahl angegeben werden. Die Auswahl erfolgt über eine standardisierte Bezeichnung der Algorithmen [Ora18a]. Beispiel: Die Bezeichnung SHA-256 steht für den Secure Hash Algorithm 2 mit einer Prüfsummenlänge von 256 Bit. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 9 / 36
Überblick Verfahren und die entsprechenden Klassen Verfahren und die entsprechenden Klassen Kryptographische Prüfsummen MessageDigest Zufallszahlen für kryptographische Zwecke SecureRandom Verschlüsselungsverfahren Cipher Digitale Signaturen Signature Schlüssel SecretKey, KeyPair Generierung von Schlüsseln SecretKeyFactory, KeyFactory Message Authentication Codes Mac... Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 10 / 36
Kryptographische Prüfsummen Kryptographische Prüfsummen Ablauf der Berechnung einer Prüfsumme: Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 11 / 36
Kryptographische Prüfsummen Beispiel 1 Beispiel 1 Beispiel: Berechnung einer Prüfsumme mit SHA-256: 1 String message = "Kryptographie macht Spass!!!"; 2 3 MessageDigest md = MessageDigest.getInstance("SHA -256"); 4 byte[] digest = md.digest(message.getbytes()); 5 6 System.out.println("Message: " + message); 7 System.out.println("Digest.: " + tohexstring(digest, 2)); Ergebnis: Die kryptographische Prüfsumme des obigen Texts ist: EA0B 1FB1 CE64 3467 A370 C811 1058 16C1 6709 5496 4DFC A23E 33FB C936 BF1D E9F2 Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 12 / 36
Kryptographische Prüfsummen Beispiel 1 Bemerkungen Das Attribut message enthält die Nachricht, für die eine Prüfsumme berechnet werden soll. In Zeile 3 wird ein MessageDigest-Objekt abgerufen, welches die kryptographische Hashfunktion SHA-256 bereitstellt. In Zeile 4 wird die Nachricht in ein Byte-Array umgewandelt und anschließend die Prüfsumme berechnet. Die Prüfsumme wird in einem Byte-Array gespeichert. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 13 / 36
Kryptographische Prüfsummen Hilfsmethode Methode tohexstring() Darstellung eines Byte-Arrays als Folge hexadezimaler Zahlen: 1 protected String tohexstring(byte[] data, int offset) { 2 if (offset <0) { 3 offset=0; 4 } 5 6 StringBuilder sb = new StringBuilder(); 7 8 for (int i=0; i<data.length; i++) { 9 sb.append(string.format("%02x", data[i])); 10 if ((offset >0) && (i+1<data.length) && ((i+1) % offset == 0)) { 11 sb.append(" "); 12 } 13 } 14 return sb.tostring(); 15 } Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 14 / 36
Kryptographische Prüfsummen Beispiel 2 Beispiel 2 Beispiel: Berechnung einer Prüfsumme mit SHA-256: 1 String m1 = "Kryptographie "; 2 String m2 = "macht "; 3 String m3 = "Spass!!!"; 4 5 MessageDigest md = MessageDigest.getInstance("SHA -256"); 6 md.reset(); 7 md.update(m1.getbytes()); 8 md.update(m2.getbytes()); 9 md.update(m3.getbytes()); 10 byte[] digest = md.digest(); 11 12 System.out.println("Message: " + m1 + m2 + m3); 13 System.out.println("Digest.: " + tohexstring(digest, 2)); Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 15 / 36
Kryptographische Prüfsummen Beispiel 2 Bemerkungen Die Nachricht besteht aus drei Teilen, die in den Strings m1, m2 und m3 gespeichert sind. In Zeile 6 wird das Objekt zurückgesetzt. (Dies ist an dieser Stelle eigentlich nicht erforderlich.) In den Zeilen 7 bis 9 wird mittels der Methode update() jeder Teil der Nachricht zur internen Prüfsumme hinzugefügt. In Zeile 9 wird die finale Prüfsumme berechnet und in einem Byte-Array speichert. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 16 / 36
Verschlüsselungsverfahren Verschlüsselungsverfahren Der Zugriff auf die Verschlüsselungsverfahren erfolgt über die Klasse Cipher. Unterscheidung 1: Symmetrische Verschlüsselung Asymmetrische Verschlüsselung Unterscheidung 2: 1. Blockchiffre 2. Stromchiffre Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 17 / 36
Verschlüsselungsverfahren Symmetrische Verschlüsselung Symmetrische Verschlüsselung Ablauf einer symmetrischen Verschlüsselung: Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 18 / 36
Verschlüsselungsverfahren Symmetrische Verschlüsselung Beispiel 1 Beispiel: Verschlüsselung mit AES im CBC-Mode: 1 String plaintext = "Dies ist eine streng geheime Nachricht!!!"; 2 3 String key = "om{ah8uugaigh8le"; 4 SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); 5 6 SecureRandom prng = new SecureRandom(); 7 byte[] iv = new byte[16]; 8 prng.nextbytes(iv); 9 IvParameterSpec ivspec = new IvParameterSpec(iv); Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 19 / 36
Verschlüsselungsverfahren Symmetrische Verschlüsselung Beispiel 1 (Forts.) 10 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 11 cipher.init(cipher.encrypt_mode, keyspec, ivspec); 12 byte[] ciphertext = cipher.dofinal(plaintext.getbytes()); 13 14 ByteArrayOutputStream os = new ByteArrayOutputStream(); 15 os.write(iv); 16 os.write(ciphertext); 17 Base64.Encoder encoder = Base64.getEncoder(); 18 String message = encoder.encodetostring(os.tobytearray()); 19 20 System.out.println("Plain text.: " + plaintext); 21 System.out.println("Message...: " + message); Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 20 / 36
Verschlüsselungsverfahren Symmetrische Verschlüsselung Bemerkungen Der zu verschlüsselnde Klartext liegt als String vor (Zeile 1). Eine mögliche Schlüssellänge von AES sind 16 Byte. Der Schlüssel wird entsprechend gewählt (Zeile 2). Anhand des Schlüssels wird eine Schlüsselspezifikation erstellt (Zeile 3). Für den CBC-Mode wird ein Initialisierungsvektor benötigt. Dieser wird zufällig erzeugt (Zeilen 6 bis 9). Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 21 / 36
Verschlüsselungsverfahren Symmetrische Verschlüsselung Bemerkungen (Forts.) Zur Verschlüsselung wird ein Objekt angefordert, welches AES im CBC-Mode mit PKCS5 Padding bereitstellt (Zeile 10). Für die Verschlüsselung wird das Objekt mit dem Initialisierungsvektor und dem Schlüssel initialisiert (Zeile 11). Der Klartext wird in ein Byte-Array umgewandelt und dieses anschließend verschlüsselt (Zeile 12). Der Initialisierungsvektor wird zusammen mit Geheimtext in einem Byte-Array gespeichert und als Base64 String kodiert (Zeilen 14 bis 18). Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 22 / 36
Verschlüsselungsverfahren Das Werkzeug keytool Das Werkzeug keytool Das Java Development Toolkit enthält das Werkzeug keytool zur Verwaltung eines digitalen Schlüsselbunds. Inhalte eines digitalen Schlüsselbunds: Schlüssel für ein symmetrisches Kryptosystem SecretKey Privater Schlüssel für ein asymmetrisches Kryptosystem PrivateKey Digitales Zertifikat Ceritificate Jeder Eintrag wird durch einen Alias referenziert. Ein digitaler Schlüsselbund kann in Java-Programmen verwendet werden. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 23 / 36
Verschlüsselungsverfahren Das Werkzeug keytool Benutzung von keytool Befehlsübersicht anzeigen: > keytool -help AES-Schlüssel erzeugen: > keytool -keystore mykeystore.jks -storetype pkcs12 \ -genseckey -alias "aes-key" -keyalg "AES" -keysize 256 Inhalt des Schlüsselbunds anzeigen: > keytool -keystore mykeystore.jks -list Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 24 / 36
Verschlüsselungsverfahren Das Werkzeug keytool Laden eines Schlüsselbunds Code-Fragment zum Laden eines digitalen Schlüsselbunds: 1 String ksfilename = "mykeystore.jks"; 2 String kspassword = "123456"; 3 KeyStore keystore = KeyStore.getInstance("pkcs12"); 4 File file = new File(ksFileName); 5 FileInputStream is = new FileInputStream(file); 6 keystore.load(is, kspassword.tochararray()); Analog: Speichern eines digitalen Schlüsselbunds Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 25 / 36
Verschlüsselungsverfahren Das Werkzeug keytool Zugriff auf einen Schlüssel des Schlüsselbunds 1 SecretKeySpec keyspec = null; 2 String keyalias = "aes-key"; 3 String kspassword = "123456"; 4 if (( keystore.containsalias(keyalias)) && keystore.iskeyentry( keyalias)) { 5 KeyStore.ProtectionParameter protparam = new KeyStore. PasswordProtection(ksPassword.toCharArray()); 6 KeyStore.Entry entry = keystore.getentry(keyalias, protparam); 7 KeyStore.SecretKeyEntry keyentry = (KeyStore.SecretKeyEntry)entry; 8 keyspec = (SecretKeySpec)keyEntry.getSecretKey(); 9 } else { 10 System.out.println("ERROR!"); 11 } Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 26 / 36
Verschlüsselungsverfahren Das Werkzeug keytool Bemerkungen In das Objekt keystore wurde im Vorfeld ein digitaler Schlüsselbund geladen. In der if-abfrage (Zeile 3) wird geprüft, ob ein Eintrag mit dem angegebenen Alias vorhanden ist, und ob dieser Eintrag einen Schlüssel für ein symmetrisches Kryptosystem enthält. Für den Zugriff auf den Schlüsselbund wird ein Objekt des Typs ProtectionParameter benötigt. In diesem wird das Passwort gespeichert (Zeilen 4 5). Der Schlüssel wird ausgelesen und eine entsprechende Spezifikation umgewandelt (Zeilen 6 7). Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 27 / 36
Verschlüsselungsverfahren Asymmetrische Verschlüsselung Asymmetrische Verschlüsselung Bei asymmetrischer Verschlüsselung kommt ein Paar bestehend aus einem privaten und öffentlichen Schlüssel zum Einsatz. Verwendung: Öffentlicher Schlüssel Verschlüsselung Privater Schlüssel Entschlüsselung Das Werkzeug keytool unterstützt die Erstellung von Schlüsselpaaren. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 28 / 36
Verschlüsselungsverfahren Asymmetrische Verschlüsselung Beispiel Beispiel: Schlüsselgenerierung und Verschlüsselung mit RSA: 1 int keysize=2000; 2 KeyPairGenerator keypairgen = KeyPairGenerator.getInstance("RSA"); 3 keypairgen.initialize(keysize); 4 KeyPair rsakeypair = keypairgen.generatekeypair(); 5 String plaintext = "Kryptographie macht immer noch Spass!!!"; 6 PublicKey publickey = rsakeypair.getpublic(); 7 Cipher rsa = Cipher.getInstance("RSA"); 8 rsa.init(cipher.encrypt_mode, publickey); 9 byte[] ciphertext = rsa.dofinal(plaintext.getbytes()); 10 Base64.Encoder encoder = Base64.getEncoder(); 11 String message = encoder.encodetostring(ciphertext); Analog: Entschlüsselung mit dem in rsakeypair gespeicherten privaten Schlüssel. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 29 / 36
Verschlüsselungsverfahren Asymmetrische Verschlüsselung Bemerkungen Zur Erzeugung des Schlüsselpaars wird ein Objekt der Klasse KeyPairGenerator verwendet (Zeilen 1 4). Zur Verschlüsselung wird der öffentliche Schlüssel aus dem Schlüsselpaar extrahiert (Zeile 6). Als Kryptosystem kommt RSA zum Einsatz. Dieses wird mit dem öffentlichen Schlüssel für die Verschlüsselung initialisiert (Zeilen 7 8). Die Verschlüsselung erfolgt mit der Methode dofinal() (Zeile 9). Abschließend wird der Geheimtext als Base64-String kodiert. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 30 / 36
Digitale Signaturen Digitale Signaturen Ablauf einer digitalen Signatur: Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 31 / 36
Digitale Signaturen Digitale Signaturen (Forts.) Eine digitale Signatur bestätigt die Authentizität einer Nachricht. Für die Durchführung einer digitalen Signatur wird ein Paar bestehend aus einem privaten und öffentlichen Schlüssel benötigt. Viele asymmetrische Kryptosysteme kann man auch für digitale Signaturen einsetzen. Ein Signaturverfahren ist in der Regel die Kombination einer kryptographsichen Hashfunktion und eines Public-Key-Kryptosystems. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 32 / 36
Digitale Signaturen Beispiel Beispiel: Schlüsselgenerierung und Signierung mit RSA: 1 int keysize=2000; 2 KeyPairGenerator keypairgen = KeyPairGenerator.getInstance("RSA"); 3 keypairgen.initialize(keysize); 4 KeyPair rsakeypair = keypairgen.generatekeypair(); 5 PrivateKey privatekey = rsakeypair.getprivate(); 6 Signature rsa = Signature.getInstance("SHA256withRSA"); 7 rsa.initsign(privatekey); 8 String plaintext = "Kryptographie macht immer noch Spass!!!"; 9 rsa.update(plaintext.getbytes()); 10 byte[] signature = rsa.sign(); 11 Base64.Encoder encoder = Base64.getEncoder(); 12 String digest = encoder.encodetostring(signature); Analog: Verifikation der Signatur mit in rsakeypair gespeicherten öffentlichen Schlüssel. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 33 / 36
Digitale Signaturen Bemerkungen Vor der Nutzung eines Signaturverfahrens muss ein Schlüsselpaar erstellt werden (Zeilen 1 4). Zur Signierung einer Nachricht wird der private Schlüssel ausgelesen (Zeile 5). Das Signaturverfahren wird auf die übliche Art und Weise ausgewählt und initialisiert (Zeilen 6 7). Die zu signierende Nachricht wird mit der Methode update() verarbeitet (Zeile 9). Die Nachricht wird mit der Methode sign() signiert (Zeile 10). Abschließend wird die Signatur als Base64-String kodiert. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 34 / 36
Digitale Signaturen Zusammenfassung Zusammenfassung Die Programmiersprache Java beinhaltet zahlreiche APIs für kryptographische Mechanismen. Die Implementierung von kryptographischen Funktionen wird über Security Provider bereitgestellt. Der im Java SE Development Kit enthaltene Security Provider beinhaltet Implementierung der wichtigsten in der Praxis verwendeten kryptographischen Verfahren. Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 35 / 36
Literatur Literatur I [Ora18a] Oracle, Hrsg. Java Security Standard Algorithm Names. Java JDK 11 Documentation. 2018. url: https://docs.oracle.com/en/java/javase/11/docs/ specs/security/standard-names.html (besucht am 09. 01. 2019). [Ora18b] Oracle, Hrsg. Security Developer s Guide. Java JDK 11 Documentation. 2018. url: https: //docs.oracle.com/en/java/javase/11/security/javasecurity-overview1.html (besucht am 09. 01. 2019). Prof. Dr. C. Karg (HS Aalen) Sichere Programmierung Kryptographie mit Java 36 / 36