key값과 iv값을 관리해야 하는 문제점이 있어서 수정된 버전입니다.
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
import java.util.Arrays;
import java.security.SecureRandom;
public class AESTest {
// 제로패딩
private static byte[] zeroPad(byte[] data, int blockSize) {
int remainder = data.length % blockSize;
if (remainder == 0) return data;
byte[] padded = new byte[data.length + (blockSize - remainder)];
System.arraycopy(data, 0, padded, 0, data.length);
return padded; // 나머지는 자동으로 0x00
}
// trailing zero 제거
private static byte[] trimTrailingZeros(byte[] data) {
int i = data.length;
while (i > 0 && data[i-1] == 0) i--;
return Arrays.copyOf(data, i);
}
// 암호화
public static String encrypt(String plaintext, String key) throws Exception {
byte[] keyBytes = key.getBytes("UTF-8");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
// 랜덤 IV
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// zero-padding
byte[] plainPadded = zeroPad(plaintext.getBytes("UTF-8"), 16);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); // NoPadding
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(plainPadded);
// IV + Ciphertext 결합
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
return Base64.getEncoder().encodeToString(combined);
}
// 복호화
public static String decrypt(String base64IvCipher, String key) throws Exception {
byte[] decoded = Base64.getDecoder().decode(base64IvCipher);
byte[] iv = Arrays.copyOfRange(decoded, 0, 16);
byte[] cipherBytes = Arrays.copyOfRange(decoded, 16, decoded.length);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); // NoPadding
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decryptedPadded = cipher.doFinal(cipherBytes);
byte[] trimmed = trimTrailingZeros(decryptedPadded);
return new String(trimmed, "UTF-8");
}
public static void main(String[] args) throws Exception {
String key = "12345678123456781234567812345678";
String plain = "story4some1";
String enc = encrypt(plain, key);
System.out.println("Encrypted(Base64): " + enc);
String dec = decrypt(enc, key);
System.out.println("Decrypted: " + dec);
}
}
/*
Encrypted(Base64): 4yv+FF1FGJUvw+LkGboyEBKx2d5qh4ZX69H12q3rGp0=
Decrypted: story4some1
*/
<?php
// 키 (32바이트 → AES-256)
$key = "12345678123456781234567812345678";
// 원문
$plaintext = "story4some1";
// --- 암호화 ---
$iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); // 랜덤 IV 생성
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$plaintext,
MCRYPT_MODE_CBC,
$iv
);
// 결과 = IV + 암호문 → Base64 저장
$final = base64_encode($iv . $ciphertext);
echo "Encrypted: " . $final . "\n";
// --- 복호화 ---
$data = base64_decode($final);
$iv_dec = substr($data, 0, 16);
$ciphertext_dec = substr($data, 16);
$decrypted = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext_dec,
MCRYPT_MODE_CBC,
$iv_dec
);
// PKCS#7 패딩 제거
$pad = ord($decrypted[strlen($decrypted) - 1]);
if($pad != 0) $decrypted = substr($decrypted, 0, -$pad);
echo "Decrypted: " . $decrypted . "\n";
/*
Encrypted: H790ZyMTYBMLV2TkKB+rpns2L7kVfmat2qlqmdBg6L8=
Decrypted: story4some1
*/
?>
다음에는 key값을 따로 관리하는 방법을 올리겠습니다. key값이 털리면 보안이 말짱 꽝이잖아요?

반응형
'꼰대개발자 > 프로그래밍 언어' 카테고리의 다른 글
| X(구 트위터)에 자동으로 포스팅하기 (0) | 2026.05.13 |
|---|---|
| Laravel 13 세팅할 때 발생한 오류에 대한 정리 (0) | 2026.04.15 |
| PHP5.2와 JAVA에서 호환이 가능한 AES256 암호화 (0) | 2025.09.01 |
| 스프링 실무에서 MyBatis와 JPA를 비교해 본다. (feat. 영속성 컨텍스트) (4) | 2025.08.14 |
| 객체 지향이라? 오브젝트 지향이라고 불러다오... (9) | 2025.08.08 |