Java KeyStore API

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

In deze zelfstudie kijken we naar het beheren van cryptografische sleutels en certificaten in Java met behulp van de KeyStore API.

2. Sleutelopslag

Als we sleutels en certificaten in Java moeten beheren, hebben we een keystore, wat gewoon een veilige verzameling van alias is inzendingen van sleutels en certificaten.

We slaan sleutelarchieven meestal op in een bestandssysteem en we kunnen het beveiligen met een wachtwoord.

Java heeft standaard een keystore-bestand op JAVA_HOME /jre/ lib / security / cacerts. We hebben toegang tot deze keystore met het standaard keystore-wachtwoord verander het.

Nu, met dat stukje achtergrond, laten we beginnen met het maken van onze eerste.

3. Aanmaken van een keystore

3.1. Bouw

We kunnen eenvoudig een keystore maken met keytool, of we kunnen het programmatisch doen met behulp van de KeyStore API:

KeyStore ks = KeyStore.getInstance (KeyStore.getDefaultType ());

Hier gebruiken we het standaardtype, hoewel er een paar keystore-typen beschikbaar zijn, zoals jceks of pcks12.

We kunnen het standaard "JKS" -type (een door Oracle gepatenteerd keystore-protocol) overschrijven met behulp van een -Dkeystore.type parameter:

-Dkeystore.type = pkcs12

Of we kunnen natuurlijk een van de ondersteunde formaten in getInstance:

KeyStore ks = KeyStore.getInstance ("pcks12"); 

3.2. Initialisatie

In eerste instantie moeten we laden de keystore:

char [] pwdArray = "wachtwoord" .toCharArray (); ks.load (null, pwdArray); 

We gebruiken laden of we nu een nieuwe keystore maken of een bestaande openen.

En we vertellen KeyStore om een ​​nieuwe te maken door te passen nul als de eerste parameter.

We bieden ook een wachtwoord, dat in de toekomst zal worden gebruikt om toegang te krijgen tot de keystore. We kunnen dit ook instellen op nul, hoewel dat onze geheimen open zou maken.

3.3. Opslag

Ten slotte slaan we onze nieuwe keystore op in het bestandssysteem:

probeer (FileOutputStream fos = nieuwe FileOutputStream ("newKeyStoreFileName.jks")) {ks.store (fos, pwdArray); } 

Merk op dat hierboven niet weergegeven de verschillende aangevinkte uitzonderingen zijn die getInstance, laden, en winkel elke worp.

4. Laden van een keystore

Om een ​​keystore te laden, moeten we eerst een KeyStore bijvoorbeeld, zoals voorheen.

Laten we deze keer echter het formaat specificeren, aangezien we een bestaand formaat laden:

KeyStore ks = KeyStore.getInstance ("JKS"); ks.load (nieuwe FileInputStream ("newKeyStoreFileName.jks"), pwdArray);

Als onze JVM het keystore-type dat we hebben doorgegeven niet ondersteunt, of als het niet overeenkomt met het type keystore op het bestandssysteem dat we openen, krijgen we een KeyStoreException:

java.security.KeyStoreException: KEYSTORE_TYPE niet gevonden

Als het wachtwoord onjuist is, krijgen we ook een UnrecoverableKeyException:

java.security.UnrecoverableKeyException: wachtwoordverificatie mislukt

5. Invoeringen opslaan

In de keystore kunnen we drie verschillende soorten items opslaan, elk item onder zijn alias:

  • Symmetrische sleutels (in de JCE geheime sleutels genoemd),
  • Asymmetrische sleutels (in de JCE openbare en privésleutels genoemd), en
  • Vertrouwde certificaten

Laten we ze allemaal eens bekijken.

5.1. Een symmetrische sleutel opslaan

Het eenvoudigste dat we in een keystore kunnen opslaan, is een symmetrische sleutel.

Om een ​​symmetrische sleutel op te slaan, hebben we drie dingen nodig:

  1. een alias - hij is gewoon de naam die we in de toekomst zullen gebruiken om naar het item te verwijzen
  2. een sleutel - die is verpakt in een KeyStore.SecretKeyEntry.
  3. een wachtwoord - die is verpakt in wat wordt genoemd BeschermingParam.
KeyStore.SecretKeyEntry secret = nieuwe KeyStore.SecretKeyEntry (secretKey); KeyStore.ProtectionParameter wachtwoord = nieuwe KeyStore.PasswordProtection (pwdArray); ks.setEntry ("db-encryption-secret", geheim, wachtwoord);

Houd er rekening mee dat het wachtwoord niet kan zijn nul, het kan echter leeg zijn Draad.Als we het wachtwoord achterlaten nul voor een inzending krijgen we een KeyStoreException:

java.security.KeyStoreException: niet-nul wachtwoord vereist om SecretKeyEntry te maken

Het lijkt misschien een beetje raar dat we de sleutel en het wachtwoord in wrapper-klassen moeten verpakken.

We pakken de sleutel omdat setEntry is een generieke methode die ook voor de andere boekingstypen kan worden gebruikt. Het type invoer staat de KeyStore API om het anders te behandelen.

We verpakken het wachtwoord omdat de KeyStore API ondersteunt callbacks naar GUI's en CLI's om het wachtwoord van de eindgebruiker te verzamelen. Bekijk de KeyStore.CallbackHandlerProtection Javadoc voor meer details.

We kunnen deze methode ook gebruiken om een ​​bestaande sleutel bij te werken. We moeten het gewoon opnieuw bellen met hetzelfde alias en wachtwoord en ons nieuwe geheim.

5.2. Een privésleutel opslaan

