AES256による暗号化/復号をPHPとJavaでやる


お疲れ様です!
GMOリサーチの石黒です!

さて、皆さんも急にAESで暗号化したくなる衝動に駆られることがあると思います。
そんな皆さんのために今回は、JavaとPHP、PHPはOpenSSLとMcryptの、計3種類の方法で暗号化/複合のコードを書いていきます。
AES256アルゴリズム、ECBモード、PKCS5パディングで実装しています。

バージョン

Java 1.8
PHP 5.6

Java

Javaでは、デフォルトでAESの鍵の長さは128bitしか扱うことができません。
256bitの鍵を扱うためには、Javaのポリシーファイルを差し替える必要があります。

詳細はJavaでAES256を使用できるようにするなどの記事をご参照ください。

aes01.java
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class aes01 {

    public static void main(String[] args) {
        try {
            byte[] key = DatatypeConverter.parseBase64Binary(
                    "キーをbase64エンコードした文字列");
            SecretKeySpec sks = new SecretKeySpec(key, "AES");

            byte[] input = "暗号化したい文字列".getBytes();

            // 暗号化
            Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
            c.init(Cipher.ENCRYPT_MODE, sks);
            byte encrypted[] = c.doFinal(input);

            System.out.println(DatatypeConverter.printBase64Binary(encrypted));

            // 復号
            c.init(Cipher.DECRYPT_MODE, sks);
            byte decrypted[] = c.doFinal(encrypted);

            System.out.println(new String(decrypted));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

PHP(OpenSSL)

PHP5.3以降での暗号化はOpenSSLで行ないます。

aes01.php
<?php
$key = base64_decode("キーをbase64エンコードした文字列");

$input = '暗号化したい文字列';

// 暗号化
$encrypted = openssl_encrypt($input, 'aes-256-ecb', $key);

echo $encrypted;

// 復号
$decrypted = openssl_decrypt($encrypted, 'aes-256-ecb', $key);

echo $decrypted;

PHP(Mcrypt)※非推奨

非推奨な方法での暗号化です。
必要な時以外は使用しないようにしましょう。
というか面倒臭すぎます。

aes02.php
<?php
$key = base64_decode('キーをbase64エンコードした文字列');

$input = '暗号化したい文字列';

$crypt = new Crypt($key);

// 暗号化
$encrypted = base64_encode($crypt->encrypt($input));

echo $encrypted;

// 復号
$decrypted = $crypt->decrypt(base64_decode($encrypted));

echo $decrypted;

class Crypt {

    private $__encrypt_key = null;

    public $iv = null;

    public function __construct($encrypt_key) {
        $this->__encrypt_key = $encrypt_key;
    }

    public function encrypt($input, $algo = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_ECB) {
        list($size, $td) = $this->__open($algo, $mode);
        $input = $this->__pkcs5Pad($input, $size);
        $data = mcrypt_generic($td, $input);
        $this->__close($td);
        return $data;
    }

    public function decrypt($input, $algo = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_ECB) {
        list ($size, $td) = $this->__open($algo, $mode);
        $input = mdecrypt_generic($td, $input);
        $data = $this->__pkcs5Unpad($input);
        $this->__close($td);
        return $data;
    }

    private function __open($algo, $mode) {
        $size = mcrypt_get_block_size($algo, $mode);
        $td = mcrypt_module_open($algo, '', $mode, '');
        $this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $this->__encrypt_key, $this->iv);
        return array($size, $td);
    }

    private function __close($td){
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
    }

    public static function __pkcs5Pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    public static function __pkcs5Unpad($text) {
        $pad = ord($text{strlen($text)-1});
        if ($pad > strlen($text)) return false;
        if (strspn($text, chr($pad), strlen($text) - $pad) !== $pad) return false;
        return substr($text, 0, -1 * $pad);
    }
}

MCRYPT_RIJNDAEL_128 というものが、AES256に相当するものらしいです。
MCRYPT_RIJNDAEL_256 というアルゴリズムもありますが、これではないので注意。

一応、汎用的に使えるよう、アルゴリズムとモードの変更はできるようになっていますが、
本当に使えるかは不明です。

おわりに

以上です!
これでAES暗号化したい衝動に駆られても大丈夫ですね。

PHPのOpenSSLが非常にシンプルで良かったと思いました。(小並感)