본문 바로가기

꼰대개발자/프로그래밍 언어

PHP5.2와 JAVA에서 호환이 가능한 AES256 암호화(2)

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값이 털리면 보안이 말짱 꽝이잖아요?

반응형