CBCでJava 256ビットAES暗号化を実装する方法
-
10-07-2019 - |
質問
次のスレッドを読んで少し助けましたが、もう少し情報を探しています。
AESの記述方法/ CBC / PKCS5BlackBerry用初期化ベクトルパラメーターを使用した暗号化および復号化のパディング
基本的に、私がしていることは、TCP / IPを介して送信されるリクエストを暗号化し、サーバープログラムによって復号化されるプログラムを作成することです。暗号化はAESである必要があり、CBCとPKCS5Paddingを使用する必要があることがわかりました。したがって、基本的には秘密鍵とIVも必要です。
私が開発しているアプリケーションは電話用であるため、サイズを抑えるためにJavaセキュリティパッケージを使用したいと思います。設計は完了しましたが、IVと共有キーの実装は不明です。
ここにいくつかのコードがあります:
// My user name
byte[] loginId = "login".getBytes();
byte[] preSharedKey128 = "ACME-1234AC".getBytes();
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes();
// 256 bit key
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes();
byte[] preSharedKey = preSharedKey256;
// Initialization Vector
// Required for CBC
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ips = new IvParameterSpec(iv);
byte[] encodedKey = new byte[loginId.length + preSharedKey.length];
System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);
// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.
// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");
// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher;
encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips);
// Our cleartext
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330";
byte[] cleartext = clearString.getBytes();
// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);
// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);
// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);
簡単に言えば、サーバーが電話からキーまたはIVを取得する必要なく、サーバーで解読できるメッセージを暗号化する必要があります。電話でIVとキーを保護し、サーバーがキーとIVを認識している場合、これを行う方法はありますか?そうでない場合は、お気軽に教えてください。
解決
コードにはいくつかの問題があります。まず、キージェネレーターを使用して秘密キーを生成する必要があります。一部のテキストを直接使用すると、一部のアルゴリズムで機能する場合もありますが、テストする必要がある脆弱なキーなどがある場合もあります。
パスワードベースの暗号化を行いたい場合でも、すでに引用した質問に対する私の回答。
また、 String
のno-arg getBytes()
メソッドを使用しないでください。これはプラットフォームに依存します。エンコードするすべての文字列にUS-ASCII文字セットの文字のみが含まれる場合は、そのエンコードを明示的に指定して明確にします。そうでなければ、電話とサーバーのプラットフォームが異なる文字エンコーディングを使用している場合、キーとIVは同じになりません。
CBCモードでは、送信するメッセージごとに新しいIVを使用するのが最善です。通常、CBC IVはランダムに生成されます。 CFBやOFBなどの他のモードは、すべてのメッセージに固有のIVを必要にします。通常、IVは暗号文に沿って送信されます。IVは秘密にする必要はありませんが、予測可能なIVが使用されると、多くのアルゴリズムが壊れます。
サーバーは電話からシークレットまたはIVを直接取得する必要はありません。秘密鍵(または秘密鍵の派生元のパスワード)を使用してサーバーを 構成できますが、多くのアプリケーションでは、これは悪い設計になります。
たとえば、アプリケーションを複数の人の電話に展開する場合、彼らが同じ秘密鍵を使用するのは良い考えではありません。 1人の悪意のあるユーザーがキーを回復し、すべてのユーザーのシステムを破壊できます。
より良い方法は、電話で新しい秘密鍵を生成し、鍵合意アルゴリズムを使用して鍵をサーバーと交換することです。これにはDiffie-Hellman鍵合意を使用できます。または、秘密鍵をRSAで暗号化してサーバーに送信できます。
更新:
「一時的な静的」のディフィー・ヘルマンサーバーの公開キーがアプリケーションに埋め込まれている限り、サーバーから電話への初期メッセージがなくても、モード(および「静的-静的」モードも可能ですが、それは望ましくありません)。
サーバーの公開鍵は、電話に共通の秘密鍵を埋め込むことと同じリスクをもたらしません。これは公開キーであるため、攻撃者は電話を手に入れる(またはリモートでハッキングする)ことで、実際の公開キーをサーバーになりすます偽のキーに置き換えることができます。
静的モードを使用することもできますが、実際にはより複雑で、少し安全性が低くなります。すべての電話機には固有のキーペアが必要です。そうでない場合、秘密キーの問題に陥ります。少なくとも、サーバーがどの電話にどのキーを持っているかを追跡する必要はありません(アプリケーションレベルでパスワードなどの認証メカニズムがある場合)。
携帯電話の速さがわかりません。私のデスクトップでは、一時的なキーペアの生成に約1/3秒かかります。 Diffie-Hellmanパラメーターの生成は非常に遅いです。サーバーキーのパラメーターを間違いなく再利用する必要があります。
他のヒント
前にミッドレットで同様のプロジェクトを完了しました。次のアドバイスがあります:
- 電話に共有秘密を保存する安全な方法はありません。使用できますが、これはセキュリティによるセキュリティというカテゴリに分類されます。 「マットの下のキー」のようなものです。セキュリティの種類。
- 広く利用されていない256ビットAESを使用しないでください。別のJCEをインストールする必要がある場合があります。 128ビットAESまたはTripleDESは、依然として安全であると見なされています。 #1を考慮すると、これについて心配する必要はありません。
- パスワード(ユーザーごとに異なる)を使用した暗号化は、はるかに安全です。ただし、この例で示しているように、パスワードをキーとして使用しないでください。 PBEKeySpec(パスワードベースの暗号化)を使用してキーを生成してください。
- MITM(man-in-the-middle)攻撃が心配な場合は、SSLを使用してください。