理系学生日記

おまえはいつまで学生気分なのか

コマンドラインで簡単にAES暗号化、または Java での AES 暗号化

Java で AES 暗号化とかやってて、コマンドラインで簡単に試せれば良いのになぁとか思ってたら、openssl 使ったら余裕で暗号化できることに気付いた。

例えば、AES/CBC の 128 bit 鍵長で暗号化したい場合は、以下のようにすれば良い。 -e は暗号化、-d は復号化を示している。

$ echo "kiririmode" | openssl aes-128-cbc -e -base64
enter aes-128-cbc encryption password:
Verifying - enter aes-128-cbc encryption password:
U2FsdGVkX190LTIvjNslBh78S+fbl+Lj8akdU/I9qGY=

$ echo "U2FsdGVkX190LTIvjNslBh78S+fbl+Lj8akdU/I9qGY=" | openssl aes-128-cbc -d -base64
enter aes-128-cbc decryption password:
kiririmode

共通鍵と初期ベクトルはどこにいった?

共通鍵も、(CBC にも関わらず)初期ベクトルを指定していないのだけれど、それは openssl が自動生成してくれる。この自動生成については途中でパスワードを尋ねられているのがミソで、実は openssl では、入力したパスワードから共通鍵と初期ベクトルを自動生成している。 このあたりの解説は、以下のサイトがくわしい。

実際に使われた共通鍵と初期ベクトルについては、-p オプションをつければ分かる。 なお、パスワードをインタラクティブに聞かれるのが煩わしい場合は -pass オプションで指定が可能。

$ echo "kiririmode" | openssl aes-128-cbc -e -base64 -p -pass pass:password
salt=ABD594EB1B826A18
key=1C99D62AFD668EDF30EE2E04385B6E29
iv =FD086E25447B49E23D48FDB3A75CF09C
U2FsdGVkX1+r1ZTrG4JqGInbs//CpkHEzmqvmlsLGTE=

共通鍵と初期ベクトルを明示的に指定する

当然ながら共通鍵と初期ベクトルは明示的に指定することが可能で、-K-iv オプションで、それぞれ共通鍵と初期ベクトルを指定する。 指定フォーマットは HEX encoded なので、双方ともに 16 進数で 32 桁 (128 bit) を指定すれば良い。 この場合、共通鍵、初期ベクトルは指定済なので、パスワードを指定する必要はない。

$ echo "kiririmode" | openssl aes-128-cbc -e -base64 -iv 1234567890ABCDEF1234567890ABCDEF -K 12345678901234567890123456789012 | openssl aes-128-cbc -d -base64 -iv 1234567890ABCDEF1234567890ABCDEF -K 12345678901234567890123456789012
kiririmode

パディング方式は?

openssl では、Padding 方式は PKCS#5 を使用する。このため、同様に PKCS#5 をサポートする実行系では openssl の暗号化結果を復号化できるし、また逆も然り。 たとえば、以下のプログラムは、openssl の暗号化結果を復号化できるし、また、その逆も当然可能。

package com.kiririmode.blog;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class AESEncryptor {

    private static String ALGORITHM = "AES/CBC/PKCS5Padding";

    public String encrypt(String hexEncodedKey, String hexEncodedIv, String plainText) throws GeneralSecurityException {

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(hexEncodedKey), getIv(hexEncodedIv));
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

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

    public String decrypt(String hexEncodedKey, String hexEncodedIv, String cipheredString)
            throws GeneralSecurityException {
        byte[] ciphered = Base64.getDecoder().decode(cipheredString);

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, getSecretKey(hexEncodedKey), getIv(hexEncodedIv));
        byte[] plainBytes = cipher.doFinal(ciphered);

        return new String(plainBytes, StandardCharsets.UTF_8);

    }

    private Key getSecretKey(String hexEncodedKey) {
        return new SecretKeySpec(DatatypeConverter.parseHexBinary(hexEncodedKey), "AES");
    }

    private AlgorithmParameterSpec getIv(String hexEncodedIv) {
        return new IvParameterSpec(DatatypeConverter.parseHexBinary(hexEncodedIv));
    }
}

その他に使用できる暗号は?

openssl enc -h あたりで見ることができるよ!