Java 8 で暗号化を使用しAES/GCM/NoPadding
ていますが、コードにセキュリティ上の欠陥があるかどうか疑問に思っています。テキストを暗号化および復号化するという点で、私のコードは機能しているようですが、いくつかの詳細は不明です。
私の主な質問はこれです:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // ?????
その IV は、「特定のキーについて、IV を繰り返してはならない」という要件を満たしていますか。RFC 4106から?
また、関連する質問 (以下を参照) に対する回答や洞察をいただければ幸いですが、その最初の質問が私を最も悩ませています。これに答えるソースコードやドキュメントがどこにあるかわかりません。
おおよその完全なコードは次のとおりです。この投稿を書いているときにエラーが発生した場合はお詫び申し上げます。
class Encryptor {
Key key;
Encryptor(byte[] key) {
if (key.length != 32) throw new IllegalArgumentException();
this.key = new SecretKeySpec(key, "AES");
}
// the output is sent to users
byte[] encrypt(byte[] src) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // See question #1
assert iv.length == 12; // See question #2
byte[] cipherText = cipher.doFinal(src);
assert cipherText.length == src.length + 16; // See question #3
byte[] message = new byte[12 + src.length + 16]; // See question #4
System.arraycopy(iv, 0, message, 0, 12);
System.arraycopy(cipherText, 0, message, 12, cipherText.length);
return message;
}
// the input comes from users
byte[] decrypt(byte[] message) throws Exception {
if (message.length < 12 + 16) throw new IllegalArgumentException();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, key, params);
return cipher.doFinal(message, 12, message.length - 12);
}
}
ユーザーが私の秘密鍵を解読すると、ゲームオーバーになります。
より詳細な質問/関連する質問:
- cipher.getIV() によって返された IV は、この方法で安全に使用できますか?
- ガロア/カウンター モードで IV キーの組み合わせを再利用することによる大惨事を回避できますか?
- 複数のアプリケーションがこのコードを同時に実行していて、すべてが同じ src データからユーザーに暗号化されたメッセージを (おそらく同じミリ秒で) 表示している場合でも安全ですか?
- 返されたIVは何でできていますか? それはアトミックカウンターとランダムノイズですか?
cipher.getIV()
自分のカウンターを使用して、自分で IVを回避して構築する必要がありますか?- 私がOracle JDK 8 + JCE無制限強度拡張機能を使用していると仮定すると、ソースコード実装は
cipher.getIV()
どこかでオンラインで入手できますか?
その IV は常に 12 バイト長ですか?
認証タグは常に 16 バイト (128 ビット) の長さですか?
#2 と #3、およびパディングがない場合、暗号化されたメッセージは常に
12 + src.length + 16
バイト長になるということですか? (そして、正しい長さを知っている1つのバイト配列に安全に押しつぶすことができますか?)ユーザーが知っている一定の src データが与えられた場合、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?
src データが毎回異なる場合 (例:
System.currentTimeMillis()
または乱数を含む)、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?暗号化する前に src データを乱数でパディングすると効果がありますか? ランダムな 8 バイトが前後にあるとか、片端だけだとか。または、それはまったく役に立たないか、暗号化を悪化させますか?
(これらの質問はすべて私自身のコードの同じブロックに関するものであり、それらは互いに強く関連しており、同じ機能を実装するときに他の人が同じ一連の質問をする可能性がある/する必要があるため、質問を複数に分割するのは間違っていると感じましたStackOverflow のフォーマットにより適している場合は、個別に再投稿できます。お知らせください!)