Javaの暗号化

JCE、またはJDK1.4の暗号化ライブラリを使った暗号化のコード。

Crypt

暗号化ユーティリティクラス。


package hoge;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.*;

import javax.crypto.*;
import javax.crypto.spec.*;

public class Crypt {

/**
* DESの秘密鍵を生成する。
*/
public static SecretKey createDESSecretKey()
throws
InvalidKeyException,
NoSuchAlgorithmException,
InvalidKeySpecException {
// 任意のバイト配列を生成。
KeyGenerator kgen = KeyGenerator.getInstance("DESede");
byte raw = kgen.generateKey().getEncoded();

// DESの秘密鍵を生成。
// バイト配列の、最初の8バイトが鍵データとして使用される。
DESKeySpec keySpec = new DESKeySpec(raw);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);

// 秘密鍵を返す。
log("[key=" + HexUtil.asHex(key.getEncoded()) + "]");
return key;
}

/**
* トリプルDESの秘密鍵を生成する。
*/
public static SecretKey createDES3SecretKey()
throws
InvalidKeyException,
NoSuchAlgorithmException,
InvalidKeySpecException {
// 任意のバイト配列を生成。
KeyGenerator kgen = KeyGenerator.getInstance("DESede");
byte raw = kgen.generateKey().getEncoded();

// トリプルDESの秘密鍵を生成。
// バイト配列の、最初の24バイトが鍵データとして使用される。
DESedeKeySpec keySpec = new DESedeKeySpec(raw);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyFactory.generateSecret(keySpec);

// 秘密鍵を返す。
log("[key=" + HexUtil.asHex(key.getEncoded()) + "]");

// 秘密鍵を返す。
return key;
}

/**
* Blowfishの秘密鍵を生成する。
*/
public static SecretKey createBlowfishSecretKey(int size)
throws
InvalidKeyException,
NoSuchAlgorithmException,
InvalidKeySpecException {
// Blowfishの秘密鍵を生成。
KeyGenerator kgen = KeyGenerator.getInstance("Blowfish");
kgen.init(size);
SecretKey key = kgen.generateKey();

// 秘密鍵を返す。
log("[key=" + HexUtil.asHex(key.getEncoded()) + "]");

// 秘密鍵を返す。
return key;
}

/**
* 文字列を暗号化する。
*/
public static String encrypt(String src, Key key, String alg)
throws
UnsupportedEncodingException,
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
IllegalStateException,
IllegalBlockSizeException,
BadPaddingException {
// 文字列からバイト配列を取得。
byte bytesrc = src.getBytes("UTF-8");

// 暗号化オブジェクトを生成し、暗号化モードで初期化。
Cipher cipher = Cipher.getInstance(alg);
cipher.init(Cipher.ENCRYPT_MODE, key);

// 文字列を暗号化。
byte bytedst = cipher.doFinal(bytesrc);

// バイト配列を16進数文字列に変換して返す。
return HexUtil.asHex(bytedst);
}

/**
* 16進数文字列を復号化する。
*/
public static String decrypt(String hex, Key key, String alg)
throws
UnsupportedEncodingException,
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
IllegalStateException,
IllegalBlockSizeException,
BadPaddingException {
// 16進数文字列からバイト配列を取得。
byte bytehex = HexUtil.asByteArray(hex);

// 暗号化オブジェクトを生成し、復号化モードで初期化。
Cipher cipher = Cipher.getInstance(alg);
cipher.init(Cipher.DECRYPT_MODE, key);

// 文字列を復号化。
byte bytedst = cipher.doFinal(bytehex);

// バイト配列を文字列に変換して返す。
return new String(bytedst, "UTF-8");
}

/**
* 入力ストリームを暗号化する。
*/
public static void encrypt(
InputStream in,
OutputStream out,
Key key,
String alg)
throws
InvalidKeyException,
NoSuchAlgorithmException,
NoSuchPaddingException,
IllegalStateException,
IllegalBlockSizeException,
BadPaddingException,
IOException {
crypt(Cipher.ENCRYPT_MODE, in, out, key, alg);
}

/**
* 入力ストリームを復号化する。
*/
public static void decrypt(
InputStream in,
OutputStream out,
Key key,
String alg)
throws
InvalidKeyException,
NoSuchAlgorithmException,
NoSuchPaddingException,
IllegalStateException,
IllegalBlockSizeException,
BadPaddingException,
IOException {
crypt(Cipher.DECRYPT_MODE, in, out, key, alg);
}

/**
* 入力ストリームを暗号化/復号化する。
*/
private static void crypt(
int mode,
InputStream in,
OutputStream out,
Key key,
String alg)
throws
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
IOException,
IllegalStateException,
IllegalBlockSizeException,
BadPaddingException {
// 暗号化オブジェクトを生成し、初期化。
Cipher cipher = Cipher.getInstance(alg);
cipher.init(mode, key);

// 256バイトのバッファを生成。
byte buffer = new byte[256];

// 入力ストリームを暗号化/復号化。
int len = 0;

while ( (len = in.read(buffer) ) != -1) {
out.write(cipher.update(buffer, 0, len));
}

byte dst = cipher.doFinal();

if (dst != null) {
out.write(dst);
}

// 出力ストリームをフラッシュ。
out.flush();
}

private static void log(Object mes) {
System.out.println(mes);
}

}

HexUtil

暗号化クラスの内部で使っている16進数文字列⇔バイト配列変換ユーティリティクラス。
たいそうなコードではないので、暗号化ユーティリティクラスに実装してもよかったかも。


package hoge;

public class HexUtil {

/**
* バイト配列を16進数の文字列に変換する。
*
* @param bytes バイト配列
* @return 16進数の文字列
*/
public static String asHex(byte bytes) {
// バイト配列の2倍の長さの文字列バッファを生成。
StringBuffer strbuf = new StringBuffer(bytes.length * 2);

// バイト配列の要素数分、処理を繰り返す。
for (int index = 0; index < bytes.length; index++) {
// バイト値を自然数に変換。
int bt = bytes[index] & 0xff;

// バイト値が0x10以下か判定。
if (bt < 0x10) {
// 0x10以下の場合、文字列バッファに0を追加。
strbuf.append("0");
}

// バイト値を16進数の文字列に変換して、文字列バッファに追加。
strbuf.append(Long.toString(bt, 16));
}

/// 16進数の文字列を返す。
return strbuf.toString();
}

/**
* 16進数の文字列をバイト配列に変換する。
*
* @param hex 16進数の文字列
* @return バイト配列
*/
public static byte asByteArray(String hex) {
// 文字列長の1/2の長さのバイト配列を生成。
byte[] bytes = new byte[hex.length() / 2];

// バイト配列の要素数分、処理を繰り返す。
for (int index = 0; index < bytes.length; index++) {
// 16進数文字列をバイトに変換して配列に格納。
bytes[index] =
(byte) Integer.parseInt(
hex.substring(index * 2, (index + 1) * 2),
16);
}

// バイト配列を返す。
return bytes;
}

}

注意点とか

  • Javaは逆コンパイルしやすいので、秘密鍵を内部に保持したバイナリを提供する場合は以下のようにしたほうがよいかも。
    • 暗号化部分だけCで実装。
    • 曖昧化ツールを使う。
  • アルゴリズムはAESがよいが、JDK1.4.2以上じゃないと使えない。