ClojureでのAES暗号化するコードを作ってみました。
Javaの標準ライブラリを使ってできるのでそんなに難しくないですね。
(import '[java.security SecureRandom Key]
'[javax.crypto KeyGenerator Cipher]
'[javax.crypto.spec IvParameterSpec]
'[java.security.spec AlgorithmParameterSpec])
(defn ^bytes rand-bytes [n]
(let [b (byte-array n)]
(.. (SecureRandom.) (nextBytes b))
b))
(defn ^Key aes-genkey [^long bits]
{:pre[(#{128 192 256} bits)]}
(let [kg (KeyGenerator/getInstance "AES")]
(.init kg bits)
(.generateKey kg)))
(def ^:dynamic *aes-mode* "AES/CBC/PKCS5Padding")
(defn- ^bytes aes-*code [^long mode ^bytes bytes ^Key key ^AlgorithmParameterSpec spec]
(let [c (Cipher/getInstance *aes-mode*)]
(if spec
(.init c mode key spec)
(.init c mode key))
(.doFinal c bytes)))
(defn ^bytes aes-encode [^bytes bytes ^Key key & [^AlgorithmParameterSpec spec]]
(aes-*code Cipher/ENCRYPT_MODE bytes key spec))
(defn ^bytes aes-decode [^bytes bytes ^Key key & [^AlgorithmParameterSpec spec]]
(aes-*code Cipher/DECRYPT_MODE bytes key spec))
使い方はこんな感じで
(def k (aes-genkey 128))
(def iv (rand-bytes 16))
(def encoded (aes-encode (byte-array 2) k (IvParameterSpec. iv)))
(def decoded (aes-decode encoded k (IvParameterSpec. iv)))
(binding [*aes-mode* "AES/ECB/NoPadding"]
(let [e (aes-encode (byte-array 16) k)]
(seq (aes-decode e k))))
*aes-mode*
には以下のものが指定できるようです。
- AES/CBC/NoPadding
- AES/CBC/PKCS5Padding
- AES/ECB/NoPadding
- AES/ECB/PKCS5Padding
CBCはAES鍵は同じでも暗号化のたびにIV(Initialization Vector)を変えることで、同じ平文でも異なる暗号文にする方式です。復号する側には暗号化のたびにIVを教える必要があります。
ECBでは鍵が同じなら同じ平文には同じ暗号文が出力されるので、暗号強度は弱くなります。
NoPaddingは平文がAESのブロック長である16バイトの倍数であると確定している場合にのみ使えます。16の倍数でない場合は例外が発生します。
PKCS5Paddingは平文が16バイトの倍数であるとは限らない場合に使います。(方式としてはバイト数を16で割った余りが1のときは0x0fを15バイト追加、余りが2のときは0x0eを14バイト追加、余りが15のときは0x01を1バイト追加、そして余りが0のときは0x10を16バイト追加します)
※なお、正式な名称としてはPKCS#7 Paddingとなるようです。PKCS#5 PaddingはDESの頃のブロック長が8バイトのときの名称とのこと。Bouncy CastleではPKCS7の名称になっていたと思います。
※ちなみに、復号の際にPadding関連の例外が発生した場合、Paddingの誤りではなく復号の失敗(鍵、IVの誤り)であるケースがほとんどです。
AESの鍵長はOracle JDKではデフォルトでは128ビットしか使えません。192, 256ビットで使いたい場合はJava Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Filesをダウンロードしてjre/lib/securityに配置(上書き)する必要があります。
この処置を行わないとjava.security.InvalidKeyException: Illegal key size
のような例外が発生します。