Hoe PEM-bestand te lezen om openbare en privésleutels te krijgen

Java Top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS

1. Overzicht

Bij cryptografie met openbare sleutels (ook bekend als asymmetrische cryptografie), vertrouwt het versleutelingsmechanisme op twee gerelateerde sleutels, een openbare sleutel en een privésleutel. De openbare sleutel wordt gebruikt om het bericht te versleutelen, terwijl alleen de eigenaar van de privésleutel het bericht kan ontsleutelen.

In deze zelfstudie gaan we zien hoe u openbare en privésleutels uit een PEM-bestand kunt lezen.

Eerst gaan we enkele belangrijke concepten bestuderen rond cryptografie met openbare sleutels. Vervolgens leren we hoe we PEM-bestanden kunnen lezen met pure Java.

Ten slotte verkennen we de BouncyCastle-bibliotheek als een alternatieve benadering.

2. Concepten

Laten we, voordat we beginnen, enkele sleutelconcepten begrijpen.

X.509 is een standaard die het formaat van certificaten met openbare sleutels definieert. Dit formaat beschrijft dus onder meer een openbare sleutel.

DER is het meest populaire coderingsformaat om gegevens zoals X.509-certificaten en PKCS8-privésleutels in bestanden op te slaan. Het is een binaire codering en de resulterende inhoud kan niet worden bekeken met een teksteditor.

PKCS8 is een standaardsyntaxis voor het opslaan van privésleutelinformatie. De privésleutel kan optioneel worden gecodeerd met behulp van een symmetrisch algoritme.

Deze standaard kan niet alleen RSA-privésleutels verwerken, maar ook andere algoritmen. De PKCS8-privésleutels worden doorgaans uitgewisseld via het PEM-coderingsformaat.

PEM is een base-64-coderingsmechanisme van een DER-certificaat. PEM kan ook andere soorten gegevens coderen, zoals openbare / privésleutels en certificaatverzoeken.

Een PEM-bestand bevat ook een koptekst en een voettekst die het type gecodeerde gegevens beschrijven:

----- BEGIN PUBLIC KEY ----- ... Base64-codering van het DER-gecodeerde certificaat ... ----- END PUBLIC KEY -----

3. Pure Java gebruiken

3.1. Lees PEM-gegevens uit een bestand

Laten we beginnen met het PEM-bestand te lezen en de inhoud ervan in een string op te slaan:

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

3.2. Haal de openbare sleutel op uit de PEM-string

We gaan een hulpprogramma-methode bouwen die de openbare sleutel uit de PEM-gecodeerde tekenreeks haalt:

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

Stel dat we een het dossier als parameter:

