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

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

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

# バージョン
Java 1.8
PHP 5.6

#Java

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

詳細は[JavaでAES256を使用できるようにする](http://qiita.com/mizuki_takahashi/items/cc26a7fd51aa04396e92)などの記事をご参照ください。

```java: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で行ないます。

```php: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)※非推奨

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

```php: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が非常にシンプルで良かったと思いました。（小並感）