JavaRush /Java Blog /Random-KO /Java 암호화 아키텍처: 첫 번째 소개
Viacheslav
레벨 3

Java 암호화 아키텍처: 첫 번째 소개

Random-KO 그룹에 게시되었습니다
데이터 교환의 보안은 최신 애플리케이션의 가장 중요한 속성 중 하나입니다. 고대부터 사람들은 인류의 발전과 함께 암호학의 전체 과학이 된 교활한 방법을 생각해 냈습니다. 당연히 Java는 개발자에게 JCA(Java Cryptography Architecture)를 제공하지 않았습니다. 이 리뷰는 그것이 어떻게 작동하는지에 대한 첫 번째 아이디어를 제공해야 합니다.

머리말

나는 시간을 거슬러 여행을 제안합니다. 우리 앞에는 고대 로마가 있습니다. 그리고 우리 앞에는 그의 지휘관들에게 메시지를 보내는 가이우스 율리우스 카이사르가 있습니다. 이 메시지의 내용을 살펴보겠습니다.
Java 암호화 아키텍처: 첫 번째 소개 - 2
이것은 무엇을 의미할까요: "ЕСКЕУГЬГМХИФЯ Е УЛП"? 예를 들어 Java 온라인 컴파일러를 열어 보겠습니다. 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);
    }
  }
}
우리 앞에는 Caesar Cipher의 가장 간단한 구현이 있습니다. 고대 로마 역사가 수에토니우스(Suetonius)의 저서 "열두 카이사르의 생애"에 따르면, 카이사르가 장군들에게 보낸 메시지를 암호화한 방법이 바로 이것이었습니다. 그리고 이것은 암호화 와 같은 것의 사용에 대한 가장 오래된 참고 자료 중 하나입니다 . "암호화"라는 단어는 고대 그리스어 "숨겨진"과 "쓰기"에서 유래되었습니다. 그것은 개인 정보 보호 기술의 과학입니다. Java에는 암호화에 대한 자체 지원이 있으며 이를 JCA( Java Cryptography Architecture )라고 합니다. 설명은 Oracle의 공식 문서인 " JCA(Java Cryptography Architecture) "에서 찾을 수 있습니다. JCA 덕분에 우리가 어떤 기회를 얻었는지 살펴보시기 바랍니다.
Java 암호화 아키텍처: 첫 번째 소개 - 3

J.C.A.

이전에 배운 것처럼 Java는 암호화 작업을 위한 JCA(Java Cryptography Architecture)를 제공합니다. 이 아키텍처에는 API(즉, 특정 인터페이스 집합)와 공급자(이를 구현하는)가 포함되어 있습니다.
Java 암호화 아키텍처: 첫 번째 소개 - 4
문서에 따르면 " Java 플랫폼에는 여러 내장 공급자가 포함되어 있습니다 "라고 나와 있습니다. 즉, Java 플랫폼은 필요한 경우 확장할 수 있는 내장 공급자 세트를 제공합니다. 이것을 직접 볼 수 있습니다:
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());
    }
  }
}
제3자 공급자를 등록하는 것은 매우 쉽습니다. 예: Security.addProvider(new BouncyCastleProvider()); 이 예에서는 가장 유명한 공급자 중 하나인 BouncyCastle을 연결합니다 . 하지만 이번 리뷰에서는 타사 라이브러리 없이 기본 도구만 사용하겠습니다. 주요 문서: " JCA(Java Cryptography Architecture) ". JCA의 작동 방식을 이해하면 동일한 JCA가 적극적으로 사용되는 기술을 더 쉽게 이해하는 데 도움이 됩니다. 예: HTTPS(" HTTP에서 HTTPS로 " 참조).
Java 암호화 아키텍처: 첫 번째 소개 - 5

메시지다이제스트

JCA 문서에서 가장 먼저 언급되는 것은 MessageDigest입니다. 일반적으로 러시아어로 된 다이제스트는 동일합니다. 다이제스트는 "요약"을 의미합니다. 그러나 암호화에서 다이제스트는 해시 합계입니다. 또한 영어 Digest에서는 Digest로 번역될 수도 있다는 것을 쉽게 기억할 수 있습니다. 자세한 내용은 JCA 문서의 " MessageDigest " 섹션에서 확인할 수 있습니다. 문서에 나와 있듯이 MessageDigest는 다이제스트 또는 해시라는 고정 크기 결과를 생성합니다. 해싱은 단방향 함수입니다. 무언가를 해시했다면 결과(즉, 해시)에서 원본 소스를 얻을 수 없습니다. 그러나 동일한 객체가 해시되는 경우(예: 동일한 문자의 문자열) 해당 해시가 일치해야 합니다. 문서에 명시된 바와 같이 이러한 해시는 때때로 데이터의 "체크섬" 또는 "디지털 지문"이라고도 합니다. 해싱은 다양한 알고리즘을 사용하여 수행될 수 있습니다. 사용 가능한 알고리즘은 " JDK 8용 Java 암호화 아키텍처 표준 알고리즘 이름 문서 " 문서에서 확인할 수 있습니다 . 해싱을 수행하고 해시를 콘솔에 인쇄해 보겠습니다.
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);
    }
  }
}
예를 들어, 비밀번호를 저장할 때 해싱이 유용할 수 있습니다. 입력한 비밀번호의 해시를 이전에 저장된 해시와 비교하여 확인할 수 있기 때문입니다. 해시가 일치하면 비밀번호도 일치합니다. 더욱 안전한 해싱을 위해 “솔트”라는 개념이 사용됩니다. Salt는 SecureRandom 클래스를 사용하여 구현할 수 있습니다 . 다이제스트 메소드를 실행하기 전에 "salt"를 추가하는 방법을 설명하겠습니다.
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
그러나 해시는 단방향 함수입니다. 하지만 암호화하고 복호화할 수 있으려면 어떻게 해야 할까요?
Java 암호화 아키텍처: 첫 번째 소개 - 6

대칭키 암호화

대칭 암호화는 암호화와 복호화에 동일한 키를 사용하는 암호화입니다. 대칭 암호화를 사용하려면 키가 필요합니다. 이를 얻으려면 KeyGenerator를 사용합니다 . 또한 암호를 나타내는 클래스( Cipher )가 필요합니다. JCA 문서의 " 암호 객체 생성 " 섹션에 명시된 대로 암호를 생성하려면 알고리즘뿐만 아니라 행에 "변환"도 지정해야 합니다. 변환 설명은 다음과 같습니다: "알고리즘/모드/패딩":
  • 알고리즘 : 여기에서는 " 암호화(암호화) 알고리즘 " 의 표준 이름을 살펴봅니다 . AES를 사용하는 것이 좋습니다.
  • 모드 : 암호화 모드입니다. 예: ECB 또는 CBC(이에 대해서는 나중에 설명하겠습니다)
  • 들여쓰기/분할 : 각 데이터 블록이 별도로 암호화됩니다. 이 매개변수는 1블록으로 계산되는 데이터의 양을 결정합니다.
예를 들어 다음과 같은 변환을 수행합니다 "AES/ECB/PKCS5Padding". 즉, 암호화 알고리즘은 AES, 암호화 모드는 ECB(Electronic Codebook의 약자), 블록 크기는 PKCS5Padding입니다. PKCS5Padding에서는 한 블록의 크기가 2바이트(16비트)라고 말합니다. 전자 코드북 암호화 모드에는 각 블록의 순차적 암호화가 포함됩니다.
Java 암호화 아키텍처: 첫 번째 소개 - 7
코드에서는 다음과 같이 보일 수 있습니다.
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);
  }
}
실행하면 반복이 나타날 것으로 예상됩니다. 우리는 32자를 지정했습니다. 다음 문자는 16비트로 구성된 2개의 블록을 구성합니다.
Java 암호화 아키텍처: 첫 번째 소개 - 8
이 경우 재생을 방지하려면 CBC(Cipher Block Chaining)라는 다른 모드를 사용해야 합니다. 이 모드에서는 초기화 벡터(IvParameterSpec 클래스로 표시됨) 개념을 도입합니다. 또한 이 모드 덕분에 마지막 블록 생성 결과는 다음 블록을 생성하는 데 사용됩니다.
Java 암호화 아키텍처: 첫 번째 소개 - 9
이제 이것을 코드로 작성해 보겠습니다.
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);
  }
}
보시다시피 결과적으로 반복되는 암호 블록이 표시되지 않습니다. 이러한 이유로 ECB 모드는 권장되지 않습니다. 반복을 확인하고 이 지식을 암호 해독에 사용할 수 있습니다. ECB 및 CBC에 대한 자세한 내용을 보려면 " 전자 코드북 모드 " 자료를 읽어 보시기 바랍니다 . 그러나 대칭 암호화에는 명백한 문제가 있습니다. 암호화하는 사람의 키를 암호화하는 사람에게 어떻게든 전송해야 합니다. 그리고 이 경로를 따라 이 키를 가로챌 수 있고, 그러면 데이터를 가로채는 것이 가능해집니다. 그리고 비대칭 암호화는 이 문제를 해결하기 위해 설계되었습니다.
Java 암호화 아키텍처: 첫 번째 소개 - 10

비대칭 암호화

비대칭 암호화 또는 공개 키 암호화는 개인 키(모든 사람에게 비밀로 유지됨)와 공개 키(공개적으로 사용 가능)라는 한 쌍의 키를 사용하는 암호화 방법입니다. 이러한 분리는 비밀 키를 안전하게 유지하면서 정보 교환 당사자 간에 공개 키를 안전하게 교환하기 위해 필요합니다. 키 쌍을 생성할 때 KeyGenerator로는 더 이상 충분하지 않습니다. KeyPairGenerator가 필요합니다 . 예를 살펴보겠습니다:
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));
  }
}
비대칭 암호화를 사용할 때 항상 KeyPair를 사용하여 암호화에 하나의 키를 사용하고 암호 해독에 다른 키를 사용한다는 점을 이해하는 것이 중요합니다. 하지만 왜냐하면 암호화의 핵심은 수신자만이 복호화할 수 있다는 것인데, 공개키로 암호화하고 개인키로만 복호화합니다.
Java 암호화 아키텍처: 첫 만남 - 11

전자 서명

위에서 본 것처럼 공개 키를 알면 개인 키 소유자만 해독할 수 있도록 데이터를 보낼 수 있습니다. 즉, 비대칭 암호화의 본질은 누구나 암호화할 수 있지만 읽기만 가능하다는 것입니다. Signature 클래스 로 표시되는 디지털 서명이라는 반대 절차도 있습니다 . 디지털 서명은 " 서명 알고리즘 " 알고리즘을 사용할 수 있습니다 . JCA 문서에서는 DSAwithMD5 및 RSAwithMD5 두 가지에 대해 자세히 살펴볼 것을 제안합니다. DSA 또는 RSA보다 나은 점은 무엇이며 차이점은 무엇입니까? 여기에서 읽을 수 있습니다. " 암호화된 파일 전송에 가장 적합한 것은 무엇입니까 - RSA 또는 DSA? ". 또는 여기에서 " SSH 인증 키에 대한 RSA와 DSA 비교 " 토론을 읽어보세요 . 그래서, 디지털 서명. 이전과 마찬가지로 KeyPair와 새로운 Signature 클래스가 필요합니다. 지금까지 온라인 컴파일러에서 테스트했다면 다음 예제가 다소 어려울 수 있습니다. 내 예는 여기서만 실행되었습니다: reextester.com . 필요한 클래스를 가져옵니다.
import javax.crypto.*;
import java.security.*;
우리는 또한 main 메소드를 다시 작성할 것입니다:
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));
    }
}
이것이 디지털 서명이 작동하는 방식입니다. 디지털 서명은 흥미로운 주제입니다. 이 주제에 대한 보고서를 살펴보는 것이 좋습니다.
Java 암호화 아키텍처: 첫 만남 - 12
위에서 우리는 당사자들이 데이터를 교환하는 방법을 살펴보았습니다. JCA에서 제공하는 이 상호작용을 위한 표준 인터페이스가 없나요? 존재하는 것으로 밝혀졌습니다. 그것을 살펴보자.
Java 암호화 아키텍처: 첫 만남 - 13

키계약

Java 암호화 아키텍처는 중요한 도구를 도입합니다. 키 계약은 프로토콜입니다. KeyAgreement 클래스 로 표현됩니다 . JCA 문서에 명시된 대로 이 프로토콜을 사용하면 여러 당사자가 당사자 간에 비밀 정보를 공유하지 않고도 동일한 암호화 키를 설정할 수 있습니다. 이상하게 들리나요? 그러면 예를 살펴보겠습니다.
// 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));
이 예는 JCA 문서 예인 " 2 당사자 간 Diffie-Hellman 키 교환 " 에서 가져온 것입니다 . 이는 키 계약 프로토콜을 사용하는 Java 암호화 아키텍처에서 대략적인 비대칭 암호화의 모습입니다. 비대칭 암호화에 대한 자세한 내용을 보려면 권장 동영상을 참조하세요.
Java 암호화 아키텍처: 첫 만남 - 14

인증서

글쎄, 디저트에는 여전히 덜 중요한 인증서가 있습니다. 일반적으로 인증서는 jdk에 포함된 keytool 유틸리티를 사용하여 생성됩니다. 자세한 내용은 예를 들어 " Java keytool 명령을 사용하여 자체 서명된 SSL 인증서 생성 "에서 읽을 수 있습니다. Oracle의 매뉴얼을 읽을 수도 있습니다. 예를 들어, " keytool을 사용하여 서버 인증서를 생성하려면 "입니다. 예를 들어 Tutorialspoint Java Online Compiler를 사용해 보겠습니다 .
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));
  }
}
보시다시피 인증서는 공개 키를 제공하는 기능을 제공합니다. 이 방법에는 단점이 있습니다. 우리가 사용하는 sun.security것은 위험하다고 간주됩니다. 왜냐하면... 이 패키지는 공개 Java API의 일부가 아닙니다. 그렇기 때문에 컴파일하는 동안 - 매개변수를 지정해야 합니다 XDignore.symbol.file. 또 다른 방법은 인증서를 수동으로 생성하는 것입니다. 단점은 문서화되지 않은 내부 API를 사용한다는 것입니다. 그러나 이에 대해 알아두면 유용합니다. 최소한 RFC-2459 사양이 어떻게 사용되는지 명확하게 볼 수 있기 때문입니다: " 인터넷 X.509 공개 키 인프라 ". 예는 다음과 같습니다.
// 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 암호화 아키텍처: 첫 번째 소개 - 15

키스토어(키스토어)

마지막으로 이야기하고 싶은 것은 KeyStore라고 불리는 키와 인증서 저장소입니다. 지속적으로 인증서와 키를 생성하는 것은 비용이 많이 들고 의미가 없다는 것이 분명합니다. 따라서 어떻게든 안전하게 보관해야 합니다. 이를 위한 도구가 있습니다 - KeyStore. 키 저장소는 JCA 문서의 " KeyManagement " 장에 설명되어 있습니다. 작업을 위한 API는 매우 명확합니다. 다음은 작은 예입니다.
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());
}
예제에서 볼 수 있듯이 loadKeyStore에 대해 먼저 실행됩니다. 하지만 우리의 경우에는 첫 번째 속성을 null로 지정했습니다. KeyStore에 대한 소스가 없습니다. 이는 추가 저장을 위해 KeyStore가 비어 있음을 의미합니다. 두 번째 매개변수도 null입니다. 우리는 새로운 KeyStore를 생성하고 있습니다. 파일에서 KeyStore를 로드하는 경우 여기에 비밀번호를 지정해야 합니다(store라는 KeyStore 메소드와 유사).

결론

그래서 우리는 JCA(Java Cryptography Architecture) 프레임워크 내에서 가장 기본적이고 기초적인 작업을 여러분과 함께 검토했습니다. 대칭 암호화와 비대칭 암호화가 무엇인지, JCA에서 어떻게 구현되는지 살펴보았습니다. 인증서와 디지털 서명이 어떻게 생성되고 어떻게 사용되는지 살펴보았습니다. 이것들은 모두 기본일 뿐이며 그 뒤에는 더 복잡하고 흥미로운 것들이 많이 있습니다. 이 검토 자료가 유용하고 이 분야에 대한 추가 연구에 관심을 갖기를 바랍니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION