String(byte[])
これはコンストラクターの適切な使用法ではないことを指摘する人もいます。Javaでは、aは文字で構成されていることを覚えておくことが重要String
です。文字は、バイトのように8ビットではなく16ビットです。また、文字エンコードについても忘れています。多くの場合、文字はバイトではないことを忘れないでください。
それを少しずつ分解してみましょう:
String s = "test123";
byte[] a = s.getBytes();
この時点で、システムのデフォルトの文字エンコードがWindows-1252
またはである場合、バイト配列には8バイトが含まれている可能性がありiso-8859-1
ますUTF-8
。
byte[] b = env.encrypt(a);
現在b
、暗号化に応じて一見ランダムなデータが含まれており、特定の長さであることが保証されていません。多くの暗号化エンジンは、出力が特定のブロックサイズに一致するように入力データをパディングします。
String t = new String(b);
これは、ランダムなバイトを取得し、Javaにそれらを文字データとして解釈するように要求しています。これらの文字はぎこちなく見える場合があり、ビットの一部のシーケンスはすべてのエンコーディングで有効な文字ではありません。Javaは忠実に最善を尽くし、16ビット文字のシーケンスを作成します。
byte[] c = t.getBytes();
b
これにより、エンコーディングに応じて、と同じバイト配列が得られる場合と得られない場合があります。問題の説明で、c
16バイトの長さであると述べています。これはおそらく、tのガベージがデフォルトの文字エンコードでうまく変換されないためです。
byte[] d = env.decrypt(c);
c
期待するデータではなく、破損しているため、これは機能しません。
ソリューション:
- バイト配列をデータベースまたはどこにでも直接保存するだけです。ただし、文字エンコードの問題についてはまだ忘れています。これについては1秒で詳しく説明します。
バイト配列データを取得し、Base64を使用して、または16進数としてエンコードし、その文字列を格納します。
byte[] cypherBytes = env.encrypt(getBytes(plainText));
StringBuffer cypherText = new StringBuffer(cypherBytes.length * 2);
for (byte b : cypherBytes) {
String hex = String.format("%02X", b); //$NON-NLS-1$
cypherText.append(hex);
}
return cypherText.toString();
文字コード:
ユーザーのパスワードはASCIIでない可能性があり、エンコードを指定しないため、システムで問題が発生する可能性があります。
比較:
String s = "tést123";
byte[] a = s.getBytes();
byte[] b = env.encrypt(a);
と
String s = "tést123";
byte[] a = s.getBytes("UTF-8");
byte[] b = env.encrypt(a);
バイト配列は、システムのデフォルトa
と同じエンコーディングの値を持ちませんUTF-8
(システムのデフォルトがである場合を除くUTF-8
)。A)一貫性があり、B)エンコーディングがデータに使用できるすべての文字を表すことができる限り、どのエンコーディングを使用してもかまいません。おそらく、システムのデフォルトのエンコーディングで中国語のテキストを保存することはできません。アプリケーションが複数のコンピューターにデプロイされていて、そのうちの1つが異なるシステムデフォルトのエンコードを使用している場合、一方のシステムで暗号化されたパスワードは、もう一方のシステムではぎこちなくなります。
話の教訓:文字はバイトではなく、バイトは文字ではありません。どちらを扱っているのか、それらの間でどのように変換するのかを覚えておく必要があります。