標準的な暗号しか使わないケースでもJavaでRSAを使う時はBouncyCastleを入れておいた方が無難、という話です。
“ECB”という文字列がプログラム中にあると監査に引っかかって時間を割かなければいけなくなるかも知れません。
CRYPTREC暗号リストとは
電子政府推奨暗号リスト。総務省と経済産業省が取り決めている「使ってもいい暗号方式」の一覧表です。
上記サイト冒頭の「電子政府における調達のために参照すべき暗号のリスト(CRYPTREC暗号リスト)」リンクで開くPDFに、政府が使用を推奨している暗号化方式リストが掲載されています。
この中のどれかで暗号化されていれば概ねセキュリティが担保されると監査側は認識していることが多いと思います。
逆にこのリストに載っていない方式がプログラム内に出てくると、納品時にセキュリティ監査による静的コードチェックに引っかかるかも知れません。
JavaコアAPIのみでRSA暗号、復号するコードの問題点
RSA暗号されたsourceを復号するコードです。6行目の
Cipher.getInstance(“RSA/ECB/PKCS1PADDING”);
が受け入れの際の単純チェックに引っかかる可能性が有ります。
public byte[] decrypt(byte[] source) throws Exception {
byte[] keyData = readKeyFile(PRIVATE_KEY_FILE);
KeySpec keyspec = new PKCS8EncodedKeySpec(keyData);
KeyFactory keyfactory = KeyFactory.getInstance("RSA");
Key privateKey = keyfactory.generatePrivate(keyspec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(source);
}
暗号処理する為のCipherインスタンスを作る為に”アルゴリズム/暗号化モード/パディング”を指定します。この際JavaコアAPIのみだとRSAを指定した場合のモードは「ECB」しか指定出来ません。それ以外を指定すると例外が発生します。
java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/None/PKCS1PADDING
at javax.crypto.Cipher.getInstance(Cipher.java:539)
ECBはCRYPTREC暗号リストには乗っておらず、初期化ベクトル仕様が無く毎回暗号化結果が同じになる為、世の中的にも非推奨です。
ただこのブロックモード指定が使われるのはAESやDESのような共通鍵暗号の場合で、RSAのような鍵ペアを使った公開鍵暗号方式では使用されません。
RSAではパディングの結果毎回暗号化結果が異なるので本来ECBやCBCのようなブロックモードは意識しなくても良いのですが、開発者側はJavaでRSAする際に取りあえずECBを指定しておくしかない現状があり、監査する側もリストからその意図が読み取れず、非推奨のブロックモードを指定していると判断してしまい、余計な誤解を招いてしまう可能性があります。
対処
“RSA/ECB/PKCS1PADDING”以外の指定が出来るように暗号化ライブラリ「BouncyCastle」を追加します。以下Mavenのpom.xml。
<dependencies>
<!-- 追加 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.62</version>
</dependency>
</dependencies>
JVM内の暗号化プロバイダに、先ほど追加したBouncyCastleProvicderを認識させることで”RSA/None/PKCS1PADDING”を指定出来るようにします。
// 追加
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
// アプリ初期化時にでも追加
Security.addProvider(new BouncyCastleProvider());
public byte[] decrypt(byte[] source) throws Exception {
byte[] keyData = readKeyFile(PRIVATE_KEY_FILE);
KeySpec keyspec = new PKCS8EncodedKeySpec(keyData);
KeyFactory keyfactory = KeyFactory.getInstance("RSA");
Key privateKey = keyfactory.generatePrivate(keyspec);
// BouncyCastleProviderのRSA/None/PKCS1PADDINGを使用する
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1PADDING", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(source);
}
“ECB”という文字列はプログラム中から無くなりました。
鬼の首を取ったようにセキュリティ監査部門から糾弾され、弁明する時間を無駄にすることはもうありません。火の無いところに煙は立てたくないところです。
JavaでRSAを使う時はBouncyCastleは必須ですね。