6

Java で安全なファイル共有アプリケーションを作成しています。一般的なアーキテクチャは次のようになります。

  1. ユーザーは、複数のユーザー間で安全に共有するためにファイルを暗号化したいと考えています。
  2. アプリケーションはクライアントでランダムな UUID を生成し、これを AES 256 パスワードとして使用し、UUID でデータを暗号化します。
  3. UUID は、各人の公開鍵で RSA 暗号化されます。共有ユーザーごとに 1 回。
  4. 暗号化された各 UUID パケットは、ファイルの一部としてカスタム ファイル ヘッダーに格納されます。
  5. ファイルは、他のユーザーがアクセスできるサーバーにアップロードされます。
  6. ユーザーはそれぞれ自分の秘密鍵を使用して AES 暗号化鍵を読み取り、ファイルを復号化できます。

これがキャッチです。複数の場所からファイルにアクセスできるように、ユーザーの秘密鍵は暗号化してサーバーのデータベースに保存する必要があります。秘密鍵は、サーバーにアップロードされる前に、クライアントでユーザーが選択したパスワードで暗号化されます。

AES 256 ビット暗号化を使用してこれを行いたいと思います。そして、BouncyCastle ライブラリやサード パーティのライブラリに依存せずにすべてを実行したいと考えています。標準の Java 5 ライブラリを使用する必要があるため、PGP などではなく、AES 256 暗号化と RSA を使用することにしました。

このアプローチで本質的に安全でないものを誰かが見つけたり、これを行うためのより効率的な方法を考えたりできますか?

編集:

OK、私が得たすべての回答は、秘密鍵をサーバーに送信しないことを示唆しているため、質問を更新しています。サーバーに秘密鍵が必要な理由は、ユーザーが複数のクライアントと複数の場所 (つまり、iPhone、iPad、職場のラップトップ、自宅の PC) からデータにアクセスできる必要があるためです。デバイスからデバイスへとキーを管理およびコピーする必要はありません。これは、キーをサーバーに保存するよりもさらに安全ではありません。

4

4 に答える 4

5

これに関する大きな問題は、UUID の使用です。UUID は (ある程度) 一意であることが保証されていますが、含まれている内容のかなりの部分は予測可能です。1 台のマシンで生成されたすべての UUID でかなりの量が一定のままです。そのため、ある人が (たとえば) 自分のキーにアクセスできる場合、おそらく他の多くの人のキーをかなり簡単に推測できます。

問題のある他の部分は、ユーザーの秘密鍵をサーバーに保存することです。これらのキーへのアクセスは、残りのすべてのデータへのアクセスを明らかに与えるため、これにより、スキームの残りの部分全体が比較的壊れやすくなります。また、(明らかに)通常はサーバー上のデータを復号化することを意味するため、ユーザーがネットワーク経由でそのデータにアクセスする場合、送信のために再暗号化する必要があり、ユーザーのマシンで復号化する必要があります。そうしないと、データを平文で送信することになります (したがって、ほとんどの暗号化が役に立たなくなります)。

編集:私がこれを行うと思う方法について:

サーバー上に公開鍵のリストがあります。クライアントが他のクライアントとファイルを共有する場合、クライアントはそれらのクライアントの公開鍵をサーバーから取得します。次に、安全なランダム キーを生成し、そのキーでデータを暗号化します。次に、データにアクセスできると想定される他のすべてのクライアントの公開鍵を使用して、ランダム キーを暗号化します。それらをストリームにまとめて、サーバーに送信します。その後、他のクライアントはストリームをダウンロードし、秘密鍵で鍵を復号化し、それを使用してデータ自体を復号化できます。

これは、各クライアントの秘密鍵が真に秘密のままであることを意味します。クライアントのマシンからいかなる形でも離れる必要はありません。世界中の人々と共有する必要があるのは、公開鍵だけです (定義上、公開鍵はセキュリティ上の問題を引き起こすべきではありません)。

これにより、攻撃の 2 つの明白なラインは、乱数ジェネレーターに対するものと、RSA 自体に対するものになります。乱数ジェネレーターには、Java の SecureRandom を使用します。これはまさに意図された目的の種類であり、メモリが機能する場合はかなり慎重に調査されており、それに対する重大なブレークはかなりありそうにありません。

RSA 自体のセキュリティについてはコメントしません。今のところ、あなたの主な関心事は、適切な暗号化アルゴリズムではなく、プロトコルにあると思います。RSA が大幅に壊れた場合、コードを変更する必要があることは明らかですが、多くの会社が存在することになります。

これにより、秘密鍵を安全に保管するかどうかはクライアント次第です。私はその仕事のためにスマートカードが好きですが、かなりの数の代替品があります. ただし、サーバーとプロトコルの観点からは、それはもはやまったく問題ではありません。

編集 2: 複数のデバイスを扱う場合は、各デバイスを個別のユーザーとして扱い、独自の公開鍵と秘密鍵のペアを使用すると思います。次に、(おそらく)実際のユーザーごとにそれらをグループ化するので、「Joe Blow」を選択して彼のすべてのデバイスへのアクセスを簡単に許可できますが、階層表示を使用すると、サブセットへのアクセスを非常に簡単に制限することもできますそのため、オフィスのマシンでジョーと共有したいが、機密性が高いため、誰かが肩越しに見ているときに共有したくない場合でも、非常に簡単に共有できます。

これにより、ユーザーの生活はシンプルになりますが、同じ基本的なセキュリティ モデルが保持されます (つまり、秘密鍵は非公開のままです)。

于 2012-07-17T17:15:35.390 に答える
2

あなたが概説するスキームは、CMS(標準の基礎となるS/MIME)およびPGPと同等です。基本的に安全です。CMS では、このモードを「キー トランスポート」と呼びます。DH や ECDH などのアルゴリズムを使用して、マルチパーティの「キー アグリーメント」を使用することもできます。

唯一の問題は、AES 用に適切に選択されていないキーを使用していることです。

非ランダム ビットを含むランダム UUID を使用する理由が思いつきません。Java 暗号化アーキテクチャの通常の鍵生成メカニズムを使用するだけです。テキストのみを収容する外部ストレージまたはトランスポートに対応する必要がない限り、キー、平文、および暗号文はすべてバイト シーケンスとして表す必要があります。

Iterable<Certificate> recipients = null;
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(256);
SecretKey contentEncryptionKey = gen.generateKey();

AES 暗号を初期化し、プロバイダーに IV を選択させます。

Cipher contentCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
contentCipher.init(Cipher.ENCRYPT_MODE, contentEncryptionKey);
AlgorithmParameters params = contentCipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

受信者ごとに、RSA 暗号を初期化し、AES キーを暗号化します。

Cipher keyEncryptionCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
for (Certificate recipient : recipients) {
  keyEncryptionCipher.init(Cipher.WRAP_MODE, recipient);
  byte[] encryptedKey = keyEncryptionCipher.wrap(contentEncryptionKey);
  /* Store the encryptedKey with an identifier for the recipient... */
}
/* Store the IV... */ 
/* Encrypt the file... */

ユーザーに 256 ビットの実効強度を与えるパスワードを選択して記憶させることは、不合理です。その強度を得るには、パスワードをランダムに選択し、テキストとしてエンコードし、ユーザーにカードに書き留めてもらう必要があります。それだけの強度が本当に必要な場合は、ユーザーの RSA キーを格納するためのスマート カード ベースのソリューションを検討できます。

CMS ライブラリを使用してファイルを保存することを強くお勧めします。使用しているプロトコルが安全であり、使用しているコードがより多くのレビューを受けており、他のツール、ライブラリ、およびシステムが暗号化されたメッセージと相互運用できる可能性が高くなります。BouncyCastle の API は少しわかりにくいですが、学習する価値はあります。

(Java 5 が「RSA/ECB/OAEPWithSHA-512AndMGF1Padding」をサポートしているかどうかは思い出せません。サポートしている場合は、PKCS1Padding の代わりにそれを使用する必要があります。)

于 2012-07-17T18:18:14.580 に答える
1

OK、この質問はプロトコルの議論を求めているので、stackoverflowの標準に完全には準拠していません。そうは言っても、とにかくいくつかの発言ができるかどうか見てみましょう:):

  1. Bouncy Castle PGPライブラリには非常に寛容なライセンスがあるため、コード内のサブパッケージにコピーすることもできます。
  2. PGPの他に、CMSやXML暗号化などの他の標準コンテナ形式もありますが、後者はそれほど優れた汎用形式ではない可能性があります。
  3. UUIDの代わりに、JavaJCE「SHA1PRNG」などの適切にシードされたPRNGを使用してAESキーを作成することを強くお勧めします。スキームでUUIDのようなものに依存する必要があるという強い理由はわかりません。
  4. AESキーは、十分なエントロピーを持つためにランダムビットで構成されていると想定されており、「パスワード」がトラップにつながると考えています。文字列を安全なAESキーとして使用することはできません。
  5. ユーザーはアプリケーションとサーバーを信頼する必要あります。信頼できるサードパーティとして機能します。ユーザーのパスワードをサーバーに送信したり、誤った公開鍵をユーザーに送信したりする可能性があります。
  6. あなたのスキームは中間者攻撃から保護されていません(そして多くの人がSSLを使用せずにこれを行うことはできないと主張しています)
  7. パスワードで直接暗号化する代わりに、パスワードベースの暗号化PBKDF2のようなものを調べて、RSA秘密鍵を暗号化する必要があります。
  8. 暗号化するときに整合性保護を追加してみてください。Java7以降では、GCMモードでAESを使用できます。それだけの価値があります。
于 2012-07-17T18:29:11.987 に答える
-1

それはすべて、暗号化をどの程度「安全」にするかによって異なります。明らかに、RSA は十分に文書化され、受け入れられている PKI の標準です。そうは言っても、平文と暗号化されたテキストを提供するときはいつでも、ハッカーが平文の一部を知っている暗号文を解読するのが非常に簡単になります. ここで、あなたはまさにそれをやっています。暗号化された UUID のみを送信していますが、同じ平文を複数のキーで暗号化することにより、攻撃者はペイロードに関する重要な洞察を得ることができます。さらに、ハッキングされた人が実際に受信者の 1 人である場合、UUID をデコードできるため、他のユーザーの公開鍵によって暗号化されている平文を自動的に知ることができます。

これはおそらく重大な問題ではありませんが、セキュリティ リスクを指摘したいと思います。

ただし、ユーザーの秘密鍵を保存する必要がある理由は完全にはわかりません。さらに、単純なパスワードを使用して秘密鍵を暗号化することにより、基本的にシステム全体の全体的なセキュリティをユーザーのパスワードの強度まで低下させました。最後に、ユーザーがパスワードを紛失した場合、彼は乾杯します。データを回復する方法はありません。

過去に似たようなことをしましたが、結果をDBに保存しました。ただし、当時は BouncyCastle ライブラリを使用していたため、これを使用せずにこれを行う方法がわかりません。

于 2012-07-17T17:19:58.210 に答える