Lesen der PEM-Datei zum Abrufen öffentlicher und privater Schlüssel

Java Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

Bei der Kryptographie mit öffentlichem Schlüssel (auch als asymmetrische Kryptographie bezeichnet) stützt sich der Verschlüsselungsmechanismus auf zwei verwandte Schlüssel, einen öffentlichen Schlüssel und einen privaten Schlüssel. Der öffentliche Schlüssel wird zum Verschlüsseln der Nachricht verwendet, während nur der Eigentümer des privaten Schlüssels die Nachricht entschlüsseln kann.

In diesem Tutorial erfahren Sie, wie Sie öffentliche und private Schlüssel aus einer PEM-Datei lesen.

Zunächst werden einige wichtige Konzepte zur Kryptografie mit öffentlichen Schlüsseln untersucht. Dann lernen wir, wie man PEM-Dateien mit reinem Java liest.

Schließlich werden wir die BouncyCastle-Bibliothek als alternativen Ansatz untersuchen.

2. Konzepte

Bevor wir beginnen, wollen wir einige Schlüsselkonzepte verstehen.

X.509 ist ein Standard, der das Format von Public-Key-Zertifikaten definiert. Dieses Format beschreibt also unter anderem einen öffentlichen Schlüssel.

DER ist das beliebteste Codierungsformat zum Speichern von Daten wie X.509-Zertifikaten und privaten PKCS8-Schlüsseln in Dateien. Es ist eine binäre Codierung und der resultierende Inhalt kann nicht mit einem Texteditor angezeigt werden.

PKCS8 ist eine Standardsyntax zum Speichern von Informationen zu privaten Schlüsseln. Der private Schlüssel kann optional mit einem symmetrischen Algorithmus verschlüsselt werden.

Mit diesem Standard können nicht nur private RSA-Schlüssel verarbeitet werden, sondern auch andere Algorithmen. Die privaten PKCS8-Schlüssel werden normalerweise über das PEM-Codierungsformat ausgetauscht.

PEM ist ein Base-64-Codierungsmechanismus eines DER-Zertifikats. PEM kann auch andere Arten von Daten wie öffentliche / private Schlüssel und Zertifikatanforderungen codieren.

Eine PEM-Datei enthält außerdem eine Kopf- und Fußzeile, die den Typ der codierten Daten beschreibt:

-----BEGIN PUBLIC KEY----- ...Base64 encoding of the DER encoded certificate... -----END PUBLIC KEY-----

3. Verwenden von Pure Java

3.1. PEM-Daten aus einer Datei lesen

Beginnen wir mit dem Lesen der PEM-Datei und dem Speichern ihres Inhalts in einer Zeichenfolge:

String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());

3.2. Öffentlichen Schlüssel vom PEM-String abrufen

Wir werden eine Dienstprogrammmethode erstellen, die den öffentlichen Schlüssel aus der PEM-codierten Zeichenfolge abruft:

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjtGIk8SxD+OEiBpP2/T JUAF0upwuKGMk6wH8Rwov88VvzJrVm2NCticTk5FUg+UG5r8JArrV4tJPRHQyvqK wF4NiksuvOjv3HyIf4oaOhZjT8hDne1Bfv+cFqZJ61Gk0MjANh/T5q9vxER/7TdU NHKpoRV+NVlKN5bEU/NQ5FQjVXicfswxh6Y6fl2PIFqT2CfjD+FkBPU1iT9qyJYH A38IRvwNtcitFgCeZwdGPoxiPPh1WHY8VxpUVBv/2JsUtrB/rAIbGqZoxAIWvijJ Pe9o1TY3VlOzk9ASZ1AeatvOir+iDVJ5OpKmLnzc46QgGPUsjIyo6Sje9dxpGtoG QQIDAQAB -----END PUBLIC KEY-----

Nehmen wir an, wir erhalten eine Datei als Parameter:

public static RSAPublicKey readPublicKey(File file) throws Exception { String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset()); String publicKeyPEM = key .replace("-----BEGIN PUBLIC KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PUBLIC KEY-----", ""); byte[] encoded = Base64.decodeBase64(publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); return (RSAPublicKey) keyFactory.generatePublic(keySpec); }

Wie wir sehen können, müssen wir zuerst auch die Kopfzeile, die Fußzeile und die neuen Zeilen entfernen. Dann müssen wir den Base64-codierten String in das entsprechende Binärformat dekodieren.

Als nächstes müssen wir das Ergebnis in eine Schlüsselspezifikationsklasse laden, die ein öffentliches Schlüsselmaterial verarbeiten kann. In unserem Fall verwenden wir die X509EncodedKeySpec- Klasse.

