4

最近、(Javaでの)MD5ハッシュを調べ始めました。それを実現するのに役立つアルゴリズムとメソッドを見つけましたが、実際にどのように機能するのか疑問に思っています。

1つは、このURLから次のことを見つけました。

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }
    return buf.toString();
}

Javaでビットシフトを使用する必要はないので、少し錆びています。上記のコードがどのように正確に変換を行うかを(簡単に)説明するのに十分な親切な人はいますか?">>>"?

また、StackOverflowで他のソリューションを見つけました。たとえば、ここここでは、代わりにBigIntegerを使用しています。

try {
   String s = "TEST STRING";
   MessageDigest md5 = MessageDigest.getInstance("MD5");
   md5.update(s.getBytes(),0,s.length());
   String signature = new BigInteger(1,md5.digest()).toString(16);
   System.out.println("Signature: "+signature);

} catch (final NoSuchAlgorithmException e) {
   e.printStackTrace();
}

なぜそれも機能するのですか、そしてどちらの方法がより効率的ですか?

御時間ありがとうございます。

4

4 に答える 4

10
private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {

この時点まで...基本的なセットアップと、配列内のすべてのバイトを通過するループの開始

        int halfbyte = (data[i] >>> 4) & 0x0F;

16進数に変換されたときのバイトは、表示するベースに応じて2桁の16進数または8桁の2進数になります。上記のステートメントは上位4ビットを下にシフトし(>>>は符号なし右シフト)、00001111と論理ANDします。結果は、バイトの上位4ビット(最初の16進数)に等しい整数になります。

23が入力だったとしましょう。これは、バイナリで00010111です。シフトは論理積を作成し、これを00000001に変換します。

        int two_halfs = 0;
        do {

これは、do/whileループを2回実行するように設定するだけです。

            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

ここでは、実際の16進数を表示しています。基本的には、ゼロまたは文字を開始点として使用し、正しい文字にシフトアップします。最初のifステートメントは0〜9のすべての数字をカバーし、2番目のステートメントは10〜15のすべての数字をカバーします(16進数のaf)

ここでも、10進数の0000 0001は1に等しい例を使用します。上部のifブロックに引っ掛かり、「0」文字に1を追加して文字「1」を取得し、それを文字列に追加して次に進みます。

                halfbyte = data[i] & 0x0F;

次に、バイトの下位ビットとちょうど等しくなるように整数を設定し、繰り返します。

繰り返しますが、入力が23 ... 0001 0111の場合、論理積は0000 0111になり、10進数で7になります。上記と同じロジックを繰り返すと、文字「7」が表示されます。

            } while(two_halfs++ < 1);

ここで、配列の次のバイトに移動して繰り返します。

        }
    return buf.toString();
}

次の質問に答えるために、JavaAPIにはすでにBigIntegerに組み込まれた基本変換ユーティリティがあります。toString(int radix)のドキュメントを参照してください。

Java APIで使用される実装がわからないので、はっきりとは言えませんが、Javaの実装は、最初に投稿したやや単純なアルゴリズムよりも効率的であると確信しています。

于 2009-06-25T11:23:25.253 に答える
2

このビットに答えるには:

なぜそれも機能するのですか

そうではありません。少なくとも、ループバージョンと同じ方法ではありません。new BigInteger(...)。toString(16)は、以前のバージョンのように先行ゼロを表示しません。通常、バイト配列(特にハッシュのようなものを表すもの)を書き出すようなものでは、固定長の出力が必要になるため、そのバージョンを使用する場合は、適切にパディングする必要があります。

于 2009-06-26T01:24:29.483 に答える
1

ビットシフトの詳細については、次のSOの質問の回答を確認してください。 ビット単位のシフト(ビットシフト)演算子とは何ですか。また、それらはどのように機能しますか。

彼は、1バイトを16より小さい数に変換しようとしているようです。そうすることで、そのバイトがコードで表す文字を簡単に判別できます。

  if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

これは単純な答えですが、とにかくそれほど明るくはありません= D

于 2009-06-25T11:19:36.337 に答える
0

これらのものは、すでにapache-commons-codecで記述されているため、自分で記述する必要はありません。

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(byte[] array)

クラスにはもっと便利なメソッドがたくさんありますHex

于 2014-04-28T13:54:19.727 に答える