JavaRush /Java Blogu /Random-AZ /Java Kriptoqrafiya Memarlığı: İlk giriş
Viacheslav
Səviyyə

Java Kriptoqrafiya Memarlığı: İlk giriş

Qrupda dərc edilmişdir
Məlumat mübadiləsinin təhlükəsizliyi müasir proqramların ən vacib xüsusiyyətlərindən biridir. Qədim dövrlərdən bəri insanlar bəşəriyyətin inkişafı ilə bütün Kriptoqrafiya elminə çevrilən hiyləgər üsullarla çıxış etdilər. Təbii ki, Java kənarda durmadı və tərtibatçılara Java Kriptoqrafiya Arxitekturasını (JCA) təklif etdi. Bu baxış onun necə işlədiyi barədə ilk fikir verməlidir.

Ön söz

Mən keçmişə səyahət etməyi təklif edirəm. Qarşımızda Qədim Romadır. Qarşımızda isə öz komandirlərinə xəbər göndərən Qay Yuli Sezardır. Bu mesajda nə olduğuna baxaq:
Java Kriptoqrafiya Memarlığı: İlk giriş - 2
Bu nə demək ola bilər: "ЕСКЕУГЬГМХИФЯ Е УЛП"? Java Onlayn Kompilyatorunu açaq, məsələn: 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);
    }
  }
}
Qarşımızda Caesar Cipher-in ən sadə tətbiqi var. Qədim Roma tarixçisi Suetoniusun “On iki Sezarın həyatı” adlı əsərinə görə, Sezar öz generallarına mesajları məhz belə şifrələyirdi. Və bu, Kriptoqrafiya kimi bir şeyin istifadəsinə dair ən qədim istinadlardan biridir . "Kriptoqrafiya" sözü qədim yunan "gizli" və "yazmaq" sözlərindən gəlir, yəni. məxfilik texnikaları elmidir. Java kriptoqrafiya üçün öz dəstəyinə malikdir və Java Kriptoqrafiya Arxitekturası (JCA) adlanır . Təsviri Oracle-ın rəsmi sənədlərində tapa bilərsiniz - " Java Kriptoqrafiya Architecture (JCA) ". Mən sizə JCA sayəsində hansı imkanlar əldə etdiyimizə baxmağı təklif edirəm.
Java Kriptoqrafiya Memarlığı: İlk giriş - 3

J.C.A.

Əvvəllər öyrəndiyimiz kimi, Java kriptoqrafiya ilə işləmək üçün Java Kriptoqrafiya Arxitekturasını (JCA) təklif edir. Bu arxitektura API (yəni müəyyən interfeyslər dəsti) və provayderləri (onları həyata keçirən) ehtiva edir:
Java Kriptoqrafiya Memarlığı: İlk giriş - 4
Sənədlərdə deyildiyi kimi, " Java platforması bir sıra daxili provayderləri ehtiva edir ". Yəni Java platforması zəruri hallarda genişləndirilə bilən daxili provayderlər dəstini təmin edir. Bunu özünüz görə bilərsiniz:
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());
    }
  }
}
Üçüncü tərəf provayderini qeydiyyatdan keçirmək çox asandır. Məsələn: Security.addProvider(new BouncyCastleProvider()); Bu nümunə ən məşhur provayderlərdən birini birləşdirir - BouncyCastle . Ancaq bu araşdırmada üçüncü tərəf kitabxanaları olmadan yalnız əsas vasitələrdən istifadə edəcəyik. Əsas sənədimiz: " Java Kriptoqrafiya Architecture (JCA) ". JCA-nın necə işlədiyini başa düşmək, eyni JCA-nın fəal şəkildə istifadə olunduğu texnologiyaları daha asan başa düşməyə kömək edəcək. Məsələn: HTTPS (bax " HTTP-dən HTTPS-ə ").
Java Kriptoqrafiya Memarlığı: İlk giriş - 5

MessageDigest

