55

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);
  }
}

ユーザーが私の秘密鍵を解読すると、ゲームオーバーになります。


より詳細な質問/関連する質問:

  1. cipher.getIV() によって返された IV は、この方法で安全に使用できますか?
  • ガロア/カウンター モードで IV キーの組み合わせを再利用することによる大惨事を回避できますか?
  • 複数のアプリケーションがこのコードを同時に実行していて、すべてが同じ src データからユーザーに暗号化されたメッセージを (おそらく同じミリ秒で) 表示している場合でも安全ですか?
  • 返されたIVは何でできていますか? それはアトミックカウンターとランダムノイズですか?
  • cipher.getIV()自分のカウンターを使用して、自分で IVを回避して構築する必要がありますか?
  • 私がOracle JDK 8 + JCE無制限強度拡張機能を使用していると仮定すると、ソースコード実装はcipher.getIV()どこかでオンラインで入手できますか?
  1. その IV は常に 12 バイト長ですか?

  2. 認証タグは常に 16 バイト (128 ビット) の長さですか?

  3. #2 と #3、およびパディングがない場合、暗号化されたメッセージは常に12 + src.length + 16バイト長になるということですか? (そして、正しい長さを知っている1つのバイト配列に安全に押しつぶすことができますか?)

  4. ユーザーが知っている一定の src データが与えられた場合、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?

  5. src データが毎回異なる場合 (例:System.currentTimeMillis()または乱数を含む)、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?

  6. 暗号化する前に src データを乱数でパディングすると効果がありますか? ランダムな 8 バイトが前後にあるとか、片端だけだとか。または、それはまったく役に立たないか、暗号化を悪化させますか?

(これらの質問はすべて私自身のコードの同じブロックに関するものであり、それらは互いに強く関連しており、同じ機能を実装するときに他の人が同じ一連の質問をする可能性がある/する必要があるため、質問を複数に分割するのは間違っていると感じましたStackOverflow のフォーマットにより適している場合は、個別に再投稿できます。お知らせください!)

4

1 に答える 1

61

Q1: cipher.getIV() によって返される IV は、この方法で安全に使用できますか?

はい、少なくともオラクルが提供する実装用です。デフォルトのSecureRandom実装を使用して個別に生成されます。サイズが 12 バイトであるため (GCM のデフォルト)、96 ビットのランダム性があります。カウンターが繰り返される可能性は非常に小さいです。Oracle JDK が基づいている OpenJDK (GPL'ed) でソースを検索できます。

ただし、他のプロバイダーの動作が異なる可能性があるため、独自の 12 のランダム バイトを生成することをお勧めします。


Q2: その IV は常に 12 バイト長ですか?

これは GCM のデフォルトであるため可能性が非常に高いですが、GCM では他の長さ有効です。ただし、アルゴリズムは、12 バイト以外のサイズについて追加の計算を行う必要があります。弱点のため、12 バイト/96 ビットに保つことを強くお勧めします。APIによって、IV サイズの選択が制限される場合があります


Q3: 認証タグは常に 16 バイト (128 ビット) の長さですか?

いいえ、64 ビットから 8 ビット単位で 128 ビットまでの任意のサイズのバイトを使用できます。小さい場合は、認証タグの左端のバイトだけで構成されます。GCMParameterSpec呼び出しの 3 番目のパラメーターとして使用して、別のサイズのタグを指定できますinit

GCM の強度は、タグのサイズに大きく依存することに注意してください。128ビットに保つことをお勧めします。特に大量の暗号文を生成する場合は、最低でも 96 ビットにする必要があります。


Q4: #2 と #3、およびパディングがない場合、暗号化されたメッセージは常に 12 + src.length + 16 バイト長になるということですか? (そして、正しい長さを知っている1つのバイト配列に安全に押しつぶすことができますか?)

上記を参照。Oracle プロバイダーの場合、これが当てはまります。GCMParameterSpec確実に使用してください。


Q5: ユーザーが知っている一定の src データが与えられた場合、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?

はい、実質的に拘束されていません。約 2^48 の暗号化の後、私は心配し始めます。ただし、一般的には、キーの変更を考慮して設計する必要があります。


Q6: src データが毎回異なる場合 (例: System.currentTimeMillis() または乱数を含む)、無制限の数の src データ暗号化をユーザーに表示しても安全ですか?

Q5 & Q7 の回答を参照


Q7: 暗号化する前に src データを乱数でパディングすると役に立ちますか? ランダムな 8 バイトが前後にあるとか、片端だけだとか。または、それはまったく役に立たないか、暗号化を悪化させますか?

いいえ、まったく役に立ちません。GCM は下で CTR モードを使用するため、キー ストリームで暗号化されるだけです。IVとして機能しません。現在、暗号化するメッセージが常に変化している場合は、AES-GCM-SIV を見ることができますが、そのアルゴリズムはどの JCA プロバイダーにも実装されていないことに注意してください。


多くの暗号文 (2^48! または 2^32 - ~40 億 - 用心深い) が必要な場合は、その乱数と鍵をキー派生関数または KDF に使用することをお勧めします。HKDF は現在のところ最良の組み合わせですが、Bouncy Castle を使用するか、自分で実装する必要がある場合があります。

于 2015-08-06T18:27:49.070 に答える