4

Stringオブジェクトにラップされたバイト配列を格納したい。これがシナリオです

  1. ユーザーがパスワードを入力します。
  2. そのパスワードのバイトは、getBytes()Stringメソッドを使用して取得されます。
  3. これらのバイトは、Javaのcrypoパッケージを使用して暗号化されます。
  4. これらのバイトは、コンストラクターnew String(bytes [])を使用して文字列に変換されます。
  5. その文字列は保存されるか、他の方法で渡されます(変更されません)
  6. その文字列のバイトが取得され、エンコードされたバイトとは異なります。

これが私が話していることを説明するコードの抜粋です。

String s = "test123";
byte[] a = s.getBytes();
byte[] b = env.encrypt(a);
String t = new String(b);
byte[] c = t.getBytes();
byte[] d = env.decrypt(c);

ここで、env.encrypt()とenv.decrypt()は暗号化と復号化を行います。私が抱えている問題は、b配列の長さが8で、c配列の長さが16であるということです。これらは等しいと思います。何が起きてる?以下のようにコードを変更してみました

String s = "test123";
Charset charset = Charset.getDefaultCharset();
byte[] a = s.getBytes(charset);
byte[] b = env.encrypt(a);
String t = new String(b, charset);
byte[] c = t.getBytes(charset);
byte[] d = env.decrypt(c);

しかし、それは役に立ちませんでした。

何か案は?

4

7 に答える 7

18

バイナリデータをStringオブジェクトに格納することはお勧めできません。バイナリデータを印刷可能な文字列にすることを目的とした、完全に可逆的なBase64エンコーディングのようなものを使用することをお勧めします。

実際、Java用のパブリックドメインbase64エンコーダーを見つけました:http://iharder.sourceforge.net/current/java/base64/

于 2009-08-18T19:25:09.277 に答える
11

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これにより、エンコーディングに応じて、と同じバイト配列が得られる場合と得られない場合があります。問題の説明で、c16バイトの長さであると述べています。これはおそらく、tのガベージがデフォルトの文字エンコードでうまく変換されないためです。

byte[] d = env.decrypt(c);

c期待するデータではなく、破損しているため、これは機能しません。

ソリューション:

  1. バイト配列をデータベースまたはどこにでも直接保存するだけです。ただし、文字エンコードの問題についてはまだ忘れています。これについては1秒で詳しく説明します。
  2. バイト配列データを取得し、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つが異なるシステムデフォルトのエンコードを使用している場合、一方のシステムで暗号化されたパスワードは、もう一方のシステムではぎこちなくなります。

話の教訓:文字はバイトではなく、バイトは文字ではありません。どちらを扱っているのか、それらの間でどのように変換するのかを覚えておく必要があります。

于 2009-08-18T19:44:33.310 に答える
4

どちらの場合も、OSのデフォルトの非Unicode文字セット(ロケールによって異なります)を使用しています。あるシステムから別のシステムに文字列を渡す場合、ロケールが異なる可能性があるため、デフォルトの文字セットが異なる可能性があります。実行しようとしていることを実行するには、明確に定義された1つの文字セットを使用する必要があります。例:ISO-8859-1。

さらに良いことに、変換を行わず、byte[]配列を直接渡します。

于 2009-08-18T19:27:02.970 に答える
3

これは、String(byte [])コンストラクターと関連メソッドの悪用です。

これは特定のエンコーディングで機能し、他のエンコーディングでは失敗します。おそらく、プラットフォームのデフォルトのエンコーディングは、失敗するエンコーディングの1つです。

これらのバイトをhexまたはbase64に変換するには、CommonsCodecのようなものを使用する必要があります。

また、とにかくパスワードをソルトでハッシュするのではなく暗号化するのはなぜですか?

于 2009-08-18T19:26:09.367 に答える
2

コンストラクターがStringargを受け取り、それをbyte[]に変換するStringWrapperクラスを実装します。「ISO-8859-1」エンコーディングを使用して、各文字が16ではなく8ビットになるようにします。その後、エンコーディング/デコーディングメソッドを使用してこれらのバイトを操作できます。

于 2012-05-18T00:31:27.597 に答える
1

これは正しく機能しません。バイトを文字列として格納することは、ASCIIセット(および他のいくつか)に対してのみ正しく機能します。暗号化された結果を文字列として保存する必要がある場合は、バイトを16進数に変換してから、それを文字列に入れるのはどうでしょうか。それはうまくいくでしょう。

パスワードはバイト単位で保持することをお勧めします。文字列として保存する本当の理由はありません(人々のパスワードが何であるかを確認したい場合を除きます)。

于 2009-08-18T19:28:11.297 に答える
0

明確な答えはありませんが、これに取り組んでいる場合は、各ステップで文字列またはバイトを出力し、それらを比較して何が起こっているかを確認します。また、bはenv.encryptからの戻り値を保持しますが、cは.getBytesからの戻り値であるため、この場合、リンゴとオレンジを比較しています。

于 2009-08-18T19:25:57.340 に答える