Het opslaan van asymmetrische sleutels is een beetje ingewikkelder omdat we te maken hebben met certificaatketens.

Ook de KeyStore API geeft ons een speciale methode genaamd setKeyEntry wat handiger is dan de generieke setEntry methode.

Dus om een ​​asymmetrische sleutel op te slaan, hebben we vier dingen nodig:

  1. een alias, hetzelfde als voorheen
  2. een privésleutel. Omdat we de generieke methode niet gebruiken, wordt de sleutel niet ingepakt. Voor ons geval zou het ook een voorbeeld moeten zijn van Prive sleutel
  3. een wachtwoord voor toegang tot het item. Dit keer is het wachtwoord verplicht
  4. een certificaatketen die de bijbehorende openbare sleutel certificeert
X509Certificate [] certificateChain = nieuw X509Certificate [2]; chain [0] = clientCert; keten [1] = caCert; ks.setKeyEntry ("sso-ondertekeningssleutel", privateKey, pwdArray, certificateChain);

Nu kan er hier natuurlijk veel mis gaan, alsof pwdArray is nul:

java.security.KeyStoreException: wachtwoord mag niet null zijn

Maar er is een heel vreemde uitzondering om op te letten, en dat is als pwdArray is een lege array:

java.security.UnrecoverableKeyException: opgegeven laatste blok niet correct opgevuld

Om bij te werken, kunnen we de methode eenvoudig opnieuw aanroepen met dezelfde alias en een nieuwe prive sleutel en certificaatChain.

Het kan ook waardevol zijn om een ​​korte opfriscursus te doen hoe u een certificaatketen genereert.

5.3. Een vertrouwd certificaat opslaan

Het opslaan van vertrouwde certificaten is vrij eenvoudig. Het vereist alleen de alias en het certificaatzelf, die van het type is Certificaat:

ks.setCertificateEntry ("google.com", vertrouwd certificaat);

Meestal is het certificaat een certificaat dat we niet hebben gegenereerd, maar afkomstig van een derde partij.

Daarom is het belangrijk om dat hier op te merken KeyStore verifieert dit certificaat niet echt. We moeten het zelf verifiëren voordat we het opslaan.

Om bij te werken, kunnen we de methode eenvoudig opnieuw aanroepen met dezelfde alias en een nieuwe TrustedCertificate.

6. Inzendingen lezen

Nu we wat artikelen hebben geschreven, willen we ze zeker lezen.

6.1. Een enkel item lezen

Ten eerste kunnen we sleutels en certificaten ophalen met hun alias:

Sleutel ssoSigningKey = ks.getKey ("sso-ondertekeningssleutel", pwdArray); Certificaat google = ks.getCertificate ("google.com");

Als er geen vermelding met die naam is of het is van een ander type, dan getKey keert gewoon terug nul:

public void whenEntryIsMissingOrOfIncorrectType_thenReturnsNull () {// ... initialiseer keystore // ... voeg een item toe met de naam "widget-api-secret" Assert.assertNull (ks.getKey ("some-other-api-secret")); Assert.assertNotNull (ks.getKey ("widget-api-secret")); Assert.assertNull (ks.getCertificate ("widget-api-secret")); }

Maar als het wachtwoord voor de sleutel onjuist is, we krijgen dezelfde vreemde fout waar we het eerder over hadden:

java.security.UnrecoverableKeyException: opgegeven laatste blok niet correct opgevuld

6.2. Controleren of een keystore een alias bevat

Sinds KeyStore slaat alleen items op met een Kaart, het toont de mogelijkheid om te controleren op bestaan ​​zonder de invoer op te halen:

public void whenAddingAlias_thenCanQueryWithoutSaving () {// ... initialiseer keystore // ... voeg een item toe met de naam "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.containsAlias ​​("some-other-api-secret")); }

6.3. Het soort invoer controleren

Of, KeyStore#entryInstanceOf is een beetje krachtiger.

Het is als bevat Alias, behalve dat het ook het invoertype controleert:

public void whenAddingAlias_thenCanQueryByType () {// ... initialiseer keystore // ... voeg een geheim item toe genaamd "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.entryInstanceOf ("widget-api-secret", KeyType.PrivateKeyEntry.class)); }

7. Invoer verwijderen

KeyStore, natuurlijk,ondersteunt het verwijderen van de items die we hebben toegevoegd:

public void whenDeletingAnAlias_thenIdempotent () {// ... initialiseer een keystore // ... voeg een item toe met de naam "widget-api-secret"
 assertEquals (ks.size (), 1);
 ks.deleteEntry ("widget-api-secret"); ks.deleteEntry ("een-ander-api-geheim");
 assertFalse (ks.size (), 0); }

Gelukkig, deleteEntry is idempotent, dus de methode reageert hetzelfde, of het item nu bestaat of niet.

8. Verwijderen van een sleutelarchief

Als we onze keystore willen verwijderen, helpt de API ons niet, maar we kunnen nog steeds Java gebruiken om het te doen:

Files.delete (Paths.get (keystorePath));

Of, als alternatief, kunnen we de keystore behouden en gewoon items verwijderen:

Opsommingsaliassen = keyStore.aliases (); while (aliases.hasMoreElements ()) {String alias = aliases.nextElement (); keyStore.deleteEntry (alias); }

9. Conclusie

In dit artikel hebben we het gehad over het beheren van certificaten en sleutels met behulp van KeyStore-API. We hebben besproken wat een keystore is, hoe u er een kunt maken, laden en verwijderen, hoe u een sleutel of certificaat in de keystore opslaat en hoe u bestaande items met nieuwe waarden kunt laden en bijwerken.

De volledige implementatie van het voorbeeld is te vinden 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