1

HashCalcの上部には「データ形式」というフィールドがあり、それを「HexString」に切り替えてから9a、データ フィールドにテスト値を入力します。SHA-1 ハッシュを実行すると、答えは次のようになります。13cba177bcfad90e7b3de70616b2e54ba4bb107f

(注: オンライン ハッシャーは "9a" を文字列としてハッシュし、結果としてe8eef065fb7295044d65b305bab18a9a645d1abf. このアプリケーションでは間違っています)

ここで、このタイプのハッシュを Java プログラムに埋め込む必要があります。これは私がこれまでに得たものです(try/catchにラップされています):

String ss = "9a";
ByteBuffer bb = ByteBuffer.allocate(8);  
byte[] ba = bb.putLong(Long.decode("0x"+ss).longValue()).array();
MessageDigest md = MessageDigest.getInstance("SHA-1");
String results = encodeHex(md.digest(ba));
System.out.println("sha:"+results);

しかし、私の結果はE73C417858807239DD5BC30BA978C14D57F80834

私は何を間違っていますか?

編集: 16 進タグを追加しました。データが何らかの 16 進形式でなければならないことは明らかです。HashCalc は、別の結果を返す「TextString」ではなく「HexString」に設定する必要があるためです。そして、解決策には、これらの 16 進数の処理方法の変更が含まれる可能性があります。--> 事実であることが判明しました

4

3 に答える 3

3

免責事項

OP (Pimp Trizkit) は、正しい解決策を見つけた人です。結果を紹介し、楽しむために、彼のソリューションを(わずかな変更を加えて)使用しました。全著作権所有)

また、OP によって提供されるバイト配列から 16 進文字列への変換アルゴリズムは、サンプル コードのものよりもはるかに高速です。実装については、彼のソリューションを参照してください。
(詳細については、下のコメントをお読みください)


手動の解決策の1つ:(
重要これは私の最初の答えでしたが、OPが要求した16進文字列からではなく、テキスト文字列からハッシュを取得するためのものです。以下の更新を参照してください):

import java.security.MessageDigest;

public class TestHash {

    public static void main(String[] args) throws Exception {
        String password = "9a";

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(password.getBytes());
        byte[] byteData = md.digest();
        // byte[] byteData = md.digest(password.getBytes());    // both updates and completes the hash computation

        // Method 1 of converting bytes to hex format
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < byteData.length; i++) {
            sb.append(Integer.toString((byteData[i] & 0xFF) + 0x100, 16).substring(1));
        }

        System.out.println("1) Hex format : " + sb.toString());

        // Method 2 of converting bytes to hex format
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < byteData.length; i++) {
            String hex = Integer.toHexString(0xff & byteData[i]);
            // NB! E.g.: Integer.toHexString(0x0C) will return "C", not "0C"            
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        System.out.println("2) Hex format : " + hexString.toString());      
    }
}

出力:

1) Hex format : e8eef065fb7295044d65b305bab18a9a645d1abf
2) Hex format : e8eef065fb7295044d65b305bab18a9a645d1abf

アップデート

OP found solutionに基づいて、テキスト文字列ではなく16 進文字列から SHA-1 ハッシュを取得する方法を示すコードを次に示します。さらに、バイト配列を 16 進文字列に手動で変換するいくつかの方法を示します (楽しみのために)。すみません、私は気分が良かったです))

重要な瞬間の説明については、メソッドmain内およびさまざまなメソッド内の私のコメントを参照してください。bytesToHexString()

import java.security.MessageDigest;
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;

public class TestHash3 {

    public static void main(String[] args) throws Exception {
        String hexString = "1234";

        /*
         * NB!
         * Before passing hex string to DatatypeConverter.parseHexBinary(),
         * we need to check if the hex sting is even-length, 
         * otherwise DatatypeConverter.parseHexBinary() will throw a
         * java.lang.IllegalArgumentException: hexBinary needs to be even-length
         */
        hexString = (hexString.length() % 2 == 0) ? hexString : "0" + hexString;
        byte[] bytes = DatatypeConverter.parseHexBinary(hexString);

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] byteData = md.digest(bytes);

        System.out.println("1) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString1(byteData));
        System.out.println("2) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString2(byteData));
        System.out.println("3) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString3(byteData));
        System.out.println("4) SHA-1 hash for the hex string " + hexString + ": " +
                            bytesToHexString4(byteData));
    }

    public static String bytesToHexString1(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            hexBuffer.append(Integer.toString((bytes[i] & 0xFF) + 0x100, 16).substring(1));
        }

        return hexBuffer.toString();
    }

    public static String bytesToHexString2(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer(bytes.length * 2);
        for (byte b: bytes) {
            int n = b & 0xFF;   // casting to integer to avoid problems with negative bytes
            if (n < 0x10) {
                hexBuffer.append("0");
            }
            hexBuffer.append(Integer.toHexString(n));
        }

        return hexBuffer.toString();
    }       

    public static String bytesToHexString3(byte[] bytes) {
        StringBuffer hexBuffer = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String hexString = Integer.toHexString(0xff & bytes[i]);
            // NB! E.g.: Integer.toHexString(0x0C) will return "C", not "0C"            
            if (hexString.length() == 1) {
                hexBuffer.append('0');
            }
            hexBuffer.append(hexString);
        }

        return hexBuffer.toString();
    }

