私はさまざまな暗号化技術の学習に興味があり、特に1つの実装を理解しようとしています。アプリケーションにパスワードを設定しましたが、結果にどのように到達したかを理解したいですか?
パスワード結果_5hfGisIwnlQ=
_
アプリケーションは_Java.crypto.cipher
_およびDES
暗号化を使用します。
key
= _ucnTRFY<jhvbu,fwy8\u00a3329+3yrwiefDY#FGVoD@IOHa?widNCD(ZH3*82YU9%Fjpo
_という文字列を見つけました
アプリケーションは次のように暗号化しているようです:
_ private SecretKey getKey() {
byte[] desKeyData = new byte[8];
for (int i = 0; i < 7; ++i) {
int j = i * 3;
desKeyData[i] = (byte)(key.charAt(j) ^ key.charAt(j + 2));
}
return new SecretKeySpec(desKeyData, "DES");
}
public byte[] encrypt(String password) {
if (password == null) {
return null;
}
byte[] encryptedBytes = password.getBytes();
if (!doEncryption) {
return encryptedBytes;
}
SecretKey key = this.getKey();
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(1, key);
byte[] bytes = null;
try {
bytes = password.getBytes("UTF-8");
}
catch (UnsupportedEncodingException ex) {
// empty catch block
}
encryptedBytes = cipher.doFinal(bytes);
this.encrypted = true;
}
_
このように復号化します
_public String decrypt(byte[] encryptedBytes) {
if (encryptedBytes == null) {
return null;
}
String password = null;
try {
password = new String(encryptedBytes, "UTF-8");
}
catch (UnsupportedEncodingException ex) {
// empty catch block
}
if (!doEncryption || !this.encrypted) {
return password;
}
SecretKey key = this.getKey();
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(2, key);
password = new String(cipher.doFinal(encryptedBytes));
}
catch (NoSuchAlgorithmException e) {
logger.error((Object)e);
}
catch (InvalidKeyException e) {
logger.error((Object)e);
}
catch (NoSuchPaddingException e) {
logger.error((Object)e);
}
catch (IllegalBlockSizeException e) {
logger.error((Object)e);
}
catch (BadPaddingException e) {
logger.error((Object)e);
}
return password;
}
_
私はこれが何をしているかに最も興味がありますか? private SecretKey getKey()
そして、引数はここにありますcipher.init(2, key)
?
この前に、Bcryptを使用してアプリケーションを実装しました。各方法の長所と短所は何ですか?
FYIこれが私がBcryptを実装した方法です。 if (BCrypt.checkpw(userId.getText() + passwordfield.getText(), passwordhashadmin)
DESアルゴリズムの理論的な部分は this や this などの多くの場所で理解できます。
プライベートSecretKey getKey()は何をしていますか?
このメソッドは、SecretKeySpec(desKeyData、 "DES")を使用してDESアルゴリズムにフィードする初期ベクトル(IV)を生成し、対称鍵を生成します。メソッド内のループは、必要な場合のIV。なぜそのようにしたのかはわかりませんが、IVを生成するために次のことも簡単に行うことができます。
String myIv ="this is my IV"
new SecretKeySpec( myIv.getBytes() , "DES");
Cipher cipher = Cipher.getInstance("DES");
このコード行は、DECアルゴリズムを使用してCipherクラスからオブジェクトをインスタンス化します。 initメソッドは、キーとモードを使用して暗号オブジェクトを初期化します。あなたが与えたコードが機能しているかどうかはわかりません。ただし、次のようにモードで暗号(ENCRYPTまたはDECRYPT)を使用する方法を説明する必要があります。
cipher.init(Cipher.ENCRYPT_MODE, key);
encryptedBytes = cipher.doFinal(bytes);
この行は実際の暗号化を行っています。 doFinalメソッドはブロック暗号であるため、パディングを行うことに注意してください。
復号化するときは、次のようにモードを使用する必要があります。
cipher.init(Cipher.DECRYPT_MODE, key);
ここでもう1つ言及する必要があります。文字列を使用してパスワードや機密情報を保存しないことをお勧めします。文字列はほとんどの言語で不変であり、メモリから取得できるためです。機密データを格納するためにバイト配列を使用する方がよい。
このコードは非常に悪いです。他の場所からコピーする場合は、コピーを停止する必要があります。自分で作成している場合は、実際にセキュリティが重要なシステムでは使用しないでください。それが目的である場合は、専門家の支援を求める必要があります。このようにコーディングする正当な目的は、教育的な演習だけです。
問題に飛び込みましょう。
DESは1970年代の暗号で、56ビットの鍵を備えています。これは、セキュリティにとっては小さすぎるキースペースです。 DESキーのブルートフォース検索は、20年以上前にすでに実証されています。
key
= _ucnTRFY<jhvbu,fwy8\u00a3329+3yrwiefDY#FGVoD@IOHa?widNCD(ZH3*82YU9%Fjpo
_という文字列を見つけました
これが実際のプログラムの文字列定数である場合、それは本当にひどいです。プログラムは暗号化キーをハードコードしないでください。安全なストレージから実行時にそれらを読み取る必要があります。このようなハードコードされたキーが許容される唯一のシナリオは、簡単なデモプログラムです。
文字列がハードコードされているのではなくファイルからのものである場合でも、既にそれを読み取ることができるという事実は、適切に保護されているかどうか疑問に思われるはずです。秘密鍵の場合、どうしてインターウェブでそれを読んでいるのですか?
DESはそのキーを_byte[8]
_として受け取ります。プログラムは、key
文字列からその_byte[8]
_を本当に複雑な方法で計算します。
_private SecretKey getKey() {
byte[] desKeyData = new byte[8];
for (int i = 0; i < 7; ++i) {
int j = i * 3;
desKeyData[i] = (byte)(key.charAt(j) ^ key.charAt(j + 2));
}
return new SecretKeySpec(desKeyData, "DES");
}
_
これはコードを不必要に複雑にする以外に何もしません。適切に作成された実際のプログラムは、バイナリファイルから直接読み取るか、バイナリ配列の文字列表現(16進数、Base64など)から逆シリアル化することにより、desKeyData
を取得します。
コードはパスワードを暗号化しています。これは安全でないパスワード保存方法です。パスワードに関するよくある質問のトップアンサーである "パスワードを安全にハッシュする方法?" をご覧ください。これは、bcryptに関する質問にも答えるはずです。
このコード行:
_Cipher cipher = Cipher.getInstance("DES");
_
... ECB(「電子コードブック」)モードでCipher
オブジェクトを初期化します。 これはほとんど例外なく悪い考えです。
一般的なルールとして、メッセージのauthenticityとconfidentialityの両方を保護する暗号のみを使用する必要があります。最新の暗号は、攻撃者がメッセージの内容を読み取るだけでなく、偽造からも保護します。
ECBモードはそれを行いません。また、CBCのようなモードを使用しない多くのサンプルコードも見つかります。
この行は不必要に読みにくいです。
_cipher.init(1, key);
_
それはこれでなければなりません:
_cipher.init(Cipher.ENCRYPT_MODE, key);
_
次のような行がたくさんあります。
_catch (UnsupportedEncodingException ex) {
// empty catch block
}
_
またはこれら:
_catch (BadPaddingException e) {
logger.error((Object)e);
}
return password;
_
セキュリティクリティカルなコードは例外的にクリーンである必要があります。 何かが失敗した場合、一般的に最悪の事態を想定して、操作全体を失敗させ、例外を飲み込まず、何も問題が発生していないように続ける必要があります。
(また、2番目の例のObject
へのアップキャストは取るに足らないものであり、削除できます。)
doEncryption
およびencrypted
ブールフィールドを使用したそのすべてのロジックは、私を緊張させます。このコードは、オブジェクトが4つの状態にあるクラスの中にあるようです。
...そしてクラスのメソッドと外部クライアントは、暗号化されていないパスワードを誤って公開しないように、その状態に注意する必要があります。 encrypt()
メソッドの次のコードは、最も症状が強い例です。
_if (!doEncryption) {
return encryptedBytes;
}
_
したがって、このクラスを使用していてencrypt(mySuperSecretData)
メソッドを呼び出すと、実際にはまったく暗号化されない可能性があります?クラスとメソッドのコントラクトは何ですか?その契約は本当に混乱しているのでしょうか、それともクラスのクライアントがそれを簡単に悪用することを可能にしていますか?
なぜこのような複雑なクラスにこれらの多くの自己破壊的なオプションがあるのですか?代わりに、PlaintextPassword
とScrambledPassword
を別々の型にして、PlaintextPassword
を期待するコードにScrambledPassword
を渡そうとすると、コンパイル時にエラーになり、セキュリティ障害にはなりません。慎重な設計と規律により、プログラムが誤って機密データを公開しないという大きな確信を得ることができます。
これは前のポイントと同じテーマの一部です。セキュリティクリティカルなコードは例外的にクリーンである必要があります。これらの2つのメソッド全体でif
ステートメントと_a || b
_条件を一時停止して注意深く読み、それらが誤って記述され、一部のシナリオで秘密情報が明らかになるリスクがあるかどうかを把握する必要がある場合、そのロジックをすべて削除できるかどうかを尋ねる必要があります。
これは、暗号化に取り組むための誤ったレベルです。暗号化の実用的な使用に興味がある場合は、簡単で簡単なインターフェースを提供する優れた高レベルの暗号化ライブラリを探してください。デフォルトの選択肢が安全であり、信頼できる本当に優れたドキュメントが用意されているため、 正確にそれを使用する方法。
デフォルトのJava暗号化APIはそうではありません。Javaには多くの暗号化サポートがありますが、すべて使用するのは非常に難しく危険です。 それを安全に使用するためのたくさんの知識、およびその知識は、どこかで見つけたランダムなコードサンプルをいじくり回して取得することはできません。 、少なくとも古くなっていて、ベストプラクティスを反映していないことがよくあります。Java暗号化APIの使用を習得するには、通常、健全な暗号化の基礎を理解できるサードパーティの資料を読む必要があります。