web-dev-qa-db-ja.com

秘密鍵、ソルト、初期化ベクトルをデータベースに保存するためのベストプラクティスは何ですか?

Javaドキュメントに署名するためのデジタル署名を作成するアプリケーションに取り組んでいます。(Elliptic-curve cryptography)ECCを使用して秘密鍵と公開鍵を既に作成しており、両方の鍵を保存したいmysqlデータベース。

公開鍵については問題ありませんが、秘密鍵を安全に保管することを懸念しています。

AES 128ビットを使用して、ユーザーからの秘密キーと6桁のパスワードを暗号化してデータベースに格納しています。暗号化するには、saltを生成し、ベクトルも初期化する必要があります。

しかし今、それを解読するには、暗号化された秘密鍵の解読に使用するものと同じソルトと初期化ベクトル(iv)も知っている必要があります。

質問1:

ソルトとベクトルを秘密鍵を使用してデータベースの同じテーブルに保存することは安全ですか?現在、私は公開鍵と秘密鍵、salt、ivをエンコードし、データベースに文字列(データ型VARCHAR)として保存しています。

質問2:

以下は私が設計している現在のワークフローです。これはベストプラクティスですか? CPU使用率に影響を与えるプロセスを作りすぎないようにします。

キーを作成しています

  • ECCを使用して秘密鍵を作成する
  • 秘密鍵のエンコード(バイトから文字列)
  • AESを使用して秘密鍵を暗号化する
  • 秘密鍵のエンコード、salt、iv(バイトからストリング)
  • VARHCARとしてデータベースに保存

署名文書

  • DBから秘密鍵を取得する
  • 秘密鍵、salt、ivのデコード
  • 秘密鍵を復号化する
  • 秘密鍵をデコードする
  • 署名されたキーを実際の秘密キーに変換して署名する

キーと暗号化を生成するコードスニペット:

        //Generate salt
        Random r = new SecureRandom();
        byte[] salt = new byte[8];
        r.nextBytes(salt);
        //System.out.println("salt: "+salt);

        //initialize vector
        byte[] vector = new byte[128/8];
        r.nextBytes(vector);
        IvParameterSpec ivspec = new IvParameterSpec(vector);
        //System.out.println("iv: "+iv);

        //initialize variables
        String MsgToEncrypt = encodedECCprivateKeyBytes;
        String userPin = ParamUtil.getString(actionRequest, "userPin");
        Cipher ecipher;

        //Generating AES key
        SecretKeyFactory factory =  SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec mykeySpec = new PBEKeySpec(userPin.toCharArray(), salt, 10000, 128);
        SecretKey tmp = factory.generateSecret(mykeySpec);
        SecretKeySpec mySecretkey = new SecretKeySpec(tmp.getEncoded(), "AES");

        //==> Create and initiate encryption
        System.out.println("Initiate encryption alogrithm...");
        ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //System.out.println("Algorithm to encrypt private key: " + ecipher);
        ecipher.init(Cipher.ENCRYPT_MODE, mySecretkey, ivspec);
        //System.out.println("SecKey: "+mySecretkey);

質問は2つに分けて聞いたので、2つに分けて答えます。

質問1:ソルトとベクターをプライベートキーを使用してデータベースの同じテーブルに保存しても安全ですか?現在、私は公開鍵と秘密鍵、salt、ivをエンコードし、データベースに文字列(データ型VARCHAR)として保存しています。

短い答え:はい、安全です。

説明:ハッシュされたパスワードと一緒にソルトをプレーンテキストで保持することが安全であると一般に認められています。これは、攻撃者がソルトとハッシュを持っている場合でも、ブルートフォース攻撃を利用してハッシュの作成に使用されたパスワードを推測する必要があるというロジックに基づいています。ソルトを秘密にしておくとセキュリティが向上しますが、パスワードが適度に強力である限り、必要ありません。 IVの場合、これははるかに複雑であり、私の意見ではここに入るのは(そして説明するのが私の快適ゾーンからは)範囲外ですが、IVが暗号の横にある平文で保存されることは安全であると考えられています。テキスト。

質問2:以下は、私が設計している現在のワークフローです。これはベストプラクティスですか? CPU使用率に影響を与えるプロセスを作りすぎたくありません。

ワークフローは私には良さそうで、私が行ったのとほぼ同じですが、ベストプラクティスを求めているので、改善できると思う詳細がいくつかあります。

  1. まず、あなたの塩は少し短すぎます。これの問題は、暗号ランダムジェネレーターを使用して生成された場合でも、8バイトのソルトを使用すると(これは良いことです)、2人以上の異なるユーザーに同じソルト値が割り当てられる衝突の可能性があることです。これを軽減するために、私が推奨するのは、ソルトの長さが256ビットまたは32バイトであるということです。

  2. 2番目のアドバイスです。すでに気付いているかどうかはコードに基づいてわかりませんが、同じAESキーで同じIVを2回再使用しないでください。非常に豪華な暗号解読のため、これは暗号化に脆弱性を作成し、それを破ることができます。キーはユーザーパスワードから生成されているため、変更されることはめったにありません。秘密キーを再暗号化するたびにIVをローテーションしてください。

  3. 現時点では、ハッシュを1万回しか実行していないようです。これは恐ろしいことではありませんが、私が常に従った黄金律は2 ^(year-2000)反復です。それは2018年なので、250 000回のハッシュ反復、または深刻なパフォーマンスの問題に到達する前に取得できるこの値に近い値がベストプラクティスです。

そうでなければ、私はすべてが良さそうだと思います、あなたはハッシュと公開鍵と秘密鍵のアルゴリズムで良い選択をし、あなたが何かを見落とさないように助けを求めることは賢明でした。

それが役に立てば幸い!

8
dFrancisco

うん。暗号化されたエントリの横にソルトとIVを保存しても安全です。

理由を理解するには、SaltとIVの目的を詳しく調べる必要があります。

Saltは、oneエントリがクラックされた場合に、他のエントリに滴り落ちないようにするための手段です。 were n'tハッシュ化された暗号化パスワードのテーブルがある場合、誰かが1つのレコードをクラックするとすぐに、同じ復号化されたパスワードを共有するすべてのレコードも同時にクラックされます。ソルトはブルートフォースを難しくすることを意図していません。これは、各エントリが暗号化に関して他のすべてのエントリから分離されていることを保証することのみを目的としています。したがって、攻撃者が特定のレコードのソルトエントリを知っているかどうかは重要ではありません。エントリごとに異なるソルトを使用している限り、ソルトがその役割を果たします。

同様に、IVは既知の平文攻撃を阻止するためのものです。これは、誰かが「これらのXエントリはすべて、バイトX-Yで同様のコンテンツを共有する」ことを悪用し、暗号分析を使用してキーの側面を推定することを防ぐためのものです。 IVは基本的に、データを暗号化する前にごちゃまぜにします。したがって、IVを知っていても攻撃者を救うことはできません。

したがって、暗号化された値とともにIVとソルトを保存します。

4
Kevin