    public static String bytesToHexString4(byte[] bytes) {
        String hexString = new BigInteger(1, bytes).toString(16);

        /*
         * NB!
         * We need an even-length hex string to propely represent bytes in hexadecimal.
         * A hexadecimal representation of one byte consists of two hex digits.
         * If the value is less than 16 (dec), it is prepended with zero
         * E.g.:
         * 1  (byte)    ==> 01 (hex)    // pay attention to the prepended zero
         * 15 (byte)    ==> 0F (hex)
         * 16 (byte)    ==> 10 (hex)    // no need to prepend
         * 255(byte)    ==> FF (hex)
         *
         * BigInteger.toString(16) can return both even and odd-length hex strings.
         * E.g.:
         * byte[] bytes = {15, 16}  // two bytes
         * BigInteger(1, bytes).toString(16) will produce (NB!): f10
         * But we need (NB!): 0f10
         * So we must check if the resulting hex string is even-length,
         * and if not, prepend it with zero.
         */
        return ((hexString.length() % 2 == 0) ? hexString : "0" + hexString);
    }
}

出力:

1) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
2) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
3) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d
4) SHA-1 hash for the hex string 1234: ffa76d854a2969e7b9d83868d455512fce0fd74d


ところで、16進文字列が内部で偶数の長さであるかどうかを確認して、byteToHexString4()独立して使用できるようにします。


更新 2

ユーザー@kanは、バイト配列を16進文字列に変換するもう1つの方法、非常に単純な1つのライナー、およびOPの方法に次ぐ2番目に速い方法をもたらしました。

DatatypeConverter.printHexBinary(byte[] val)

于 2013-03-27T21:30:59.630 に答える
1

私はそれを考え出した!私の質問へのコメントでインスピレーションを与えてくれたLouis Wassermanに感謝します。HashCalc で使用000000000000009aすると、関数と同じ結果が返されました。特定のデータ長 (a の長さlong) を強制していることに気付きました。それは任意の長さである必要があることが判明しました (HashCalc のように...私が言ったように、私9aたちが使用する小さなテストケースだけでなく、可能なすべてのデータに対して、HashCalc の動作を複製する必要がありました)。よりもさらに長いlong。したがって、これらの他のソリューションはここでは機能しません。

元の 16 進文字列を char[] 配列に変換し、それらをペアで連結し、ループ内でバイトに変換し、ダイジェストに渡される byte[] にそれぞれを格納することで、これを回避しました。これはうまくいきました!それから、これに関する彼の回答に対するカンのコメントを見て、私が車輪の再発明をしていることを示しました。

これが最終的なコードで、元のコードと驚くほど同じサイズです。

private String HexToSHA1(String ss) throws IllegalArgumentException {
    MessageDigest md = null;
    try { md = MessageDigest.getInstance("SHA-1"); }
    catch ( Exception e ) {}
    return byteArrayToHexString(md.digest(DatatypeConverter.parseHexBinary(ss)));
}

これはかなり速く実行されますが、もっと速い方法があるかどうかはわかりません。もちろん、それbyteArrayToHexStringは関数の速度にも基づいています。これが私のものです:

private String byteArrayToHexString(byte[] data) {
    char[] toDigits =  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    int l = data.length;
    char[] out = new char[l << 1];
    for (int i = 0, j = 0; i < l; i++) {
        out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
        out[j++] = toDigits[0x0F & data[i]];
    }
    return new String(out);
}

そして、かなり速いです。どこかの誰かの功績。私はそれを書いていません。

EDIT:char[] toDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};グローバル(または「スコープ内の1つ上」)に移動すると、さらに高速になります。

于 2013-03-28T00:54:56.620 に答える
1

小さなユーティリティ クラスを使用します。

public abstract class Sha1Util
{
    private static final Charset UTF8 = Charset.forName("UTF-8");

    public static MessageDigest newSha1Digest()
    {
        try
        {
            return MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e)
        {
            throw new Error(e);
        }
    }

    public static void update(final MessageDigest digest, final String s)
    {
        digest.update(s.getBytes(UTF8));
    }

    public static String sha1sum(final MessageDigest digest)
    {
        return String.format("%040x", new BigInteger(1, digest.digest()));
    }
}

テスト:

@Test
public void testSha1For9a()
{
    final MessageDigest md = SecUtil.newSha1Digest();
    SecUtil.update(md, "9a");// you could use several updates e.g. for salted passwords
    assertEquals("e8eef065fb7295044d65b305bab18a9a645d1abf", SecUtil.sha1sum(md));

}
于 2013-03-27T21:53:43.060 に答える