본문 바로가기

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

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

현재 구축된 시스템에 PHP5.2인데 JAVA와 호환이 가능한 코드가 필요하여 적어봅니다.

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class AES256Example {

    // zero-padding (mcrypt 기본 동작)
    private static byte[] zeroPad(byte[] data, int blockSize) {
        int remainder = data.length % blockSize;
        if (remainder == 0) {
            return data; // mcrypt는 블록 크기와 정확히 맞으면 추가 패딩을 하지 않습니다.
        }
        int paddedLen = data.length + (blockSize - remainder);
        byte[] padded = new byte[paddedLen];
        System.arraycopy(data, 0, padded, 0, data.length);
        // 나머지 바이트는 0x00으로 자동 초기화되므로 따로 채울 필요 없음
        return padded;
    }

    // trailing NUL(0x00) 제거 (PHP의 rtrim(..., "\0")과 동일 동작)
    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, String iv) throws Exception {
        if (key == null || key.getBytes(StandardCharsets.UTF_8).length != 32) {
            throw new IllegalArgumentException("Key must be 32 bytes (AES-256).");
        }
        if (iv == null || iv.getBytes(StandardCharsets.UTF_8).length != 16) {
            throw new IllegalArgumentException("IV must be 16 bytes.");
        }

        byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8);
        byte[] padded = zeroPad(plainBytes, 16);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); // NoPadding — we 처리한 패딩을 사용
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));

        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        byte[] encrypted = cipher.doFinal(padded);

        return Base64.getEncoder().encodeToString(encrypted);
    }

    public static String decrypt(String base64CipherText, String key, String iv) throws Exception {
        if (key == null || key.getBytes(StandardCharsets.UTF_8).length != 32) {
            throw new IllegalArgumentException("Key must be 32 bytes (AES-256).");
        }
        if (iv == null || iv.getBytes(StandardCharsets.UTF_8).length != 16) {
            throw new IllegalArgumentException("IV must be 16 bytes.");
        }

        byte[] cipherBytes = Base64.getDecoder().decode(base64CipherText);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));

        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        byte[] decryptedPadded = cipher.doFinal(cipherBytes);

        byte[] trimmed = trimTrailingZeros(decryptedPadded);
        return new String(trimmed, StandardCharsets.UTF_8);
    }

    // 테스트 예제
    public static void main(String[] args) throws Exception {
        String key = "12345678123456781234567812345678"; // 32바이트
        String iv  = "1234567890123456";                 // 16바이트

        String plain = "story4some1";
        String enc = encrypt(plain, key, iv);
        System.out.println("Base64 암호문: " + enc);

        String dec = decrypt(enc, key, iv);
        System.out.println("복호화: " + dec);
    }
    
    /*
    Base64 암호문: GK3yp1MRRJaHHfeTQzOMHQ==
    복호화: story4some1    
    */
}

 

 

<?php
function aes256_encrypt($plaintext, $key, $iv) {
	// AES-256-CBC 암호화
	$ciphertext = mcrypt_encrypt(
			MCRYPT_RIJNDAEL_128,              // 블록 암호: AES (Rijndael-128)
			$key,                             // 키 (32바이트)
			$plaintext,                       // 평문
			MCRYPT_MODE_CBC,                  // CBC 모드
			$iv                               // IV (16바이트)
			);
	return base64_encode($ciphertext);    // 안전하게 Base64 인코딩
}

function aes256_decrypt($ciphertext_base64, $key, $iv) {
	$ciphertext = base64_decode($ciphertext_base64);
	$plaintext = mcrypt_decrypt(
			MCRYPT_RIJNDAEL_128,
			$key,
			$ciphertext,
			MCRYPT_MODE_CBC,
			$iv
			);
	return rtrim($plaintext, "\0"); // PKCS#7 패딩 제거
}

// ----------------- 테스트 -----------------
$key = "12345678123456781234567812345678"; // 32바이트 (AES-256)
$iv  = "1234567890123456";                 // 16바이트 (AES 블록 크기)

$plain = "story4some1";
echo "평문: $plain\n";

$enc = aes256_encrypt($plain, $key, $iv);
echo "암호문 (Base64): $enc\n";

$dec = aes256_decrypt($enc, $key, $iv);
echo "복호화 결과: $dec\n";

/*
평문: story4some1
암호문 (Base64): GK3yp1MRRJaHHfeTQzOMHQ==
복호화 결과: story4some1
*/
?>

 

참고로 PHP7부터는 mcrypt가 제거되었으므로 다른 방법을 사용해야 합니다. 

 

 

 

반응형