今回は、Blowfish+Base64を使用した暗号化とエンコード、デコードについて見ていきたいと思います。
暗号化とは
暗号化とは一定のルールに従って文字列を変えるなどして、データの内容を第三者にわからなくする技術または手法です。
この記事では「Blowfish」を使います。
Blowfishとは
暗号化と復号に同じ暗号鍵を用いる共通鍵暗号(秘密鍵暗号)で、鍵長は32ビットから448ビットまでの可変長となっています。平文を64ビット単位で暗号文に変換するブロック暗号です。
32ビット命令語プロセッサを基準で設計され、DESより早いです。
PKCS5とは
Padding方法の1つです。
データをブロックに暗号PKCS5はブロック暗号処理を任意のデータ長にかますための約束事のひとつです。
ブロック暗号は通常決められたブロックサイズに対する暗号処理のため、たとえばAESは16(128bit)、24(196bit)、32(256bit)のブロック暗号なので、このサイズのデータでなければ暗号化できないです。
そこで足りない分に詰め物(padding)をして、規定のサイズにした上で暗号化を行います。
PKCS7もありますが、これは機会があれば調べてみたいと思います。
暗号利用モード
暗号における利用モードでは、ECB, CBC, OFB, CFBの4種類が良く知られていますが、この記事ではCBCを使います。
CBCモードでは前のブロックと現在のブロックをXORすることにより、ブロックの暗号文が一つ前の暗号ブロックに依存するようになっていいます。つまり、ECBモードの欠点である「同じ平文ブロックが同じ暗号文となる」点を解消します。
サンプル
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
public class BlowfishBase64 {
// 暗号化の秘密鍵
private static final String KEY = "testkey";
// 初期化ベクトル
private static final String IV_PARAM_SPEC = "12345678";
// 使用する暗号化アルゴリズム
private static final String ALGORITHME = "BLOWFISH";
public static void main(String []args) throws Exception {
String text = "ABCDEFG"; // ターゲット文字列
String encryptedText = encryptAndEncoding(text);
String decryptedText = decodeAndDecrypt(encryptedText);
System.out.println("before : " + text);
System.out.println("encrypted: " + encryptedText);
System.out.println("decrypted: " + decryptedText);
}
public static String encryptAndEncoding(String text) throws Exception {
// 暗号化
SecretKeySpec sksSpec = new SecretKeySpec(KEY.getBytes("UTF-8"), ALGORITHME);
IvParameterSpec ivSpeck = new IvParameterSpec(IV_PARAM_SPEC.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("BLOWFISH/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sksSpec, ivSpeck);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
// Base64エンコード
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decodeAndDecrypt(String encryptedText) throws Exception {
// Base64エンコードされた文字列をデコード
byte[] encrypted = Base64.getDecoder().decode(encryptedText);
// 複合化
SecretKeySpec sksSpec = new SecretKeySpec(KEY.getBytes("UTF-8"), ALGORITHME);
IvParameterSpec ivSpeck = new IvParameterSpec(IV_PARAM_SPEC.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("BLOWFISH/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sksSpec, ivSpeck);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, "UTF-8");
}
}
- 初期ベクトル(IV)
- 同じデータを常に同じ暗号文に置き換えると、その頻度から暗号化される前の元のデータが推測されてしまいます。そのため、同じデータでも違う暗号文に置き換える仕組みが必要です。そこで用いられるのが初期化ベクトルです。初期化ベクトルはランダムに生成されるビット列であり、これを利用することで暗号化の結果を毎回変えられます。
- Javaの標準クラス仕様の場合は128ビットの文字列です。
- Cipher.getInstance(“BLOWFISH/CBC/PKCS5Padding”)
- アルゴリズムがBlowfish
- 暗号利用モードがCBC
- Padding方式がPKCS5であるインスタンスを取得します。
- エンコード、デコードはBase64を使用しました。
before : ABCDEFG
encrypted: Id5sP2xUwUQ=
decrypted: ABCDEFG
- 実行結果となります。
終わり
調べることがいっぱいありすぎて、全てを記載できなかったのですが、AES暗号化だろうがDES暗号化だろうがロジックはにているかと思います。暗号化についてこんなに調べたことがなく、間違っているところがあるかもしれないので、参考だけにしていただければと思います。
参考
https://qiita.com/tkxlab/items/8c802b018afa138640f1#ivinitial-vector
http://hanjoonkblog.blogspot.com/2016/12/aes-aescbcpkcs5padding.html
http://happinessoncode.com/2019/04/06/java-cipher-algorithm-mode-padding/