JCA sənədlərində qeyd olunan ilk şey MessageDigest-dir. Ümumiyyətlə, rus dilindəki Digest eyni olacaq - həzm "xülasə" mənasına uyğun gəlir. Lakin kriptoqrafiyada həzm hash cəmidir. Siz həmçinin asanlıqla xatırlaya bilərsiniz ki, İngiliscə Digest də həzm kimi tərcümə edilə bilər. Daha ətraflı məlumatı " MessageDigest " bölməsində JCA sənədlərində tapa bilərsiniz . Sənədlərdə deyildiyi kimi, MessageDigest həzm və ya hash adlı sabit ölçülü nəticə yaradır. Hashing birtərəfli funksiyadır, yəni. Əgər biz bir şeyi hash etdiksə, nəticədən (yəni hashdan) orijinal mənbəni ala bilmərik. Ancaq eyni obyektlər hesh edilirsə (məsələn, eyni simvolların sətirləri), onda onların hashı uyğun olmalıdır. Sənədlərdə deyildiyi kimi, belə bir hash bəzən məlumatların "yoxlama cəmi" və ya "rəqəmsal barmaq izi" adlanır. Hashing müxtəlif alqoritmlərdən istifadə etməklə həyata keçirilə bilər. Mövcud alqoritmlərə " JDK 8 üçün Java Kriptoqrafiya Arxitekturasının Standart Alqoritm Adı Sənədi " sənədində baxmaq olar . Gəlin hashing edək və hashı konsola çap edək:
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, məsələn, parolları saxlayarkən faydalı ola bilər. Daxil edilmiş parolun hashı əvvəllər saxlanmış hash ilə yoxlanıla bilər. Haşlar uyğun gəlirsə, parol da uyğun gəlir. Daha təhlükəsiz hashing üçün "duz" adlı bir anlayış istifadə olunur. Duz SecureRandom sinifindən istifadə etməklə həyata keçirilə bilər . Həzm metodunu yerinə yetirməzdən əvvəl "duz" əlavə etməyi təsvir edək:
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
Lakin hash birtərəfli funksiyadır. Bəs şifrləmək və deşifrə etmək istəsəniz nə olacaq?
Java Kriptoqrafiya Memarlığı: İlk giriş - 6

Simmetrik açar kriptoqrafiyası

Simmetrik şifrələmə şifrələmə və şifrənin açılması üçün eyni açardan istifadə edən şifrələmədir. Simmetrik şifrələmədən istifadə etmək üçün bizə açar lazımdır. Onu əldə etmək üçün KeyGenerator istifadə edirik . Bundan əlavə, şifrəni təmsil edən bir sinifə ehtiyacımız olacaq ( Cipher ). JCA sənədlərinin “ Şifrə obyektinin yaradılması ” bölməsində qeyd edildiyi kimi , Şifrə yaratmaq üçün siz sadəcə alqoritmi deyil, sətirdə “çevirmə” də göstərməlisiniz. Transformasiya təsviri belə görünür: "alqoritm/rejim/doldurma":
  • Alqoritm : burada biz “ Şifrə (Şifrələmə) Alqoritmləri ” üçün standart adlara baxırıq . AES-dən istifadə etmək tövsiyə olunur.
  • Rejim : şifrələmə rejimi. Məsələn: ECB və ya CBC (bu barədə bir az sonra danışacağıq)
  • Girinti/Bölünmə : Hər bir məlumat bloku ayrıca şifrələnir. Bu parametr nə qədər məlumatın 1 blok kimi hesablandığını müəyyən edir.
Məsələn, aşağıdakı çevrilməni götürək: "AES/ECB/PKCS5Padding". Yəni, şifrələmə alqoritmi AES, şifrələmə rejimi ECB (Elektron Kod Kitabı üçün qısa), blok ölçüsü PKCS5Padding-dir. PKCS5Padding deyir ki, bir blokun ölçüsü 2 baytdır (16 bit). Elektron Kod Kitabının şifrələmə rejimi hər bir blokun ardıcıl şifrələnməsini əhatə edir:
Java Kriptoqrafiya Memarlığı: İlk giriş - 7
Kodda belə görünə bilər:
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);
  }
}
İcra etsək, təkrarını gözləyəcəyik, çünki 32 simvol təyin etdik. Bu simvollar 16 bitlik 2 blok təşkil edir:
Java Kriptoqrafiya Memarlığı: İlk giriş - 8
Bu vəziyyətdə təkrar oynatmamaq üçün başqa bir rejimdən istifadə etməlisiniz - Cipher Block Chaining (CBC). Bu rejim Initialization Vector konsepsiyasını təqdim edir (IvParameterSpec sinfi ilə təmsil olunur). Həm də bu rejim sayəsində sonuncu blokun yaradılmasının nəticəsi növbəti blokun yaradılması üçün istifadə olunacaq:
Java Kriptoqrafiya Memarlığı: İlk giriş - 9
İndi bunu kodla yazaq:
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);
  }
}
Gördüyümüz kimi, nəticədə təkrarlanan şifrə bloklarını görmürük. Bu səbəbdən ECB rejimi tövsiyə edilmir, çünki təkrarları görməyə və bu biliklərdən şifrəni açmaq üçün istifadə etməyə imkan verir. ECB və CBC haqqında daha çox məlumat üçün sizə materialı oxumağı məsləhət görürəm: “ Elektron kod kitabı rejimi ”. Ancaq simmetrik şifrələmənin açıq bir problemi var - açarı birtəhər şifrələyəndən şifrələyənə ötürmək lazımdır. Və bu yolda bu açar ələ keçirilə bilər və sonra məlumatları ələ keçirmək mümkün olacaq. Və asimmetrik şifrələmə bu problemi həll etmək üçün nəzərdə tutulub.
Java Kriptoqrafiya Memarlığı: İlk giriş - 10

