[암호화] Encryption/Decryption (대칭키, 공개키, 단방향) - AES, RSA, SHA
AES (Adavanced Encryption Standard AES)
대칭형, 블럭 암호화 알고리즘이다.대칭형 암호화 알고리즘중 가장 유명하고, 128, 192, 256 bit 중 하나가 된다.
암호화 키의 길이에 따라 실행하는 라운드 수가 다른데, 각각 10(128), 12(192), 14(256) 라운드를 실행한다.
1. AES모드
- ECB (Electronic Codebook): 평문 블록을 독립적으로 암호화하는 가장 기본적인 모드입니다. 하지만, 동일한 평문 블록이 동일한 암호문 블록으로 변환되기 때문에 패턴이 노출될 수 있습니다. 보안성이 약한 모드입니다.
- CBC (Cipher Block Chaining): 이전 블록의 암호문과 현재 평문 블록을 XOR하여 암호화하는 모드입니다. 초기화 벡터(IV)를 사용하여 암호문의 예측을 어렵게 합니다. 블록 간의 의존성으로 인해 병렬 처리가 어렵습니다.
- CFB (Cipher Feedback): 블록 암호를 스트림 암호처럼 사용하는 모드입니다. 이전 암호문 블록을 평문 블록과 XOR하여 스트림을 생성하고, 그 스트림을 평문과 XOR하여 암호문을 생성합니다. 피드백 사이즈에 따라 블록 또는 비트 CFB 모드가 있습니다.
- OFB (Output Feedback): CFB와 유사하지만, 스트림을 생성하기 위해 이전 암호문 블록을 사용하는 대신에 암호화 키로부터 생성된 스트림을 사용합니다. OFB 모드는 암호화와 복호화에 동일한 알고리즘을 사용할 수 있어서 유용합니다.
- CTR (Counter): 논스(Nonce) 또는 카운터 값을 사용하여 평문을 암호문으로 변환합니다. 병렬 처리가 가능하고, 블록 간의 의존성이 없어서 처리 속도가 빠릅니다. 스트림 암호와 유사한 동작을 하기 때문에 스트림 모드로도 볼 수 있습니다.
2. 패딩 (PCKS5, PKCS7)
: AES 패딩은 AES 블록 암호화에서 마지막 블록이 블록 크기와 일치하지 않을 때 사용되는 추가 데이터입니다. 주로 마지막 블록이 부족한 경우에 사용됩니다.
- 암복화 알고리즘 경우 input 데이터의 길이는 block size의 배수가 되어야 한다.
- 하지만, 데이터의 길이가 block size의 배수가 아닌 경우 마지막 블록에 값을 추가해 block size의 배수로 맞춘다.
- 이때, 추가 되는 행위 또는 값을 padding 이라고 한다.
- PKCS5 : 8바이트 블록 사이즈에 맞추어져 패딩이 들어갑니다. 그리고 그 값으로 몇 바이트를 패딩으로 채웠는지 적혀 있습니다.
- AA 07 07 07 07 07 07 07 [1 바이트 데이터 + 7 바이트 패딩]
- AA BB CC DD 04 04 04 04 [4 바이트 데이터 + 4 바이트 패딩]
- 08 08 08 08 08 08 08 08 [0 바이트 데이터 + 8 바이트 패딩]
public class AES256 {
private static final String AES_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final int KEY_SIZE = 256;
private static final int IV_SIZE = 16;
// 예제용 키와 초기화 벡터(IV)
private static final String KEY = "ThisIsASecretKey";
private static final String IV = "InitializationVe";
public static String encrypt(String plaintext) throws Exception {
byte[] keyData = KEY.getBytes();
byte[] ivData = IV.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(keyData, AES_ALGORITHM);
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String ciphertext) throws Exception {
byte[] keyData = KEY.getBytes();
byte[] ivData = IV.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(keyData, AES_ALGORITHM);
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
return new String(decryptedBytes, "UTF-8");
}
public static void main(String[] args) {
try {
String plaintext = "Hello, AES256!";
String ciphertext = encrypt(plaintext);
System.out.println("암호화된 텍스트: " + ciphertext);
String decryptedText = decrypt(ciphertext);
System.out.println("복호화된 텍스트: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}
RSA
: Rivest, Shamir, Adleman의 이름에서 따온 암호화 알고리즘입니다. 공개키 암호화 방식의 하나로, 공개키와 개인키를 사용하여 데이터를 암호화 및 복호화합니다.
- 공개키 (Public Key):
- 암호화에 사용되며, 누구에게나 공개될 수 있습니다..
- 개인키 (Private Key):
- 복호화에 사용되며, 소유자만 알고 있어야 합니다.
RSA 알고리즘은 큰 소수를 사용하여 키를 생성합니다. 암호화와 복호화는 모듈러 연산을 사용하여 수행됩니다. 이러한 방식으로 RSA는 안전하고 강력한 암호화를 제공합니다.
RSA는 주로 인터넷 통신에서 사용되며, 전자 서명, SSL/TLS 프로토콜, 데이터 암호화 등 다양한 보안 응용 프로그램에 활용됩니다. 그러나 RSA는 계산량이 많아서 대량의 데이터를 처리하는 데는 적합하지 않을 수 있습니다. 따라서 대량의 데이터를 보호해야 하는 경우에는 대칭키 알고리즘과 결합하여 사용하는 것이 일반적입니다.
public class RSA {
public static void main(String[] args) {
try {
// RSA 키 페어 생성
KeyPair keyPair = generateKeyPair();
// 공개키와 개인키 추출
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 원문
String plaintext = "Hello, RSA!";
// 암호화
String encryptedText = encrypt(plaintext, publicKey);
System.out.println("암호화된 텍스트: " + encryptedText);
// 복호화
String decryptedText = decrypt(encryptedText, privateKey);
System.out.println("복호화된 텍스트: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // 키 크기 설정
return keyPairGenerator.generateKeyPair();
}
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String ciphertext, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
return new String(decryptedBytes);
}
}
SHA256
: SHA-2 (Secure Hash Algorithm 2)는 미국 국가안보국(NSA)가 설계한 암호화 해시 함수들의 집합이다.
일방향암호화로 복호화가 불가합니다.
"데이터의 위 변조 유무 확인"(정보의 무결성 확인)입니다. 통신 중인 메시지 내용이 위 변조가 발생했는지를
확인하기 위해서 사용하는 것을 말합니다.
public class SHA256 {
public static void main(String[] args) {
String originalString = "Hello, SHA-256!";
try {
String hashedString = hashString(originalString);
System.out.println("해시된 문자열: " + hashedString);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static String hashString(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte hashByte : hashBytes) {
String hex = Integer.toHexString(0xff & hashByte);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
https://ojava.tistory.com/103
https://mike-tyson.tistory.com/11
https://m.blog.naver.com/doksg/221811748218