おじゃまぷよ系エンジニアメモ

アプリエンジニアからサーバーとインフラエンジニアに転身しました

queryパラメーターをPHPで暗号化して渡してJavaで復元したい

とあるPHPで書かれたWebサービスAから、とあるJavaで書かれたWebサービスBにパラメーターを暗号化してqueryパラメーターで渡したいときがあったのでその時のメモ

PHP側の暗号・復号化を行うクラス

<?php
class MyCipher {
    const SECRET_KEY = "1234567890123456"; //Javaと共通の秘密鍵。ランダウな文字列16桁
    const IV_PARAMETER = "1234567890123456"; //Javaと共通のIV。ランダムな文字列16桁
    const METHOD = "AES-128-CBC"; //256のほうが安全だが、Java側がデフォルトで256使えないので128にしてる

    /**
     * 受け取った文字列を暗号化しBase64+URLエンコードして返す
     * @param $originalStr
     * @return string
     */
    public static function encrypt($originalStr) {
        return urlencode(openssl_encrypt($originalStr, self::METHOD, self::SECRET_KEY, false, self::IV_PARAMETER));
    }

    /**
     * Base64+URLEncodeされている暗号化文字列を元の文字列に復号する
     * @param $encryptStr
     * @return string
     */
    public static function decrypt($encryptStr){
        return openssl_decrypt(urldecode($encryptStr), self::METHOD, self::SECRET_KEY, false, self::IV_PARAMETER);
    }
}

$encrypt = MyCipher::encrypt("abcdef"); //JxdRLkrOq5SDY9YMcUSUhg%3D%3D
$decrypt = MyCipher::decrypt($encrypt); //abcdef
echo $decrypt === "abcdef"; //復号される

Javaの暗号・復号化を行うクラス

public final class MyCipher {

    private static final String SECRET_KEY = "1234567890123456";// 秘密鍵は16文字で。PHP側と同じものを指定する
    private static final byte[] IV_Parameter = "1234567890123456".getBytes();//IVの値。PHP側と同じものを指定する
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";// 暗号化方式

    /**
     * 文字列を16文字の秘密鍵でAES暗号化してBase64+URLEncodeした文字列で返す
     */
    public static String encrypt(String originalSource) throws Exception {
        Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
        //文字列をバイト列に変換
        byte[] originalBytes = originalSource.getBytes();
        //暗号化後のバイト列取得
        byte[] encryptBytes = cipher.doFinal(originalBytes);
        //バイト列を文字列にして返す
        return URLEncoder.encode(Base64.getEncoder().encodeToString(encryptBytes),"UTF-8");
    }

    /**
     * Base64+URLEncodeされたAES暗号化文字列を元の文字列に復元する
     */
    public static String decrypt(String encryptStr) throws Exception {

        //暗号化されたmd文字列からバイト列を取得
        //JavaのUrlDecoderは「+」を半角スペースに変えてしまうので、「%2B」に置換する
        byte[] encryptBytes = Base64.getDecoder().decode(URLDecoder.decode(encryptStr.replace("+", "%2B"),"UTF-8"));
        Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
        //復号化後にバイト列取得
        byte[] originalBytes = cipher.doFinal(encryptBytes);
        //文字列にして返却
        return new String(originalBytes);
    }

    private static Cipher initCipher(int method) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(method,new SecretKeySpec(SECRET_KEY.getBytes(),"AES"),new IvParameterSpec(IV_Parameter));
        return cipher;
    }
}
public class Sample {
    public static void main(String[] args) throws Exception {
        String encrypt = MyCipher.encrypt("abcdef");
        System.out.println(encrypt); //JxdRLkrOq5SDY9YMcUSUhg%3D%3D
        System.out.println(MyCipher.decrypt(encrypt)); //abcdef
        //復号化されてる
    }
}

これでPHPJavaで相互に値を受け渡しできるようになりました。 秘密鍵と初期ベクトルが外部に漏れない限りは復号できないと思われるので、まぁ十分に安全かと思います。
ただ不安はつきものなのであまり重要なデータは別の方法で受け渡ししたほうが良いかもしれない。

参考URL : stackoverflow.com

http://itmemo.net-luck.com/java-aes/

日々是妄想 : 初期化ベクトルってなに?(解決編)