Asimmetrik şifrələmə

Asimmetrik şifrələmə və ya Açıq açar kriptoqrafiyası bir cüt açardan istifadə edən şifrələmə üsuludur: şəxsi açar (hamıdan gizli saxlanılır) və açıq açar (ictimaiyyət üçün açıqdır). Bu ayırma məxfi açarı təhlükəsiz saxlamaqla, məlumat mübadiləsinin tərəfləri arasında açıq açarı təhlükəsiz şəkildə mübadilə etmək üçün lazımdır. Açar cütü yaratarkən KeyGenerator artıq bizim üçün kifayət deyil, KeyPairGenerator lazımdır . Bir misala baxaq:
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));
  }
}
Burada başa düşmək vacibdir ki, asimmetrik şifrələmədən istifadə edərkən biz həmişə bir açarı şifrələmə üçün, digərini isə deşifrə üçün istifadə etmək üçün KeyPair-dən istifadə edirik. Amma ona görə ki Şifrələmənin mahiyyəti ondan ibarətdir ki, yalnız alıcı onu deşifrə edə bilər, o, açıq açarla şifrələnir və yalnız şəxsi ilə şifrələnir.
Java Kriptoqrafiya Memarlığı: İlk tanışlıq - 11

Rəqəmsal imza

Yuxarıda gördüyümüz kimi, açıq açarı bilməklə, məlumat göndərə bilərsiniz ki, yalnız şəxsi açarın sahibi onu deşifrə edə bilsin. Yəni, asimmetrik şifrələmənin mahiyyəti ondan ibarətdir ki, hər kəs şifrələyir, ancaq biz oxuyuruq. Əks prosedur da var - İmza sinfi ilə təmsil olunan rəqəmsal imza . Rəqəmsal imza aşağıdakı alqoritmlərdən istifadə edə bilər: " İmza alqoritmləri ". JCA sənədləri bu ikisinə daha yaxından nəzər salmağı təklif edir: DSAwithMD5 və RSAwithMD5 DSA və ya RSA-dan daha yaxşı nədir və onların fərqi nədir burada oxuya bilərsiniz: " Şifrələnmiş Fayl Transferləri üçün Hansı Ən Yaxşı İşləyir - RSA və ya DSA? ". Və ya burada müzakirələri oxuyun: " SSH autentifikasiya açarları üçün RSA vs DSA ". Beləliklə, rəqəmsal imza. Bizə əvvəlki kimi KeyPair və yeni İmza sinfi lazım olacaq. Əgər siz indiyə qədər onlayn tərtibçilərdə sınaqdan keçirmisinizsə, onda aşağıdakı nümunə onlar üçün bir qədər çətin ola bilər. Mənim nümunəm yalnız burada işlədi: rextester.com . Bizə lazım olan sinifləri idxal edirik:
import javax.crypto.*;
import java.security.*;
Əsas metodu da yenidən yazacağıq:
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));
    }
}
Rəqəmsal imza belə işləyir. Rəqəmsal imza maraqlı mövzudur. Bu mövzuda hesabata baxmağı məsləhət görürəm:
Java Kriptoqrafiya Memarlığı: İlk tanışlıq - 12
Yuxarıda tərəflərin məlumat mübadiləsinin necə aparıldığını gördük. JCA-da təqdim olunan bu qarşılıqlı əlaqə üçün bəzi standart interfeys yoxdur? Belə çıxır ki, var. Gəlin buna baxaq.
Java Kriptoqrafiya Memarlığı: İlk tanışlıq - 13

Açar Müqavilə

Java Kriptoqrafiya Arxitekturası mühüm alət təqdim edir - Əsas razılaşma protokoldur. KeyAgreement sinfi ilə təmsil olunur . JCA sənədlərində qeyd edildiyi kimi, bu protokol tərəflər arasında heç bir məxfi məlumat paylaşmadan bir neçə tərəfə eyni kriptoqrafik açarı təyin etməyə imkan verir. Qəribə səslənir? Sonra bir nümunəyə baxaq:
// 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));
Bu nümunə JCA sənədləşdirmə nümunəsindən götürülmüşdür: " 2 Tərəf arasında Diffie-Hellman Açar Mübadiləsi ". Açar razılaşma protokolundan istifadə edərək Java Kriptoqrafiya Arxitekturasında asimmetrik şifrələmə təxminən belə görünür. Asimmetrik şifrələmə haqqında ətraflı məlumat üçün tövsiyə olunan videolar:
Java Kriptoqrafiya Arxitekturası: İlk tanışlıq - 14

Sertifikatlar

Yaxşı, desert üçün hələ də daha az vacib olmayan bir şey var - sertifikatlar. Tipik olaraq, sertifikatlar jdk ilə birlikdə olan keytool yardım proqramından istifadə etməklə yaradılır. Ətraflı təfərrüatları, məsələn, burada oxuya bilərsiniz: " Java keytool əmrindən istifadə edərək özünü imzalanmış SSL sertifikatının yaradılması ". Siz həmçinin Oracle-dan təlimatları oxuya bilərsiniz. Məsələn, burada: " Server Sertifikatı Yaratmaq üçün Keytooldan istifadə etmək üçün ". Məsələn, Tutorialspoint Java Online Compiler istifadə edək :
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));
  }
}
Gördüyümüz kimi, sertifikat açıq açar təqdim etmək imkanı verir. Bu metodun çatışmazlıqları var - biz istifadə edirik sun.security, riskli hesab olunur, çünki... bu paket ictimai Java API-nin bir hissəsi deyil. Məhz buna görə kompilyasiya zamanı - parametrini göstərmək lazımdır XDignore.symbol.file. Başqa bir yol var - əl ilə sertifikat yaratmaq. İşin mənfi tərəfi, sənədləşdirilməmiş daxili API istifadə etməsidir. Bununla belə, bu barədə bilmək faydalıdır. Ən azı ona görə ki, RFC-2459 spesifikasiyasının necə istifadə edildiyi aydın görünür: “ Internet X.509 Public Key Infrastructure ”. Budur bir nümunə:
// 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);
}
Java Kriptoqrafiya Memarlığı: İlk giriş - 15

Açar anbarı (Keystore)

Haqqında danışmaq istədiyim son şey KeyStore adlanan açar və sertifikat mağazasıdır. Aydındır ki, davamlı sertifikatlar və açarlar yaratmaq bahalı və mənasızdır. Buna görə də, onları bir şəkildə təhlükəsiz saxlamaq lazımdır. Bunun üçün bir alət var - KeyStore. Açar anbarı JCA sənədlərində " Açarların idarə edilməsi " fəslində təsvir edilmişdir. Onunla işləmək üçün API çox aydındır. Budur kiçik bir nümunə:
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());
}
Nümunədən göründüyü kimi, ilk növbədə loadKeyStore üçün icra olunur. Ancaq bizim vəziyyətimizdə biz birinci atributu null olaraq təyin etdik, yəni. KeyStore üçün heç bir mənbə yoxdur. Bu o deməkdir ki, KeyStore onu daha da saxlamaq üçün boş yaradılmışdır. İkinci parametr də sıfırdır, çünki biz yeni KeyStore yaradırıq. Əgər biz KeyStore-u fayldan yükləyirdiksə, onda biz burada parol təyin etməliyik (mağaza adlanan KeyStore metoduna bənzər).

Alt xətt

Beləliklə, biz sizinlə Java Kriptoqrafiya Memarlığı (aka JCA) çərçivəsində ən əsas və elementar hərəkətləri nəzərdən keçirdik. Simmetrik və asimmetrik şifrələmənin nə olduğunu və JCA-da necə həyata keçirildiyini gördük. Sertifikatların və rəqəmsal imzaların necə yaradıldığını və necə istifadə edildiyini gördük. Bütün bunlar sadəcə əsaslardır, bunun arxasında daha çox mürəkkəb və maraqlı şeylər var. Ümid edirəm ki, bu icmal materialı faydalı olacaq və bu sahənin daha da öyrənilməsində sizi maraqlandıracaq.
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION