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]