openbare statische RSAPublicKey readPublicKey (bestandsbestand) genereert uitzondering {String key = nieuwe string (Files.readAllBytes (file.toPath ()), Charset.defaultCharset ()); String publicKeyPEM = key .replace ("----- BEGIN PUBLIC KEY -----", "") .replaceAll (System.lineSeparator (), "") .replace ("----- END PUBLIC KEY ----- "," "); byte [] gecodeerd = Base64.decodeBase64 (publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance ("RSA"); X509EncodedKeySpec keySpec = nieuwe X509EncodedKeySpec (gecodeerd); return (RSAPublicKey) keyFactory.generatePublic (keySpec); }

Zoals we kunnen zien, moeten we eerst de koptekst, de voettekst en ook de nieuwe regels verwijderen. Vervolgens moeten we de met Base64 gecodeerde tekenreeks decoderen in het overeenkomstige binaire formaat.

Vervolgens moeten we het resultaat laden in een sleutelspecificatieklasse die in staat is om materiaal met openbare sleutels te verwerken. In ons geval gaan we de X509EncodedKeySpec klasse.

Ten slotte kunnen we een public key-object genereren op basis van de specificatie met behulp van de KeyFactory klasse.

3.3. Ontvang een privésleutel van de PEM-string

Nu we weten hoe we een openbare sleutel moeten lezen, lijkt het algoritme om een ​​privésleutel te lezen erg op elkaar.

We gaan een met PEM gecodeerde privésleutel in PKCS8-indeling gebruiken. Laten we eens kijken hoe de koptekst en voettekst eruit zien:

----- BEGIN PRIVATE KEY ----- ... Base64 gecodeerde sleutel ... ----- END PRIVATE KEY -----

Zoals we eerder hebben geleerd, hebben we een klas nodig die PKCS8-sleutelmateriaal kan verwerken. De PKCS8EncodedKeySpec klasse vervult die rol.

Laten we dus eens kijken naar het algoritme:

openbare RSAPrivateKey readPrivateKey (bestandsbestand) genereert uitzondering {String key = nieuwe string (Files.readAllBytes (file.toPath ()), Charset.defaultCharset ()); String privateKeyPEM = key .replace ("----- BEGIN PRIVATE KEY -----", "") .replaceAll (System.lineSeparator (), "") .replace ("----- END PRIVATE KEY ----- "," "); byte [] gecodeerd = Base64.decodeBase64 (privateKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance ("RSA"); PKCS8EncodedKeySpec keySpec = nieuwe PKCS8EncodedKeySpec (gecodeerd); return (RSAPrivateKey) keyFactory.generatePrivate (keySpec); }

4. Met behulp van BouncyCastle Library

4.1. Lees openbare sleutel

We gaan de BouncyCastle-bibliotheek verkennen en zien hoe deze kan worden gebruikt als alternatief voor de pure Java-implementatie.

Laten we de openbare sleutel ophalen:

openbare RSAPublicKey readPublicKey (bestandsbestand) genereert uitzondering {KeyFactory factory = KeyFactory.getInstance ("RSA"); probeer (FileReader keyReader = nieuwe FileReader (bestand); PemReader pemReader = nieuwe PemReader (keyReader)) {PemObject pemObject = pemReader.readPemObject (); byte [] content = pemObject.getContent (); X509EncodedKeySpec pubKeySpec = nieuwe X509EncodedKeySpec (inhoud); return (RSAPublicKey) factory.generatePublic (pubKeySpec); }}

Er zijn een paar belangrijke klassen waarvan we op de hoogte moeten zijn bij het gebruik van BouncyCastle:

  • PemReader - duurt een Lezer als parameter en parseert de inhoud ervan. Het verwijdert de onnodige kopteksten en decodeert de onderliggende Base64 PEM-gegevens in een binair formaat.
  • PemObject slaat het resultaat gegenereerd door de PemReader.

Laten we bovendien eens kijken naar een andere benadering die de klassen van Java omvat (X509EncodedKeySpec, KeyFactory) in de eigen klasse van BouncyCastle (JcaPEMKeyConverter):

openbare RSAPublicKey readPublicKeySecondApproach (bestandsbestand) gooit IOException {try (FileReader keyReader = nieuwe FileReader (bestand)) {PEMParser pemParser = nieuwe PEMParser (keyReader); JcaPEMKeyConverter converter = nieuwe JcaPEMKeyConverter (); SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance (pemParser.readObject ()); return (RSAPublicKey) converter.getPublicKey (publicKeyInfo); }}

4.2. Lees de privésleutel

We gaan twee voorbeelden zien die erg lijken op de voorbeelden hierboven.

In het eerste voorbeeld hoeven we alleen de X509EncodedKeySpec klasse met de PKCS8EncodedKeySpec klasse en retourneer een RSAPrivateKey object in plaats van een RSAPublicKey:

openbare RSAPrivateKey readPrivateKey (bestandsbestand) genereert uitzondering {KeyFactory factory = KeyFactory.getInstance ("RSA"); probeer (FileReader keyReader = nieuwe FileReader (bestand); PemReader pemReader = nieuwe PemReader (keyReader)) {PemObject pemObject = pemReader.readPemObject (); byte [] inhoud = pemObject.getContent (); PKCS8EncodedKeySpec privKeySpec = nieuwe PKCS8EncodedKeySpec (inhoud); return (RSAPrivateKey) factory.generatePrivate (privKeySpec); }}

Laten we nu de tweede benadering van de vorige sectie een beetje herwerken om een ​​privésleutel te lezen:

openbare RSAPrivateKey readPrivateKeySecondApproach (bestandsbestand) gooit IOException {probeer (FileReader keyReader = nieuwe FileReader (bestand)) {PEMParser pemParser = nieuwe PEMParser (keyReader); JcaPEMKeyConverter converter = nieuwe JcaPEMKeyConverter (); PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance (pemParser.readObject ()); return (RSAPrivateKey) converter.getPrivateKey (privateKeyInfo); }}

Zoals we kunnen zien, hebben we net vervangen SubjectPublicKeyInfo met PrivateKeyInfo en RSAPublicKey met RSAPrivateKey.

4.3. Voordelen

De BouncyCastle-bibliotheek biedt een aantal voordelen.

Een voordeel is dat we hoeven de kop- en voettekst niet handmatig over te slaan of te verwijderen. Een andere is dat wij zijn niet verantwoordelijk voor de Base64-decodering een van beide. Daarom kunnen we met BouncyCastle minder foutgevoelige code schrijven.

Bovendien is de De BouncyCastle-bibliotheek ondersteunt het PKCS1-formaat ook. Ondanks het feit dat PKCS1 ook een populair formaat is dat wordt gebruikt om cryptografische sleutels op te slaan (alleen RSA-sleutels), ondersteunt Java dit op zichzelf niet.

5. Conclusie

In dit artikel hebben we geleerd hoe we openbare en privésleutels uit PEM-bestanden kunnen lezen.

Eerst hebben we enkele sleutelconcepten rond cryptografie met openbare sleutels bestudeerd. Vervolgens hebben we gezien hoe we openbare en privésleutels kunnen lezen met pure Java.

Ten slotte hebben we de BouncyCastle-bibliotheek verkend en geleerd dat dit een goed alternatief is, omdat het een paar voordelen biedt in vergelijking met de pure Java-implementatie.

De volledige broncode voor zowel Java- als BouncyCastle-benaderingen is beschikbaar op GitHub.

Java onderkant

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS