Java KeyStore API

Java Top

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

U ovom uputstvu gledamo upravljanje kriptografskim ključevima i certifikatima u Javi pomoću KeyStore API.

2. Trgovine ključeva

Ako trebamo upravljati ključevima i certifikatima na Javi, trebamo pohrana ključeva, što je jednostavno sigurno prikupljanje aliasa unosi ključeva i potvrda.

Obično pohranjujemo pohrane ključeva u datotečni sustav i možemo ih zaštititi lozinkom.

Prema zadanim postavkama Java ima datoteku pohrane ključeva koja se nalazi na JAVA_HOME /jre/ lib / security / cacerts. Ovoj pohrani ključeva možemo pristupiti pomoću zadane lozinke pohrane ključeva promijeni to.

Sad, s tim dijelom pozadine, krenimo do stvaranja našeg prvog.

3. Stvaranje pohrane ključeva

3.1. Građevinarstvo

Lako možemo stvoriti pohranu ključeva pomoću alata za ključeve ili to možemo učiniti programski koristeći KeyStore API:

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

Ovdje koristimo zadani tip, iako je na raspolaganju nekoliko vrsta pohrane ključeva jceks ili pcks12.

Možemo nadjačati zadani tip "JKS" (Oracle-vlasnički protokol pohrane ključeva) pomoću a -Dkeystore.tip parametar:

-Dkeystore.type = pkcs12

Ili, naravno, možemo navesti jedan od podržanih formata u getInstance:

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

3.2. Inicijalizacija

U početku moramo opterećenje pohrana ključeva:

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

Koristimo opterećenje bez obzira stvaramo li novu pohranu ključeva ili otvaramo postojeću.

I, kažemo KeyStore stvoriti novi prolazeći null kao prvi parametar.

Također pružamo lozinku koja će se ubuduće koristiti za pristup pohrani ključeva. To također možemo postaviti na null, iako bi to otvorilo naše tajne.

3.3. Skladištenje

Na kraju, našu novu pohranu ključeva spremamo u datotečni sustav:

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

Imajte na umu da gore nije prikazano nekoliko provjerenih iznimaka koji getInstance, opterećenje, i pohraniti svako bacanje.

4. Učitavanje pohrane ključeva

Da bismo učitali pohranu ključeva, prvo moramo stvoriti KeyStore primjerice, kao prije.

No, ovoga puta navedimo format budući da učitavamo postojeći:

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

Ako naš JVM ne podržava tip pohrane ključeva koji smo proslijedili ili ako se ne podudara s tipom pohrane ključeva u datotečnom sustavu koji otvaramo, dobit ćemo KeyStoreException:

java.security.KeyStoreException: KEYSTORE_TYPE nije pronađen

Također, ako je lozinka pogrešna, dobit ćemo UnrecoverableKeyException:

java.security.UnrecoverableKeyException: Provjera lozinke nije uspjela

5. Pohranjivanje unosa

U pohrani ključeva možemo pohraniti tri različite vrste unosa, svaki unos pod svojim pseudonimom:

  • Simetrični ključevi (u UZP-u poznati kao tajni ključevi),
  • Asimetrični ključevi (u UZP-u poznati kao javni i privatni ključevi) i
  • Pouzdani certifikati

Pogledajmo svaku.

5.1. Spremanje simetričnog ključa

Najjednostavnije što možemo pohraniti u pohranu ključeva je simetrični ključ.

Da bismo spremili simetrični ključ, trebat će nam tri stvari:

  1. alias - njegovo je jednostavno ime koje ćemo ubuduće koristiti za upućivanje na unos
  2. ključ - koji je umotan u a KeyStore.SecretKeyEntry.
  3. lozinku - koji je umotan u ono što se naziva a ProtectionParam.
KeyStore.SecretKeyEntry secret = novi KeyStore.SecretKeyEntry (secretKey); KeyStore.ProtectionParameter lozinka = nova KeyStore.PasswordProtection (pwdArray); ks.setEntry ("db-encryption-secret", tajna, lozinka);

Imajte na umu da lozinka ne može biti null, međutim, može biti prazna Niz.Ako ostavimo lozinku null za unos ćemo dobiti KeyStoreException:

java.security.KeyStoreException: ne null lozinka potrebna za stvaranje SecretKeyEntry

Možda se čini pomalo čudno da ključ i lozinku moramo umotati u klase omota.

Zamotavamo ključ jer setEntry je generička metoda koja se može koristiti i za ostale vrste unosa. Vrsta unosa omogućuje KeyStore API za različito postupanje.

Obmotavamo lozinku jer KeyStore API podržava povratne pozive za GUI i CLI za prikupljanje lozinke od krajnjeg korisnika. Pogledajte KeyStore.CallbackHandlerProtection Javadoc za više detalja.

Ovu metodu možemo koristiti i za ažuriranje postojećeg ključa. Samo ga moramo ponovno nazvati s istim pseudonimom i lozinkom i našim novim tajna.

5.2. Spremanje privatnog ključa

Pohranjivanje asimetričnih ključeva nešto je složenije jer se moramo nositi s lancima certifikata.

Također, KeyStore API nam daje namjensku metodu tzv setKeyEntry što je prikladnije od generičkog setEntry metoda.

Dakle, za spremanje asimetričnog ključa trebat će nam četiri stvari:

  1. alias, isto kao prije
  2. privatni ključ. Budući da ne koristimo generičku metodu, ključ se neće zamotati. Također, u našem slučaju, to bi trebao biti primjer PrivateKey
  3. lozinku za pristup unosu. Ovaj put je lozinka obavezna
  4. lanac certifikata koji potvrđuje odgovarajući javni ključ
X509Certificate [] certificateChain = novi X509Certificate [2]; lanac [0] = clientCert; lanac [1] = caCert; ks.setKeyEntry ("ključ sso-potpisivanja", privateKey, pwdArray, certificateChain);

Sad ovdje, naravno, može puno pogriješiti, kao da pwdArray je null:

java.security.KeyStoreException: lozinka ne može biti null

Ali, mora postojati zaista čudna iznimka, a to je ako pwdArray je prazan niz:

java.security.UnrecoverableKeyException: Dati završni blok nije pravilno podstavljen

Da bismo je ažurirali, možemo jednostavno ponovno pozvati metodu s istim aliasom i novim privateKey i certifikatChain.

Također, možda bi bilo drago obaviti kratko osvježavanje kako generirati lanac certifikata.

5.3. Spremanje pouzdanog certifikata

Pohranjivanje pouzdanih certifikata prilično je jednostavno. Potreban je samo pseudonim i potvrdasebe, koji je tipa Potvrda:

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

Potvrda je obično ona koju nismo generirali, ali je došla od treće strane.

Zbog toga je ovdje važno napomenuti da KeyStore zapravo ne potvrđuje ovu potvrdu. Prije spremanja trebali bismo ga sami provjeriti.

Da bismo je ažurirali, možemo jednostavno ponovno pozvati metodu s istim aliasom i novim trustedCertificate.

6. Čitanje članaka

Sad kad smo napisali neke unose, sigurno ćemo ih htjeti pročitati.

6.1. Čitanje jednog unosa

Prvo, ključeve i certifikate možemo izvući prema zamjenskim imenima:

Ključ ssoSigningKey = ks.getKey ("ključ sso-potpisivanja", pwdArray); Potvrda google = ks.getCertificate ("google.com");

Ako nema unosa s tim imenom ili je drugačije vrste, onda getKey jednostavno se vraća null:

javna praznina whenEntryIsMissingOrOfIncorrectType_thenReturnsNull () {// ... inicijaliziranje trgovine ključeva // ... dodajte unos pod nazivom "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")); }

Ali, ako je lozinka za ključ pogrešna, dobit ćemo onu istu neobičnu pogrešku o kojoj smo ranije govorili:

java.security.UnrecoverableKeyException: Dati završni blok nije pravilno podstavljen

6.2. Provjera sadrži li pohrana ključeva pseudonim

Od KeyStore samo pohranjuje unose pomoću a Karta, izlaže mogućnost provjere postojanja bez dohvaćanja unosa:

javna praznina kadaAddingAlias_thenCanQueryWithoutSaving () {// ... inicijalizira pohranu ključeva // ... dodaj unos pod nazivom "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.containsAlias ​​("neki-drugi-api-tajna")); }

6.3. Provjera vrste ulaska

Ili, KeyStore#entryInstanceOf je malo moćniji.

To je kao sadrži Alias, osim što također provjerava vrstu unosa:

javna praznina kadaAddingAlias_thenCanQueryByType () {// ... inicijalizira pohranu ključeva // ... dodaj tajni unos pod nazivom "widget-api-secret"
 assertTrue (ks.containsAlias ​​("widget-api-secret")); assertFalse (ks.entryInstanceOf ("widget-api-secret", KeyType.PrivateKeyEntry.class)); }

7. Brisanje unosa

KeyStore, naravno,podržava brisanje unosa koje smo dodali:

javna praznina whenDeletingAnAlias_thenIdempotent () {// ... inicijalizacija pohrane ključeva // ... dodavanje unosa pod nazivom "widget-api-secret"
 assertEquals (ks.size (), 1);
 ks.deleteEntry ("widget-api-secret"); ks.deleteEntry ("neka-druga-api-tajna");
 assertFalse (ks.size (), 0); }

Srećom, deleteEntry je idempotentan, tako da metoda reagira isto, bez obzira postoji li unos.

8. Brisanje pohrane ključeva

Ako želimo izbrisati našu pohranu ključeva, API nam nije od pomoći, ali za to još uvijek možemo koristiti Javu:

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

Ili, kao alternativu, možemo držati spremište ključeva i jednostavno uklanjati unose:

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

9. Zaključak

U ovom smo članku govorili o upravljanju certifikatima i ključevima API KeyStore. Razgovarali smo o tome što je pohrana ključeva, kako je stvoriti, učitati i izbrisati, kako pohraniti ključ ili certifikat u pohranu ključeva i kako učitati i ažurirati postojeće unose s novim vrijednostima.

Potpuna provedba primjera može se naći na Githubu.

Dno Java

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