Bezpieczeństwo wymiany danych to jedna z najważniejszych cech współczesnych aplikacji. Od czasów starożytnych ludzie wymyślali przebiegłe metody, które wraz z rozwojem ludzkości stały się całą nauką kryptografii. Oczywiście Java nie pozostała z boku i zaoferowała programistom architekturę Java Cryptography Architecture (JCA). Ta recenzja powinna dać pierwsze pojęcie o tym, jak to działa.
Co to może oznaczać
Jak mówi dokumentacja: „ Platforma Java zawiera wielu wbudowanych dostawców ”. Oznacza to, że platforma Java udostępnia zestaw wbudowanych dostawców, których w razie potrzeby można rozszerzyć. Możesz to zobaczyć na własne oczy:
Może to wyglądać tak w kodzie:
Aby uniknąć powtórzeń w tym przypadku, należy skorzystać z innego trybu - Cipher Block Chaining (CBC). W tym trybie wprowadzono koncepcję wektora inicjującego (reprezentowanego przez klasę IvParameterSpec). A także dzięki temu trybowi wynik wygenerowania ostatniego bloku zostanie wykorzystany do wygenerowania następnego:
Zapiszmy to teraz w kodzie:
Powyżej widzieliśmy, jak strony wymieniają dane. Czy w JCA nie ma standardowego interfejsu dla tej interakcji? Okazuje się, że istnieje. Spójrzmy na to.
Przedmowa
Proponuję podróż w czasie. Przed nami starożytny Rzym. A przed nami Gajusz Juliusz Cezar, który wysyła wiadomość do swoich dowódców. Zobaczmy, co jest w tej wiadomości:"ЕСКЕУГЬГМХИФЯ Е УЛП"
:? Otwórzmy kompilator Java Online, na przykład: repl.it
class Main {
public static void main(String[] args) {
String code = "ЕСКЕУГЬГМХИФЯ Е УЛП";
for (char symbol : code.toCharArray()) {
if (symbol != ' ') {
symbol = (char) (symbol - 3);
}
System.out.print(symbol);
}
}
}
Przed nami najprostsza implementacja szyfru Cezara. Według dzieła starożytnego rzymskiego historyka Swetoniusza zatytułowanego „Żywoty dwunastu cezarów” dokładnie w ten sposób Cezar szyfrował wiadomości dla swoich generałów. I to jest jedno z najstarszych odniesień do stosowania czegoś takiego jak kryptografia . Słowo „kryptografia” pochodzi od starożytnych greckich słów „ukryty” i „pisz”, tj. jest to nauka o technikach zapewniających prywatność. Java ma własną obsługę kryptografii i nosi nazwę Java Cryptography Architecture (JCA). Opis można znaleźć w oficjalnej dokumentacji firmy Oracle - „ Java Cryptography Architecture (JCA) ”. Proponuję przyjrzeć się jakie możliwości daje nam JCA.
JCA
Jak dowiedzieliśmy się wcześniej, Java oferuje architekturę Java Cryptography Architecture (JCA) do pracy z kryptografią. Architektura ta zawiera API (tj. pewien zestaw interfejsów) i dostawców (którzy je implementują):import java.security.Provider;
import java.security.Security;
class Main {
public static void main(String[] args) {
Provider[] providers = Security.getProviders();
for (Provider p : providers) {
System.out.println(p.getName());
}
}
}
Rejestracja zewnętrznego dostawcy jest bardzo łatwa. Przykład: Security.addProvider(new BouncyCastleProvider());
Ten przykład łączy jednego z najbardziej znanych dostawców - BouncyCastle . Ale w tej recenzji użyjemy tylko podstawowych narzędzi, bez bibliotek stron trzecich. Nasz główny dokument: „ Architektura kryptografii Java (JCA) ”. Zrozumienie działania JCA pomoże Ci łatwiej zrozumieć technologie, w których aktywnie wykorzystuje się tę samą JCA. Na przykład: HTTPS (patrz „ Od HTTP do HTTPS ”).
Przegląd wiadomości
Pierwszą rzeczą wymienioną w dokumentacji JCA jest MessageDigest. Ogólnie rzecz biorąc, podsumowanie w języku rosyjskim będzie takie samo - podsumowanie odpowiada w znaczeniu „podsumowaniu”. Ale w kryptografii skrót jest sumą skrótu. Możesz także łatwo zapamiętać, że w języku angielskim Digest można również przetłumaczyć jako streszczenie. Więcej szczegółów można znaleźć w dokumentacji JCA w sekcji „ MessageDigest ”. Jak mówi dokumentacja, MessageDigest generuje wynik o stałym rozmiarze, zwany skrótem lub skrótem. Haszowanie jest funkcją jednokierunkową, tj. jeśli coś zahaszowaliśmy, to z wyniku (tj. z hasha) nie możemy uzyskać oryginalnego źródła. Jeśli jednak identyczne obiekty zostaną zahaszowane (na przykład ciągi identycznych znaków), wówczas ich skrót musi się zgadzać. Jak stwierdzono w dokumentacji, taki skrót jest czasami nazywany „sumą kontrolną” lub „cyfrowym odciskiem palca” danych. Haszowanie można przeprowadzić przy użyciu różnych algorytmów. Dostępne algorytmy można zobaczyć w dokumencie „ Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8 ”. Zróbmy hashowanie i wypiszmy hash na konsoli:import javax.xml.bind.DatatypeConverter;
import java.security.*;
public class Main {
public static void main(String[] args) {
try {
MessageDigest digester = MessageDigest.getInstance("SHA-512");
byte[] input = "Secret string".getBytes();
byte[] digest = digester.digest(input);
System.out.println(DatatypeConverter.printHexBinary(digest));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
}
Haszowanie może być przydatne na przykład podczas przechowywania haseł. Ponieważ skrót wprowadzonego hasła można porównać z wcześniej zapisanym skrótem. Jeśli skróty się zgadzają, hasło również się zgadza. W celu jeszcze bezpieczniejszego mieszania używana jest koncepcja zwana „sól”. Sól można zaimplementować przy użyciu klasy SecureRandom . Przed wykonaniem metody Digest opiszemy dodanie „soli”:
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
Ale hash jest funkcją jednokierunkową. Ale co, jeśli chcesz mieć możliwość szyfrowania i deszyfrowania?
Kryptografia klucza symetrycznego
Szyfrowanie symetryczne to szyfrowanie wykorzystujące ten sam klucz do szyfrowania i deszyfrowania. Aby skorzystać z szyfrowania symetrycznego potrzebny jest klucz. Aby to uzyskać, używamy KeyGenerator . Dodatkowo będziemy potrzebować klasy reprezentującej szyfr ( Cipher ). Jak stwierdzono w dokumentacji JCA w sekcji „ Tworzenie obiektu szyfru ”, aby utworzyć szyfr, należy określić nie tylko algorytm, ale także „transformację” w linii. Opis transformacji wygląda następująco: „algorytm/tryb/wypełnienie”:- Algorytm : tutaj przyjrzymy się standardowym nazwom „ algorytmów szyfrowania (szyfrowania) ”. Zalecane jest użycie AES.
- Tryb : tryb szyfrowania. Na przykład: EBC lub CBC (porozmawiamy o tym trochę później)
- Wcięcie/podział : każdy blok danych jest szyfrowany osobno. Ten parametr określa, ile danych jest liczonych jako 1 blok.
"AES/ECB/PKCS5Padding"
. Oznacza to, że algorytm szyfrowania to AES, tryb szyfrowania to ECB (skrót od Electronic Codebook), rozmiar bloku to PKCS5Padding. PKCS5Padding mówi, że rozmiar jednego bloku wynosi 2 bajty (16 bitów). Tryb szyfrowania Elektronicznej Książki Kodowej obejmuje sekwencyjne szyfrowanie każdego bloku:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.Key;
public class Main {
public static void main(String[] args) throws Exception {
String text = "secret!!secret!!secret!!secret!!";
// Generate new key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(256);
Key key = keygen.generateKey();
// Encrypt with key
String transformation = "AES/ECB/PKCS5Padding";
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(text.getBytes());
System.out.println(DatatypeConverter.printHexBinary(encrypted));
// Decrypt with key
cipher.init(Cipher.DECRYPT_MODE, key);
String result = new String(cipher.doFinal(encrypted));
System.out.println(result);
}
}
Jeśli wykonamy, spodziewamy się powtórki, ponieważ określiliśmy 32 znaki. Znaki te tworzą 2 bloki po 16 bitów:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.IvParameterSpec;
public class Main {
public static void main(String[] args) throws Exception {
// Initialization Vector
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] rnd = new byte[16];
random.nextBytes(rnd);
IvParameterSpec ivSpec = new IvParameterSpec(rnd);
// Prepare key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(256);
Key key = keygen.generateKey();
// CBC
String text = "secret!!secret!!secret!!secret!!";
String transformation = "AES/CBC/PKCS5Padding";
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] enc = cipher.doFinal(text.getBytes());
System.out.println(DatatypeConverter.printHexBinary(enc));
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
String result = new String(cipher.doFinal(enc));
System.out.println(result);
}
}
Jak widzimy, w rezultacie nie widzimy powtarzających się bloków szyfru. Z tego powodu nie zaleca się trybu EBC, ponieważ umożliwia dostrzeżenie powtórzeń i wykorzystanie tej wiedzy do odszyfrowania. Aby uzyskać więcej informacji na temat EBC i CBC, polecam zapoznać się z materiałem: „ Tryb elektronicznej książki kodowej ”. Ale szyfrowanie symetryczne ma oczywisty problem - trzeba w jakiś sposób przenieść klucz od tego, kto szyfruje, do tego, kto szyfruje. I wzdłuż tej ścieżki klucz ten będzie mógł zostać przechwycony i wtedy możliwe będzie przechwycenie danych. Szyfrowanie asymetryczne ma na celu rozwiązanie tego problemu.
Szyfrowanie asymetryczne
Szyfrowanie asymetryczne lub kryptografia klucza publicznego to metoda szyfrowania wykorzystująca parę kluczy: klucz prywatny (utrzymywany w tajemnicy przed wszystkimi) i klucz publiczny (dostępny publicznie). To rozdzielenie jest konieczne, aby bezpiecznie wymieniać klucz publiczny pomiędzy stronami wymiany informacji, zachowując jednocześnie bezpieczeństwo klucza tajnego. Przy tworzeniu pary kluczy nie wystarczy nam już KeyGenerator, potrzebujemy KeyPairGenerator . Spójrzmy na przykład:import javax.crypto.*;
import java.security.*;
public class Main {
public static void main(String[] args) throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
KeyPair keyPair = generator.generateKeyPair();
// Encrypt with PRIVATE KEY
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] data = cipher.doFinal("Hello!".getBytes());
// Decrypt with PUBLIC KEY
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] result = cipher.doFinal(data);
System.out.println(new String(result));
}
}
Ważne jest, aby zrozumieć, że podczas korzystania z szyfrowania asymetrycznego zawsze używamy KeyPair, aby używać jednego klucza do szyfrowania, a drugiego do deszyfrowania. Ale ponieważ Istota szyfrowania polega na tym, że tylko odbiorca może go odszyfrować; jest on szyfrowany kluczem publicznym i odszyfrowywany tylko kluczem prywatnym.
Podpis cyfrowy
Jak widzieliśmy powyżej, znając klucz publiczny, możesz wysłać dane, aby tylko właściciel klucza prywatnego mógł je odszyfrować. Oznacza to, że istotą szyfrowania asymetrycznego jest to, że każdy szyfruje, ale tylko my czytamy. Istnieje również procedura odwrotna - podpis cyfrowy, reprezentowany przez klasę Signature . Podpis cyfrowy może wykorzystywać następujące algorytmy: „ Algorytmy podpisu ”. Dokumentacja JCA sugeruje przyjrzenie się bliżej tym dwóm: DSAwithMD5 i RSAwithMD5. Co jest lepsze niż DSA lub RSA i jaka jest między nimi różnica, możesz przeczytać tutaj: „ Który działa najlepiej w przypadku szyfrowanych transferów plików – RSA czy DSA? ”. Lub przeczytaj dyskusje tutaj: „ RSA vs. DSA dla kluczy uwierzytelniających SSH ”. A więc podpis cyfrowy. Będziemy potrzebować, tak jak poprzednio, pary kluczy i nowej klasy Signature. Jeśli do tej pory testowałeś w kompilatorach online, poniższy przykład może być dla nich nieco trudny. Mój przykład działał tylko tutaj: rextester.com . Importujemy potrzebne nam klasy:import javax.crypto.*;
import java.security.*;
Przepiszemy także główną metodę:
public static void main(String[] args) throws Exception {
// Generate keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstanceStrong();
generator.initialize(2048, random);
KeyPair keyPair = generator.generateKeyPair();
// Digital Signature
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initSign(keyPair.getPrivate());
// Update and sign the data
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] data = cipher.doFinal("Hello!".getBytes());
dsa.update(data);
byte[] signature = dsa.sign();
// Verify signature
dsa.initVerify(keyPair.getPublic());
dsa.update(data);
boolean verifies = dsa.verify(signature);
System.out.println("Signature is ok: " + verifies);
// Decrypt if signature is correct
if (verifies) {
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] result = cipher.doFinal(data);
System.out.println(new String(result));
}
}
Tak działa podpis cyfrowy. Podpis cyfrowy to ciekawy temat. Radzę zapoznać się z raportem na ten temat:
Kluczowa Umowa
Architektura kryptografii Java wprowadza ważne narzędzie - Umową kluczową jest protokół. Jest reprezentowana przez klasę KeyAgreement . Jak stwierdzono w dokumentacji JCA, protokół ten umożliwia wielu stronom ustawienie tego samego klucza kryptograficznego bez udostępniania jakichkolwiek tajnych informacji między stronami. Brzmi dziwnie? Następnie spójrzmy na przykład:// 1. Одна из сторон (Алиса) генерирует пару ключей. Encoded публичный ключ отдаёт.
KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
KeyPair aliceKeyPair = generator.generateKeyPair();
byte[] alicePubKeyEncoded = aliceKeyPair.getPublic().getEncoded();
// 2. Другая сторона (например, Боб) получает открытый ключ Алисы
KeyFactory bobKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEncoded);
PublicKey alicePubKey = bobKeyFactory.generatePublic(x509KeySpec);
// Параметры, которые использовала Алиса при генерации ключей
DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();
// Создаёт свою пару ключей. Отдаёт свой Encoded открытый ключ
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamFromAlicePubKey);
KeyPair bobKeyPair = bobKpairGen.generateKeyPair();
byte[] bobPubKeyEncoded = bobKeyPair.getPublic().getEncoded();
Теперь, у Алисы есть открытый ключ Боба, а у Боба есть открытый ключ Алисы. Co дальше?
Как сказано в dokumentации JCA, у нас есть инструмент KeyAgreement, https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyAgreement который позволяет установить одинаковые ключи шифрования без необходимости обмениваться секретной информацией (т.е. без обмена private key). Соглашение выглядит следующим образом:
// 3. Соглашение по протоколу Диффи-Хеллмана (Diffie–Hellman, DH)
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKeyPair.getPrivate());
// Алиса на основе ключа боба и своего private key создаёт общий shared ключ
KeyFactory aliceKeyFactory = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEncoded);
PublicKey bobPubKey = aliceKeyFactory.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, "AES");
// Боб на основе ключа Алисы и своего private key создаёт общий shared ключ
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKeyPair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
byte[] bobSharedSecret = bobKeyAgree.generateSecret();
SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, "AES");
// Общий ключ у Алисы и Боба одинаков
System.out.println("Shared keys are equals: " + Arrays.equals(aliceSharedSecret, bobSharedSecret));
Далее Боб и Алиса, используя общий ключ, про который больше никто не знает, обмениваются зашифрованными данными:
// 4. Боб шифрует сообщение для Алисы
Cipher bobCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
bobCipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
byte[] ciphertext = bobCipher.doFinal("Hello, Alice!".getBytes());
// Передаёт Алисе параметры, с которыми выполнялась шифровка
byte[] encodedParamsFromBob = bobCipher.getParameters().getEncoded();
// 5. Алиса принимает сообщение и расшифровывает его
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(encodedParamsFromBob);
Cipher aliceCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
byte[] recovered = aliceCipher.doFinal(ciphertext);
System.out.println(new String(recovered));
Ten przykład został zaczerpnięty z przykładowej dokumentacji JCA: „ Wymiana kluczy Diffiego-Hellmana między 2 stronami ”. Tak mniej więcej wygląda szyfrowanie asymetryczne w architekturze Java Cryptography przy użyciu protokołu uzgadniania klucza. Więcej informacji na temat szyfrowania asymetrycznego można znaleźć w polecanych filmach:
Certyfikaty
Cóż, na deser mamy jeszcze coś nie mniej ważnego – certyfikaty. Zwykle certyfikaty są generowane przy użyciu narzędzia keytool dołączonego do pakietu jdk. Więcej szczegółów możesz przeczytać na przykład tutaj: „ Generowanie certyfikatu SSL z podpisem własnym za pomocą polecenia Java keytool ”. Możesz także przeczytać podręczniki firmy Oracle. Na przykład tutaj: „ Aby użyć narzędzia keytool do utworzenia certyfikatu serwera ”. Na przykład użyjmy kompilatora Java Online Tutorialspoint :import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.*;
import java.security.cert.*;
import java.security.*;
// Compiler args: -XDignore.symbol.file
public class Main {
public static void main(String[] args) throws Exception {
CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
// generate it with 2048 bits
certGen.generate(2048);
PrivateKey privateKey = certGen.getPrivateKey();
X509Key publicKey = certGen.getPublicKey();
// prepare the validity of the certificate
long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
// enter your details according to your application
X500Name principal = new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE");
// add the certificate information, currently only valid for one year.
X509Certificate cert = certGen.getSelfCertificate(principal, validSecs);
// Public Key from Cert equals Public Key from generator
PublicKey publicKeyFromCert = cert.getPublicKey();
System.out.println(publicKeyFromCert.equals(publicKey));
}
}
Jak widzimy, certyfikat zapewnia możliwość dostarczenia klucza publicznego. Ta metoda ma wadę - używamy sun.security
, co jest uważane za ryzykowne, ponieważ... ten pakiet nie jest częścią publicznego API Java. Dlatego podczas kompilacji konieczne jest określenie parametru - XDignore.symbol.file
. Jest inny sposób - ręczne utworzenie certyfikatu. Wadą jest to, że wykorzystuje wewnętrzne API, które nie jest udokumentowane. Warto jednak o tym wiedzieć. Przynajmniej, bo wyraźnie widać, w jaki sposób używana jest specyfikacja RFC-2459: „ Internetowa infrastruktura klucza publicznego X.509 ”. Oto przykład:
// 1. Генерируем пару ключей
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 2. Определяем данные сертификата
// Определяем срок действия сертификата
Date from = new Date();
Date to = new Date(from.getTime() + 365 * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(from, to);
// Определяем subject name, т.е. Nazwa того, с чем ассоциирован публичный ключ
// CN = Common Name. Через точку с запятой могут быть указаны также другие атрибуты
// См. https://docs.oracle.com/cd/E24191_01/common/tutorials/authz_cert_attributes.html
X500Name owner = new X500Name("cn=Unknown");
// Уникальный в пределах CA, т.е. Certificate Authority (тот, кто выдаёт сертификат) номер
BigInteger number = new BigInteger(64, new SecureRandom());
CertificateSerialNumber serialNumber = new CertificateSerialNumber(number);
// Определяем алгоритм подписи сертификата
AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
CertificateAlgorithmId certificateAlgorithmId = new CertificateAlgorithmId(algorithmId);
// 3. По подготовленной информации создаём сертификат
X509CertInfo info = new X509CertInfo();
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, serialNumber);
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, certificateAlgorithmId);
// 4. Подписываем сертификат
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), "SHA256withRSA");
// 5. Проверка сертификата
try {
// В случае ошибки здесь будет брошено исключение. Например: java.security.SignatureException
certificate.verify(keyPair.getPublic());
} catch (Exception e) {
throw new IllegalStateException(e);
}
Magazyn kluczy (magazyn kluczy)
Ostatnią rzeczą, o której chciałbym porozmawiać, jest magazyn kluczy i certyfikatów, który nazywa się KeyStore. Wiadomo, że ciągłe generowanie certyfikatów i kluczy jest kosztowne i bezcelowe. Dlatego należy je jakoś bezpiecznie przechowywać. Jest na to narzędzie – KeyStore. Magazyn kluczy jest opisany w dokumentacji JCA w rozdziale „ Zarządzanie kluczami ”. Interfejs API do pracy z nim jest bardzo przejrzysty. Oto mały przykład:KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
String alias = "EntityAlias";
java.security.cert.Certificate[] chain = {certificate};
keyStore.setKeyEntry(alias, keyPair.getPrivate(), "keyPassword".toCharArray(), chain);
// Загрузка содержимого (Private Key + Certificate)
Key key = keyStore.getKey(alias, "keyPassword".toCharArray());
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
// Сохранение KeyStore на диск
File file = File.createTempFile("security_", ".ks");
System.out.println(file.getAbsolutePath());
try (FileOutputStream fos = new FileOutputStream(file)) {
keyStore.store(fos, "keyStorePassword".toCharArray());
}
Jak widać na przykładzie, jest on wykonywany najpierw load
dla magazynu kluczy. Ale w naszym przypadku pierwszy atrybut określiliśmy jako null, tj. nie ma źródła KeyStore. Oznacza to, że magazyn kluczy jest pusty w celu dalszego zapisania. Drugi parametr również ma wartość null, ponieważ tworzymy nowy KeyStore. Gdybyśmy ładowali KeyStore z pliku, musielibyśmy tutaj podać hasło (podobnie jak w przypadku metody KeyStore zwanej store).
GO TO FULL VERSION