S-JIS[2006-11-14/2008-08-15] 変更履歴
Javaには、Cipherという暗号化・復号化を行うクラスが用意されている。
DESとか色々な種類の暗号をこのクラスによって使うことが出来る。
AESは JDK1.4.1ではサポートされていないが、JDK1.4.2ではサポートされている。
JDK1.5でもサポートされているが、128bit以外は使えないっぽい。
JDK1.6ではjce_policyを更新すれば使える。[2008-08-15]
|
秘密鍵(暗号化・復号化で同じものを使う)をバイト列で用意して使う例。
import java.security.AlgorithmParameters; import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec;
public static void main(String[] args) {
Key skey = makeKey1(128);
// 暗号化
byte[] enc = encode1(args[0].getBytes(), skey);
// 復号化
byte[] dec = decode1(enc, skey);
System.out.println(new String(dec));
}
/**
* 秘密鍵をバイト列から生成する
* @param key_bits 鍵の長さ(ビット単位)
*/
public static Key makeKey1(int key_bits) {
// バイト列
byte[] key = new byte[key_bits / 8];
// バイト列の内容(秘密鍵の値)はプログラマーが決める
for (int i = 0; i < key.length; i++) {
key[i] = (byte) (i + 1);
}
return new SecretKeySpec(key, "AES");
}
/**
* 暗号化
*/
public static byte[] encode1(byte[] src, Key skey) {
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
return cipher.doFinal(src);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 復号化
*/
public static byte[] decode1(byte[] src, Key skey) {
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skey);
return cipher.doFinal(src);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
鍵をランダムで生成する例。
乱数を生成するSecureRandomのgetInstance()に指定する引数は"SHA1PRNG"にする(それしか指定できない)。
public static void main(String[] args) { Key skey = makeKey2(128); // 暗号化 byte[] enc = encode1(args[0].getBytes(), skey); // 復号化 byte[] dec = decode1(enc, skey); System.out.println(new String(dec)); } /** * 秘密鍵をランダムに生成する * @param key_bits 鍵の長さ(ビット単位) */ public static Key makeKey2(int key_bits) { try { KeyGenerator generator = KeyGenerator.getInstance("AES"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); generator.init(key_bits, random); return generator.generateKey(); } catch (Exception e) { throw new RuntimeException(e); } }
イニシャルバリュー(IV)を使う形式の例。
Cipher#getInstance()で、モードに「CBC」を指定する。(今までは
これを省略して、デフォルトのものを使っていた)
この場合、暗号化する際にIVというものが自動的に作られ、使われる。
復号化する際には 暗号化に使用したIVが必要となるので、何らかの方法で受け渡さなければならない。
ここでは、暗号化したデータの先頭にIVをくっつけることにした。
復号化する際には、先頭16バイトをIVとして扱い、残りのデータを復号する。
(IVのサイズは16バイトっぽい。これはブロックサイズと同じなのだと思われる)
AlgorithmParameters#init()には、本来は暗号化時に取得したAlgorithmParameters#getEncoded()の返り値と同じものを渡すんだけど、正当性/将来性/互換性を全く無視して、無理矢理作ってみた。(JavaVMのバージョンが変わらなければ、きっとこれで動くでしょう)
public static void main(String[] args) { Key skey = makeKey1(128); //暗号化 byte[] enc = encode3(args[0].getBytes(), skey); //復号化 byte[] dec = decode3(enc, skey); System.out.println(new String(dec)); } /** * 暗号化 */ public static byte[] encode3(byte[] src, Key skey) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, skey); // AlgorithmParameters prm = cipher.getParameters(); // System.out.println(prm); // byte[] prme = prm.getEncoded(); // for (int i = 0; i < prme.length; i++) { // System.out.print(Integer.toHexString(prme[i] & 0xff) + " "); // } // System.out.println(); byte[] iv = cipher.getIV(); byte[] enc = cipher.doFinal(src); byte[] ret = new byte[iv.length + enc.length]; System.arraycopy(iv, 0, ret, 0, iv.length); System.arraycopy(enc, 0, ret, iv.length, enc.length); return ret; } catch (Exception e) { throw new RuntimeException(e); } } /** * 復号化 */ public static byte[] decode3(byte[] src, Key skey) { try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); final int BLOCK_SIZE = cipher.getBlockSize(); AlgorithmParameters iv = AlgorithmParameters.getInstance("AES"); byte[] ib = new byte[2 + BLOCK_SIZE]; ib[0] = 4; // getEncoded()で取った値がこういう数字になっている ib[1] = (byte) BLOCK_SIZE; // 動きはするけど、これで正しいのかどうかは不明 System.arraycopy(src, 0, ib, 2, BLOCK_SIZE); iv.init(ib); cipher.init(Cipher.DECRYPT_MODE, skey, iv); return cipher.doFinal(src, BLOCK_SIZE, src.length - BLOCK_SIZE); } catch (Exception e) { throw new RuntimeException(e); } }
AESは16バイトずつ暗号化する。(16バイトでブロック化)
したがって、暗号化すべきデータのサイズは16の倍数でないといけない。
PKCS5Paddingを指定すると、16の倍数でない場合に適当にデータを補ってくれる。(その結果、暗号化したデータのサイズは16の倍数となる)
暗号化するデータが16の倍数に決まっている場合、パディングを指定しないことも可能。
/** * 暗号化 */ public static byte[] encode4(byte[] src, Key skey) { try { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, skey); byte[] iv = cipher.getIV(); byte[] enc = cipher.doFinal(src); byte[] ret = new byte[iv.length + enc.length]; System.arraycopy(iv, 0, ret, 0, iv.length); System.arraycopy(enc, 0, ret, iv.length, enc.length); return ret; } catch (Exception e) { throw new RuntimeException(e); } } /** * 復号化 */ public static byte[] decode4(byte[] src, Key skey) { try { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); final int BLOCK_SIZE = cipher.getBlockSize(); AlgorithmParameters iv = AlgorithmParameters.getInstance("AES"); byte[] ib = new byte[2 + BLOCK_SIZE]; ib[0] = 4; ib[1] = (byte) BLOCK_SIZE; System.arraycopy(src, 0, ib, 2, BLOCK_SIZE); iv.init(ib); cipher.init(Cipher.DECRYPT_MODE, skey, iv); return cipher.doFinal(src, BLOCK_SIZE, src.length - BLOCK_SIZE); } catch (Exception e) { throw new RuntimeException(e); } }
この指定で“暗号化するデータのサイズ”が16の倍数でない場合、実行時に以下のような例外が発生する。
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
使用できる暗号の種類はJava暗号化アーキテクチャー標準アルゴリズム名のドキュメン ト(JDK1.6)に載っている。[2008-08-15]
また、以下のようにして調べることも出来る。
Set names = Security.getAlgorithms("Cipher"); for (Iterator i = names.iterator(); i.hasNext();) { String name = (String) i.next(); System.out.println(name); }
また、JDK1.5以降では「int bit = Cipher.getMaxAllowedKeyLength(name);
」で使用可能な(最大の)鍵の長さを取得することが出来る。
AESの鍵の長さは128bit・192bit・256bitの3種類あるが、デフォルトでは128bitしか使えない。[2006-11-18]
これは、(AESの規格を定めた)アメリカの輸出規制か何かの制限によるものらしい。
使おうとすると、Cipher#init()の実行時に以下のような例外が発生する。
java.lang.SecurityException: Unsupported keysize or algorithm parameters
が、日本ではアメリカと何かの協定を結んでいるらしく、Javaのポリシーファイルを書き換えれば256bitも使える。
バージョン | ダウンロード元 | ファイル名 | 更新日 |
---|---|---|---|
JDK1.4.2 | ダウンロード Java2 SDKの下の方の「その他のダウンロード」 | jce_policy-1_4_2.zip | 2006-11-18 |
JDK1.5 | Java SE Downloadsの下の方の「その他のダウンロード」 | jce_policy-1_5_0.zip | 2006-11-18 |
JDK1.6 | Java SE Downloadsの下の方の「その他のダウンロード」 | jce_policy-6.zip | 2008-08-15 |
これで、256bitの鍵も使える。
はずだが、1.4.2では確かに使えたんだけど、1.5(Java5.0)では同じようにやっても何故か出来なかった。ダウンロードしたアーカイブの中に入っているポリシーファイルの更新日がけっこう古い(2004年)なのがちょっと気になるけど…関係ないかなぁ?
JDK1.6では問題なく使えた。[2008-08-15]