Schließlich können wir mithilfe der KeyFactory- Klasse ein öffentliches Schlüsselobjekt aus der Spezifikation generieren .

3.3. Privaten Schlüssel vom PEM-String abrufen

Nachdem wir nun wissen, wie man einen öffentlichen Schlüssel liest, ist der Algorithmus zum Lesen eines privaten Schlüssels sehr ähnlich.

Wir werden einen PEM-codierten privaten Schlüssel im PKCS8-Format verwenden. Mal sehen, wie die Kopf- und Fußzeile aussehen:

-----BEGIN PRIVATE KEY----- ...Base64 encoded key... -----END PRIVATE KEY-----

Wie wir zuvor gelernt haben, benötigen wir eine Klasse, die mit PKCS8-Schlüsselmaterial umgehen kann. Die PKCS8EncodedKeySpec- Klasse erfüllt diese Rolle.

Schauen wir uns also den Algorithmus an:

public RSAPrivateKey readPrivateKey(File file) throws Exception { String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset()); String privateKeyPEM = key .replace("-----BEGIN PRIVATE KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PRIVATE KEY-----", ""); byte[] encoded = Base64.decodeBase64(privateKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); }

4. Verwenden der BouncyCastle-Bibliothek

4.1. Öffentlichen Schlüssel lesen

Wir werden die BouncyCastle-Bibliothek untersuchen und sehen, wie sie als Alternative zur reinen Java-Implementierung verwendet werden kann.

Holen wir uns den öffentlichen Schlüssel:

public RSAPublicKey readPublicKey(File file) throws Exception { KeyFactory factory = KeyFactory.getInstance("RSA"); try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) { PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content); return (RSAPublicKey) factory.generatePublic(pubKeySpec); } }

Es gibt einige wichtige Klassen, die wir bei der Verwendung von BouncyCastle beachten müssen:

  • PemReader - verwendet einen Reader als Parameter und analysiert dessen Inhalt. Es entfernt die unnötigen Header und decodiert die zugrunde liegenden Base64-PEM-Daten in ein Binärformat.
  • PemObject - speichert das vom PemReader generierte Ergebnis .

Schauen wir uns außerdem einen anderen Ansatz an, der die Java-Klassen ( X509EncodedKeySpec, KeyFactory ) in die eigene Klasse von BouncyCastle ( JcaPEMKeyConverter ) einschließt :

public RSAPublicKey readPublicKeySecondApproach(File file) throws IOException { try (FileReader keyReader = new FileReader(file)) { PEMParser pemParser = new PEMParser(keyReader); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(pemParser.readObject()); return (RSAPublicKey) converter.getPublicKey(publicKeyInfo); } }

4.2. Privaten Schlüssel lesen

We're going to see two examples that are very similar to the ones showed above.

In the first example, we just need to replace the X509EncodedKeySpec class with the PKCS8EncodedKeySpec class and return an RSAPrivateKey object instead of an RSAPublicKey:

public RSAPrivateKey readPrivateKey(File file) throws Exception { KeyFactory factory = KeyFactory.getInstance("RSA"); try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) { PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content); return (RSAPrivateKey) factory.generatePrivate(privKeySpec); } }

Now, let's rework a bit the second approach from the previous section in order to read a private key:

public RSAPrivateKey readPrivateKeySecondApproach(File file) throws IOException { try (FileReader keyReader = new FileReader(file)) { PEMParser pemParser = new PEMParser(keyReader); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(pemParser.readObject()); return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo); } }

As we can see, we just replaced SubjectPublicKeyInfo with PrivateKeyInfo and RSAPublicKey with RSAPrivateKey.

4.3. Advantages

There are a couple of advantages provided by the BouncyCastle library.

One advantage is that we don’t need to manually skip or remove the header and the footer. Another one is that we’re not responsible for the Base64 decoding either. Therefore, we can write less error-prone code with BouncyCastle.

Moreover, the BouncyCastle library supports the PKCS1 format as well. Despite the fact that PKCS1 is also a popular format used to store cryptographic keys (only RSA keys), Java doesn't support it on its own.

5. Conclusion

In this article, we learned how to read public and private keys from PEM files.

First, we studied a few key concepts around public-key cryptography. Then, we saw how to read public and private keys using pure Java.

Schließlich haben wir die BouncyCastle-Bibliothek untersucht und festgestellt, dass dies eine gute Alternative ist, da sie im Vergleich zur reinen Java-Implementierung einige Vorteile bietet.

Der vollständige Quellcode für Java- und BouncyCastle-Ansätze ist auf GitHub verfügbar.

Java unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs