Java KeyStore API

Java Top

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

1. Oversigt

I denne vejledning ser vi på styring af kryptografiske nøgler og certifikater i Java ved hjælp af KeyStore API.

2. Keystores

Hvis vi har brug for at administrere nøgler og certifikater i Java, har vi brug for en keystore, som simpelthen er en sikker samling af alias poster af nøgler og certifikater.

Vi gemmer typisk nøgleforretninger i et filsystem, og vi kan beskytte det med en adgangskode.

Som standard har Java en keystore-fil placeret på JAVA_HOME /jre/ lib / sikkerhed / cacerts. Vi kan få adgang til denne nøglelager ved hjælp af adgangskoden til standardnøglelageret Ændre det.

Nu med den smule baggrund, lad os komme til at skabe vores første.

3. Oprettelse af en Keystore

3.1. Konstruktion

Vi kan nemt oprette en keystore ved hjælp af keytool, eller vi kan gøre det programmatisk ved hjælp af KeyStore API:

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

Her bruger vi standardtypen, selvom der er et par keystore-typer tilgængelige som jceks eller pcks12.

Vi kan tilsidesætte standardtypen "JKS" (en Oracle-proprietær keystore-protokol) ved hjælp af en -Dkeystore.type parameter:

-Dkeystore.type = pkcs12

Eller vi kan selvfølgelig liste et af de understøttede formater i getInstance:

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

3.2. Initialisering

Oprindeligt skal vi belastning nøglelageret:

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

Vi bruger belastning om vi opretter en ny keystore eller åbner en eksisterende.

Og det fortæller vi KeyStore at skabe en ny ved at passere nul som den første parameter.

Vi leverer også en adgangskode, som vil blive brugt til at få adgang til keystore i fremtiden. Vi kan også indstille dette til nul, selvom det ville gøre vores hemmeligheder åbne.

3.3. Opbevaring

Endelig gemmer vi vores nye nøglelager i filsystemet:

prøv (FileOutputStream fos = ny FileOutputStream ("newKeyStoreFileName.jks")) {ks.store (fos, pwdArray); } 

Bemærk, at ikke vist ovenfor er de flere kontrollerede undtagelser, der getInstance, belastning, og butik hvert kast.

4. Indlæsning af en Keystore

For at indlæse en keystore skal vi først oprette en KeyStore eksempel som før.

Denne gang skal vi dog specificere formatet, da vi indlæser et eksisterende format:

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

Hvis vores JVM ikke understøtter den keystore-type, vi har bestået, eller hvis den ikke matcher typen af ​​keystore på det filsystem, vi åbner, får vi en KeyStoreException:

java.security.KeyStoreException: KEYSTORE_TYPE ikke fundet

Også, hvis adgangskoden er forkert, får vi en UnrecoverableKeyException:

java.security.UnrecoverableKeyException: Bekræftelse af adgangskode mislykkedes

5. Lagring af poster

I nøglelageret kan vi gemme tre forskellige slags poster, hver post under dets alias:

  • Symmetriske nøgler (kaldet hemmelige nøgler i JCE),
  • Asymmetriske nøgler (benævnt offentlige og private nøgler i JCE) og
  • Pålidelige certifikater

Lad os se på hver enkelt.

5.1. Gemme en symmetrisk nøgle

Den enkleste ting, vi kan gemme i en keystore, er en symmetrisk nøgle.

For at gemme en symmetrisk nøgle har vi brug for tre ting:

  1. et alias - hans er simpelthen det navn, vi vil bruge i fremtiden til at henvise til posten
  2. en nøgle - som er pakket i en KeyStore.SecretKeyEntry.
  3. et kodeord - som er pakket ind i det, der kaldes en Beskyttelse Param.
KeyStore.SecretKeyEntry secret = ny KeyStore.SecretKeyEntry (secretKey); KeyStore.ProtectionParameter password = ny KeyStore.PasswordProtection (pwdArray); ks.setEntry ("db-kryptering-hemmelighed", hemmelighed, adgangskode);

Husk, at adgangskoden ikke kan være nul, dog kan det være tomt Snor.Hvis vi efterlader adgangskoden nul for en post får vi en KeyStoreException:

java.security.KeyStoreException: adgangskode, der ikke er nul, kræves for at oprette SecretKeyEntry

Det kan virke lidt underligt, at vi skal indpakke nøglen og adgangskoden i indpakningsklasser.

Vi pakker nøglen ind, fordi setEntry er en generisk metode, der også kan bruges til de andre indgangstyper. Indtastningstypen tillader KeyStore API til at behandle det forskelligt.

Vi indpakker adgangskoden, fordi KeyStore API understøtter tilbagekald til GUI'er og CLI'er for at indsamle adgangskoden fra slutbrugeren. Tjek KeyStore.CallbackHandlerProtection Javadoc for flere detaljer.

Vi kan også bruge denne metode til at opdatere en eksisterende nøgle. Vi skal bare ringe til det igen med det samme alias og adgangskode og vores nye hemmelighed.

5.2. Gemme en privat nøgle

Opbevaring af asymmetriske nøgler er lidt mere kompliceret, da vi har brug for certifikatkæder.

Også den KeyStore API giver os en dedikeret metode kaldet setKeyEntry hvilket er mere praktisk end det generiske setEntry metode.

Så for at gemme en asymmetrisk nøgle har vi brug for fire ting:

  1. et alias, samme som før
  2. en privat nøgle. Fordi vi ikke bruger den generiske metode, bliver nøglen ikke pakket. Også for vores sag skal det være en forekomst af PrivateKey
  3. et kodeord for at få adgang til posten. Denne gang er adgangskoden obligatorisk
  4. en certifikatkæde der certificerer den tilsvarende offentlige nøgle
X509Certificate [] certificateChain = nyt X509Certificate [2]; kæde [0] = clientCert; kæde [1] = caCert; ks.setKeyEntry ("sso-signatur-nøgle", privateKey, pwdArray, certificateChain);

Nu kan meget gå galt her, selvfølgelig, som hvis pwdArray er nul:

java.security.KeyStoreException: adgangskode kan ikke være nul

Men der er en virkelig underlig undtagelse at være opmærksom på, og det er hvis pwdArray er en tom matrix:

java.security.UnrecoverableKeyException: Givet endelig blok ikke korrekt polstret

For at opdatere kan vi blot kalde metoden igen med det samme alias og et nyt privateKey og certifikatKæde.

Det kan også være værdifuldt at lave en hurtig opdatering på hvordan man genererer en certifikatkæde.

5.3. Gemme et betroet certifikat

Opbevaring af pålidelige certifikater er ret simpelt. Det kræver kun aliaset og certifikatetsig selv, som er af typen Certifikat:

ks.setCertificateEntry ("google.com", trustedCertificate);

Normalt er certifikatet et, som vi ikke genererede, men som kom fra en tredjepart.

På grund af det er det vigtigt at bemærke her, at KeyStore bekræfter faktisk ikke dette certifikat. Vi skal kontrollere det alene, før vi gemmer det.

For at opdatere kan vi blot kalde metoden igen med det samme alias og et nyt trustCertificate.

6. Læsning af indlæg

Nu hvor vi har skrevet nogle poster, vil vi bestemt læse dem.

6.1. Læsning af en enkelt post

For det første kan vi trække nøgler og certifikater ud af deres alias:

Nøgle ssoSigningKey = ks.getKey ("sso-signatur-nøgle", pwdArray); Certifikat google = ks.getCertificate ("google.com");

Hvis der ikke er nogen indtastning ved det navn, eller det er af en anden type, så getKey bare vender tilbage nul:

offentlig ugyldig nårEntryIsMissingOrOfIncorrectType_thenReturnsNull () {// ... initialiser keystore // ... tilføj en post kaldet "widget-api-secret" Assert.assertNull (ks.getKey ("noget andet-api-hemmeligt")); Assert.assertNotNull (ks.getKey ("widget-api-hemmelighed")); Assert.assertNull (ks.getCertificate ("widget-api-hemmelighed")); }

Men hvis adgangskoden til nøglen er forkert, vi får den samme ulige fejl, vi talte om tidligere:

java.security.UnrecoverableKeyException: Givet endelig blok ikke korrekt polstret

6.2. Kontrollerer, om en Keystore indeholder et alias

Siden KeyStore gemmer bare poster ved hjælp af en Kort, det udsætter muligheden for at kontrollere eksistensen uden at hente posten:

offentligt ugyldigt nårAddingAlias_thenCanQueryWithoutSaving () {// ... initialiser keystore // ... tilføj en post kaldet "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.containsAlias ​​("noget andet-api-hemmeligt")); }

6.3. Kontrol af indgangstypen

Eller, KeyStore#entryInstanceOf er lidt mere kraftfuld.

Det er ligesom indeholderAlias, medmindre det også kontrollerer indtastningstypen:

offentligt ugyldigt nårAddingAlias_thenCanQueryByType () {// ... initialiser nøglelager // ... tilføj en hemmelig post kaldet "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.entryInstanceOf ("widget-api-secret", KeyType.PrivateKeyEntry.class)); }

7. Sletning af poster

KeyStore, selvfølgelig,understøtter sletning af de poster, vi har tilføjet:

offentligt ugyldigt nårDeletingAnAlias_thenIdempotent () {// ... initialiser en nøglelager // ... tilføj en post kaldet "widget-api-secret"
 assertEquals (ks.size (), 1);
 ks.deleteEntry ("widget-api-hemmelighed"); ks.deleteEntry ("noget andet api-hemmeligt");
 assertFalse (ks.størrelse (), 0); }

Heldigvis, deleteEntry er idempotent, så metoden reagerer det samme, uanset om posten findes eller ej.

8. Sletning af en Keystore

Hvis vi vil slette vores nøglelager, er API'en ikke nogen hjælp for os, men vi kan stadig bruge Java til at gøre det:

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

Eller som et alternativ kan vi holde nøglelageret rundt og bare fjerne poster:

Optællingsaliaser = keyStore.aliases (); mens (aliases.hasMoreElements ()) {String alias = aliases.nextElement (); keyStore.deleteEntry (alias); }

9. Konklusion

I denne artikel talte vi om styring af certifikater og nøgler ved hjælp af KeyStore API. Vi diskuterede, hvad en keystore er, hvordan man opretter, indlæser og sletter en, hvordan man gemmer en nøgle eller et certifikat i keystore, og hvordan man indlæser og opdaterer eksisterende poster med nye værdier.

Den fulde implementering af eksemplet findes på Github.

Java bund

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN