비대칭키 양방향 암호화에 대해서 글을 정리한 적이 있다. 이번에는 Java에서 RSA를 구현해보고 테스트도 진행해보고자 한다.
1. 비대칭키 PublicKey, PrivateKey 생성
private static final int KEY_SIZE = 2048;
private Pair<String, String> createKeyPair() throws NoSuchAlgorithmException {
SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
return Pair.of(publicKey, privateKey);
}
RSA 알고리즘의 키를 생성하려면 키의 베이스가 되는 난수가 필요하므로 SecureRandom을 사용했다. Random을 사용하지 않고 SecureRandom을 사용한 이유는
- Random은 48비트이고 SecureRandom은 최대 128비트를 포함한 수를 만들어 내기에 난수 중복의 확률이 낮다.
- Random은 시스템 시간 베이스로 난수 생성을 하는 반면 SecureRandom은 OS의 임의 값을 베이스로 생성하기에 난수 예측이 매우 힘들다.
- 중요한 데이터를 보호하기 위해서는 Random 보다는 SecureRandom을 사용해야 한다.
java.security 에서 제공하는 KeyPairGenerator를 통해 RSA 알고리즘을 사용하는 인스턴스 생성하고 키의 크기는 2048비트를 사용하도록 설정하였다. (참고로 키 생성은 1024 또는 2048 크기로 정하면 된다. RSA-1024, RSA-2048)
2. 평문 암호화
private String encode(String plainText, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] byteEncryptedData = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(byteEncryptedData);
}
RSA 알고리즘을 사용하는 Cipher 인스턴스를 생성하여 PublicKey를 적용하고 평문을 암호화하여 byte로 변환, 변환된 byte를 상대방이 받을 수 있도록 Base64 Encoding으로 문자열로 변환을 해준다.
3. 암호문 복호화
private String decode(String encrypt, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
byte[] byteEncrypted = Base64.getDecoder().decode(encrypt.getBytes());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] byteDecryptedData = cipher.doFinal(byteEncrypted);
return new String(byteDecryptedData);
}
RSA 알고리즘을 사용하는 Cipher 인스턴스를 생성하여 PrivateKey를 적용하고 암호문을 복호화하여 byte로 변환, 변환된 byte를 문자열로 변환하여 정보를 읽는다.
4. 테스트 코드
@Test
public void RSA() throws Exception {
KeyPair keyPair = createKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("공개키:" + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("비공개키:" + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
String plainText = "민감한 개인정보입니다.";
System.out.println("평문: " + plainText);
String encryptedText = encode(plainText, publicKey);
System.out.println("암호화: " + encryptedText);
String decryptedText = decode(encryptedText, privateKey);
System.out.println("복호화: " + decryptedText);
decodeWithPublicKey(encryptedText, publicKey); // PublicKey로 decode 시도 시 Decryption Error
}
공개키 : MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTcodumQPXBpAu+2V9PN3ubPNPrsaP/rXj2ZcVvtBNMtTl17Mt6Q5OanbhJTTo5nsN753h3hQ7Al6qsJnB4Yh3cl2lkTipE6qFXFvkM1XPtlJ2/3CqmWphumF29BNbWDLtriH3QCPsy+SpGou/G82SbEdpp5noP+AK8mjLaA2cYHzVF7vguLGkKWT9EOPAF+w4oFABT/uH3wRtj/cYJnJvhNlh8cDpcmyWlpqOGJ2tjEoGYgdrffipLcMSQj6F8eyvmJlqJcGOdB4lZ/L8apNpmZdlc2MB47YgIshbKE84qZ1UN+xN+A8TjbCVYYL34WNH9w+Rert/RIZ/3OA2iJkwIDAQAB
비공개키 : MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1Nyh26ZA9cGkC77ZX083e5s80+uxo/+tePZlxW+0E0y1OXXsy3pDk5qduElNOjmew3vneHeFDsCXqqwmcHhiHdyXaWROKkTqoVcW+QzVc+2Unb/cKqZamG6YXb0E1tYMu2uIfdAI+zL5Kkai78bzZJsR2mnmeg/4AryaMtoDZxgfNUXu+C4saQpZP0Q48AX7DigUAFP+4ffBG2P9xgmcm+E2WHxwOlybJaWmo4Yna2MSgZiB2t9+KktwxJCPoXx7K+YmWolwY50HiVn8vxqk2mZl2VzYwHjtiAiyFsoTzipnVQ37E34DxONsJVhgvfhY0f3D5F6u39Ehn/c4DaImTAgMBAAECggEAD065fFH+fTIhrL3TCVg667lTh4YqsjPsDenmLE03mFC+YwBCh+mFE9DoE8Q4hUpCOA6dzMV5Yd5fVZNdHHLuX4MRD4C2xd2+trmIGqpSt6gEw3ARzjTFJQLw5MnhGEhc7HhQ4ZdLGULyUBo9xqKE8VL1BY/G9jzwF96zwu8nsz2CheL74Ls6x4nD2oNDBLabouZWTZhfS5gfcNCh5ZbLyR51J9L/6UiAd6HyyFL40EowmzHhOpf0os9X/DaUy9UsmpGdb/Z0OVbhbCD5vePaLcCTvIbs2rTqel6EzjN7tfVrkXqL6IHKfpMWn8rrC0sqZXsgy+fA7y7+pHyz80ESAQKBgQDykbd/TKUPzEkQR8Vf8zIfHAgW1hZqOUVEs5WIC0ciWb2ucMsOWnJMTO0GAnToeAzR1cbjqv9jU9MYKg95ZnRY8lbcOVfH3f97tjflJ5EkSjqM+5BQuIzP0Ig+OuPVT+X7/OF/35etIWYRTec6kFt93I7D8WjUc71zfHrfobFROwKBgQC/P8lB0f8a/dtmwirev3x9jMfLNSdr4Uhv+J/TmOGt3oM1yq9xBSRnDt8NWTXTjBdgC53sT7w4YvVHMVIHnw0oQ0LcpOFSY9NMsTAWYpYSKsOaq9st5ngGPLajty/keWrVwedLs9CRNI8313oz5p3khY64ivysxbJ+MUacQasjiQKBgQDITBLdhwh1dxaN3vXYORUWIyt6z+VKaWgNRs0evVFLyBacyH6jp4PiBVNjNvwaSacsTf4hWL3x4vHYbkLA2B0SpWmcB624WFPyb5DQJE/xS2n1qHSOcxWzNFqMKEyhg4xA+5uo35R7XazOdSmObk1eLJWmrt0FMV0q04sSxo2IqQKBgQC+g4cz90I+nCXkfoYAMRaNZyFcL44erGPhjvV/7urZrjBc9JTCbJHvy+Osw3KYUxSyC8B8uI1SSln2Wo5Ro0KTGjXP4LvIvEKwQk4Z7ol6fXkvagVD+zMTW4XBHuopmj4vxCJpyi15iLcmtJM6goBFNjqPYX0Fi0cc6G9jlxMpqQKBgCmtTFgVaFdPHWI4E0ux7Uw3V5ZSmchM1phCZuSvfvWLDxip2shVx1gBA7Izpqlt5cJ0OjrQmmu4WvL71ICbWicUyd/wDtUWeHd+eK00NYE1P78gZpysp69YpG7BTOJKbrB1oe7TsycW/pOqW/1jeS9T0cjlTfSBvdXd3iNJAmEs
평문 : 민감한 개인정보입니다.
암호화 : L28V/6rL7DZn7whhOyLL3uxVeNvdt+/bZ8bBj6WFW1PChFlHydNtSO8rpUtSg1n9C5W5aeC2GZ8Co3FqtyTBPlPQNCnqe6RVe1JvWwdBsfeLEedXK2IKyMfyeise0UrlGawwfGHgSlASlTJc/4J2hZDiWQjQEvQpBdw639VkTWikrNlFYUPeW781cKgqkIx3NM6TLBumTkFasVDhhOE4uCfxOsGyIuxAj3Vur060te9Lrfn9dn/JpJICGWv4MeDJILi22RQfgdiFa3VyGB2v8XfOFsk03w1GoiCtrRTUXF5ZnRHS6YpmmeQ699EQjlbxN0cj/e1d+ycKQgmjzFU91A==
복호화 : 민감한 개인정보입니다.
위의 로그와 같이 서로 다른 키 두 개와 PublicKey로 만든 암호문을 PrivateKey로 복호화하여 평문의 정보를 얻을 수 있다.
마지막으로 암호문을 PublicKey로 복호화 시도 시 에러가 발생되므로 복호화는 PrivateKey로만 할 수 있다.
공개키를 pem 확장자를 가진 파일로 업로드하는 방식을 사용하고는 하는데 pem 파일을 확인하면 문자열로 구성되어 있다. 물론 사람이 읽을 수 없는 정보이다. 이번 테스트를 통해서 공개키, 비공개키의 사용에 대한 인코딩 디코딩 흐름을 볼 수 있도록 구성하였다.
'Java' 카테고리의 다른 글
[Java] 0.1 + 0.2 = 0.3이 아니다! (0) | 2022.11.11 |
---|---|
[Java] 클래스로더(Class loader) (0) | 2022.11.10 |
[Java] JDK11 특징 (0) | 2022.11.09 |
[Java] Reflection으로 객체 비교 (0) | 2022.11.08 |
[Java] String, StringBuffer, StringBuilder 특징 (0) | 2022.11.07 |
댓글