Ma'lumotlar almashinuvining xavfsizligi zamonaviy ilovalarning eng muhim xususiyatlaridan biridir. Qadim zamonlardan beri odamlar ayyor usullarni o'ylab topishgan, ular insoniyat rivojlanishi bilan butun kriptografiya faniga aylangan. Tabiiyki, Java chetda turmadi va ishlab chiquvchilarga Java Kriptografiya Arxitekturasini (JCA) taklif qildi. Ushbu sharh uning qanday ishlashi haqida birinchi fikrni berishi kerak.
Bu nimani anglatishi mumkin
Hujjatlarda aytilganidek, " Java platformasi bir qator o'rnatilgan provayderlarni o'z ichiga oladi ". Ya'ni, Java platformasi kerak bo'lganda kengaytirilishi mumkin bo'lgan o'rnatilgan provayderlar to'plamini taqdim etadi. Buni o'zingiz ko'rishingiz mumkin:
Kodda shunday ko'rinishi mumkin:
Bunday holatda takrorlashni oldini olish uchun siz boshqa rejimdan foydalanishingiz kerak - Cipher Block Chaining (CBC). Ushbu rejim Initialization Vector (IvParameterSpec klassi bilan ifodalanadi) tushunchasini taqdim etadi. Shuningdek, ushbu rejim tufayli oxirgi blokni yaratish natijasi keyingi blokni yaratish uchun ishlatiladi:
Endi buni kodda yozamiz:
Yuqorida biz tomonlar qanday ma'lumotlarni almashishini ko'rdik. JCA da taqdim etilgan ushbu o'zaro aloqa uchun standart interfeys mavjud emasmi? Ma'lum bo'lishicha, bor. Keling, buni ko'rib chiqaylik.
Muqaddima
Men vaqtga sayohat qilishni taklif qilaman. Bizning oldimizda Qadimgi Rim. Va oldimizda Gay Yuliy Tsezar turibdi, u o'z qo'mondonlariga xabar yuboradi. Keling, ushbu xabarda nima borligini ko'rib chiqaylik:"ЕСКЕУГЬГМХИФЯ Е УЛП"
:? Keling, Java Online Compiler dasturini ochamiz, masalan: 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);
}
}
}
Bizning oldimizda Sezar shifrining eng oddiy amalga oshirilishi. Qadimgi Rim tarixchisi Suetoniusning "O'n ikki Qaysarning hayoti" nomli asariga ko'ra, Sezar o'z sarkardalariga xabarlarni aynan shunday shifrlagan. Va bu Kriptografiya kabi narsadan foydalanishga oid eng qadimiy havolalardan biridir . "Kriptografiya" so'zi qadimgi yunoncha "yashirin" va "yozish" so'zlaridan kelib chiqqan, ya'ni. bu maxfiylik texnikasi haqidagi fan. Java kriptografiya uchun o'z yordamiga ega va u Java Kriptografiya Arxitekturasi (JCA) deb ataladi . Ta'rifni Oracle rasmiy hujjatlarida topish mumkin - " Java Kriptografiya Architecture (JCA) ". Men sizga JCA tufayli qanday imkoniyatlarga ega ekanligimizni ko'rib chiqishni taklif qilaman.
J.C.A.
Avval bilib olganimizdek, Java kriptografiya bilan ishlash uchun Java Kriptografiya Arxitekturasini (JCA) taklif qiladi. Ushbu arxitektura API (ya'ni, ma'lum interfeyslar to'plami) va provayderlarni (ularni amalga oshiradigan) o'z ichiga oladi: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());
}
}
}
Uchinchi tomon provayderini ro'yxatdan o'tkazish juda oson. Misol uchun: Security.addProvider(new BouncyCastleProvider());
Bu misol eng mashhur provayderlardan birini bog'laydi - BouncyCastle . Ammo ushbu sharhda biz uchinchi tomon kutubxonalarisiz faqat asosiy vositalardan foydalanamiz. Bizning asosiy hujjatimiz: " Java Kriptografiya Arxitekturasi (JCA) ". JCA qanday ishlashini tushunish, xuddi shu JCA faol qo'llaniladigan texnologiyalarni osonroq tushunishga yordam beradi. Masalan: HTTPS (qarang: " HTTPdan HTTPSga ").
MessageDigest
JCA hujjatlarida qayd etilgan birinchi narsa bu MessageDigest. Umuman olganda, rus tilidagi Dijest bir xil bo'ladi - dayjest "xulosa" ma'nosiga mos keladi. Ammo kriptografiyada dayjest xesh summasidir. Bundan tashqari, ingliz tilidagi Digestni dayjest sifatida ham tarjima qilish mumkinligini osongina eslashingiz mumkin. Batafsil ma'lumotni JCA hujjatlarida " MessageDigest " bo'limida topishingiz mumkin. Hujjatlarda aytilganidek, MessageDigest dayjest yoki xesh deb ataladigan qat'iy o'lchamdagi natijani yaratadi. Hashing bir tomonlama funktsiyadir, ya'ni. agar biz biror narsani xeshlagan bo'lsak, natijadan (ya'ni, xeshdan) biz asl manbani ololmaymiz. Ammo agar bir xil ob'ektlar xeshlangan bo'lsa (masalan, bir xil belgilar qatorlari), unda ularning xeshlari mos kelishi kerak. Hujjatlarda ta'kidlanganidek, bunday xesh ba'zida ma'lumotlarning "cheksum" yoki "raqamli barmoq izi" deb ham ataladi. Xeshlash turli xil algoritmlar yordamida amalga oshirilishi mumkin. Mavjud algoritmlarni " JDK 8 uchun Java Kriptografiya Arxitekturasi Standart Algoritm Nomi Hujjatlari " hujjatida ko'rish mumkin . Keling, xeshlashni bajaramiz va konsolga xeshni chop qilamiz: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);
}
}
}
Hashing, masalan, parollarni saqlashda foydali bo'lishi mumkin. Kiritilgan parolning xeshini oldindan saqlangan xesh bilan tekshirish mumkin. Agar xeshlar mos kelsa, parol ham mos keladi. Yana xavfsizroq xeshlash uchun "tuz" deb nomlangan tushuncha ishlatiladi. Tuz SecureRandom klassi yordamida amalga oshirilishi mumkin . Digest usulini bajarishdan oldin, keling, "tuz" qo'shishni tavsiflaymiz:
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
Ammo hash bir tomonlama funktsiyadir. Ammo shifrlash va shifrni ochish imkoniyatiga ega bo'lishni istasangiz-chi?
Simmetrik kalit kriptografiyasi
Simmetrik shifrlash - shifrlash va shifrni ochish uchun bir xil kalitdan foydalanadigan shifrlash. Simmetrik shifrlashdan foydalanish uchun bizga kalit kerak. Uni olish uchun biz KeyGenerator dan foydalanamiz . Bundan tashqari, bizga shifrni ifodalovchi sinf kerak bo'ladi ( Cipher ). JCA hujjatlarida " Shifr ob'ektini yaratish " bo'limida aytilganidek , shifrni yaratish uchun siz faqat algoritmni emas, balki qatorda "o'zgartirish" ni ko'rsatishingiz kerak. Transformatsiya tavsifi quyidagicha ko'rinadi: "algorithm/mode/padding":- Algoritm : bu erda biz " Shifrlash (shifrlash) algoritmlari " ning standart nomlarini ko'rib chiqamiz . AES dan foydalanish tavsiya etiladi.
- Tartib : shifrlash rejimi. Masalan: ECB yoki CBC (bu haqda biroz keyinroq gaplashamiz)
- Indentation/Split : Har bir ma'lumot bloki alohida shifrlangan. Ushbu parametr 1 blok sifatida qancha ma'lumot hisoblanishini aniqlaydi.
"AES/ECB/PKCS5Padding"
. Ya'ni, shifrlash algoritmi - AES, shifrlash rejimi - ECB (elektron kod kitobining qisqartmasi), blok hajmi - PKCS5Padding. PKCS5Padding, bitta blokning hajmi 2 bayt (16 bit) ekanligini aytadi. Elektron kod kitobini shifrlash rejimi har bir blokning ketma-ket shifrlanishini o'z ichiga oladi:
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);
}
}
Agar biz ijro etsak, takrorlashni ko'rishni kutamiz, chunki biz 32 ta belgi belgiladik. Bu belgilar 16 bitli 2 blokdan iborat:
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);
}
}
Ko'rib turganimizdek, natijada biz takroriy shifr bloklarini ko'rmayapmiz. Shu sababli, ECB rejimi tavsiya etilmaydi, chunki takrorlashlarni ko'rish va bu bilimlardan shifrni ochish uchun foydalanish imkonini beradi. ECB va CBC haqida ko'proq ma'lumot olish uchun sizga materialni o'qishni maslahat beraman: “ Elektron kod kitobi rejimi ”. Ammo nosimmetrik shifrlashda aniq muammo bor - siz qandaydir tarzda kalitni shifrlagandan shifrlaganga o'tkazishingiz kerak. Va bu yo'lda bu kalitni ushlab turish mumkin va keyin ma'lumotlarni ushlab turish mumkin bo'ladi. Va assimetrik shifrlash bu muammoni hal qilish uchun mo'ljallangan.
Asimmetrik shifrlash
Assimetrik shifrlash yoki Ochiq kalitli kriptografiya shifrlash usuli bo'lib, bir juft kalitdan foydalanadi: shaxsiy kalit (hamma uchun sir saqlanadi) va ochiq kalit (omma uchun mavjud). Bu ajratish maxfiy kalitni xavfsiz saqlagan holda axborot almashuvchi tomonlar o'rtasida ochiq kalitni xavfsiz almashish uchun zarurdir. Kalitlar juftligini yaratishda KeyGenerator endi biz uchun yetarli emas; bizga KeyPairGenerator kerak . Keling, bir misolni ko'rib chiqaylik: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));
}
}
Bu erda shuni tushunish kerakki, assimetrik shifrlashdan foydalanganda biz har doim bitta kalitni shifrlash uchun, ikkinchisini shifrlash uchun ishlatish uchun KeyPair-dan foydalanamiz. Lekin chunki Shifrlashning mohiyati shundan iboratki, uni faqat qabul qiluvchi shifrlashi mumkin, u ochiq kalit bilan shifrlangan va faqat shaxsiy kalit bilan shifrlangan.
Raqamli imzo
Yuqorida ko'rganimizdek, ochiq kalitni bilgan holda, siz ma'lumotlarni faqat shaxsiy kalit egasi shifrini ochishi uchun yuborishingiz mumkin. Ya'ni, assimetrik shifrlashning mohiyati shundaki, har kim shifrlaydi, lekin faqat biz o'qiymiz. Bundan tashqari, teskari protsedura mavjud - raqamli imzo, Imzo sinfi bilan ifodalanadi . Raqamli imzo quyidagi algoritmlardan foydalanishi mumkin: " Imzo algoritmlari ". JCA hujjatlari ushbu ikkitasini batafsil ko'rib chiqishni taklif qiladi: DSAwithMD5 va RSAwithMD5 DSA yoki RSA dan nima yaxshiroq va ularning farqi nimada bu yerda o'qishingiz mumkin: " Shifrlangan fayllarni uzatish uchun qaysi biri eng yaxshi ishlaydi - RSA yoki DSA? ". Yoki bu yerda muhokamalarni o'qing: " SSH autentifikatsiya kalitlari uchun RSA va DSA ". Shunday qilib, raqamli imzo. Bizga avvalgidek KeyPair va yangi Signature sinfi kerak bo'ladi. Agar siz hozirgacha onlayn kompilyatorlarda sinovdan o'tgan bo'lsangiz, unda quyidagi misol ular uchun biroz qiyin bo'lishi mumkin. Mening misolim faqat bu erda ishlaydi: rextester.com . Biz kerakli sinflarni import qilamiz:import javax.crypto.*;
import java.security.*;
Shuningdek, biz asosiy usulni qayta yozamiz:
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));
}
}
Raqamli imzo shunday ishlaydi. Raqamli imzo - qiziqarli mavzu. Men sizga ushbu mavzu bo'yicha hisobotni ko'rib chiqishni maslahat beraman:
Asosiy kelishuv
Java kriptografiya arxitekturasi muhim vositani taqdim etadi - Kalit kelishuvi protokoldir. U KeyAgreement sinfi bilan ifodalanadi . JCA hujjatlarida ta'kidlanganidek, ushbu protokol bir nechta tomonlarga bir xil kriptografik kalitni tomonlar o'rtasida hech qanday maxfiy ma'lumot almashmasdan o'rnatish imkonini beradi. G'alati tuyuladimi? Keyin bir misolni ko'rib chiqaylik:// 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();
Теперь, у Алисы есть открытый ключ Боба, а у Боба есть открытый ключ Алисы. What дальше?
Как сказано в documentации 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));
Ushbu misol JCA hujjatlari misolidan olingan: " 2 Tomon o'rtasida Diffie-Hellman Key almashinuvi ". Assimetrik shifrlash Java kriptografiyasi arxitekturasida Kalit kelishuv protokoli yordamida taxminan shunday ko'rinadi. Asimmetrik shifrlash haqida ko'proq ma'lumot olish uchun tavsiya etilgan videolar:
Sertifikatlar
Xo'sh, shirinlik uchun bizda hali ham muhim bo'lmagan narsa bor - sertifikatlar. Odatda, sertifikatlar jdk-ga kiritilgan keytool yordam dasturi yordamida yaratiladi. Batafsil ma'lumotni bu erda o'qishingiz mumkin, masalan: " Java keytool buyrug'i yordamida o'z-o'zidan imzolangan SSL sertifikatini yaratish ". Oracle'dan qo'llanmalarni ham o'qishingiz mumkin. Masalan, bu erda: " Server sertifikatini yaratish uchun keytooldan foydalanish ". Masalan, Tutorialspoint Java Online Compiler dan foydalanamiz :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));
}
}
Ko'rib turganimizdek, sertifikat ochiq kalitni taqdim etish imkoniyatini beradi. Bu usulning kamchiligi bor - biz dan foydalanamiz sun.security
, bu xavfli hisoblanadi, chunki... bu paket umumiy Java API ning bir qismi emas. Shuning uchun kompilyatsiya paytida - parametrini ko'rsatish kerak XDignore.symbol.file
. Boshqa yo'l bor - sertifikatni qo'lda yaratish. Salbiy tomoni shundaki, u hujjatlashtirilmagan ichki API dan foydalanadi. Biroq, bu haqda bilish foydalidir. Hech bo'lmaganda, chunki RFC-2459 spetsifikatsiyasi qanday qo'llanilishi aniq ko'rinib turadi: “ Internet X.509 ochiq kalit infratuzilmasi ”. Mana bir misol:
// 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, т.е. Name того, с чем ассоциирован публичный ключ
// 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);
}
Kalit do'koni (KeyStore)
Men gaplashmoqchi bo'lgan oxirgi narsa - bu KeyStore deb nomlangan kalit va sertifikatlar do'koni. Doimiy ravishda sertifikatlar va kalitlarni yaratish qimmat va ma'nosiz ekanligi aniq. Shuning uchun ular qandaydir tarzda xavfsiz tarzda saqlanishi kerak. Buning uchun vosita mavjud - KeyStore. Kalitlar do'koni JCA hujjatlarida " KeyManagement " bo'limida tasvirlangan. U bilan ishlash uchun API juda aniq. Mana kichik bir misol: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());
}
Misoldan ko'rinib turibdiki, u birinchi navbatda load
KeyStore uchun bajariladi. Ammo bizning holatlarimizda biz birinchi atributni null deb belgiladik, ya'ni. KeyStore uchun manba yo'q. Bu yana saqlash uchun KeyStore bo'sh yaratilganligini anglatadi. Ikkinchi parametr ham null, chunki biz yangi KeyStore yaratmoqdamiz. Agar biz KeyStore-ni fayldan yuklayotgan bo'lsak, bu erda parolni ko'rsatishimiz kerak bo'ladi (do'kon deb ataladigan KeyStore usuliga o'xshash).
GO TO FULL VERSION