вычислить хэш md5 для данных из нескольких частей (несколько строк) - PullRequest
12 голосов
/ 24 января 2011

Я пытаюсь создать [одиночный] хэш md5 из нескольких строк [в Java]. То есть я хочу

md5(string1, string2, string3, ..., stringN)

В настоящее время я пытаюсь объединить все строки с некоторым редко используемым разделителем, таким как #. Это

md5(string1#string2#...#stringN)

Это выглядит странно, и меня беспокоит какая-то странная строка, в которой фактически есть разделитель. Каков наилучший способ сделать это?

Ответы [ 11 ]

10 голосов
/ 24 января 2011

Возможно, это будет лучше:

md5(md5(string1) + md5(string2) + ... + md5(stringN))

Это устранит проблему с разделителем, но трудно сказать, насколько он хорош.

4 голосов
/ 24 января 2011

Не имеет значения, является ли разделитель частью строки.Вы, вероятно, даже не нуждаетесь в разделителе, поскольку вы не собираетесь разбивать составную строку на части

3 голосов
/ 24 января 2011

Если вы хотите быть уверены, что не может быть столкновения при перемещении текста из одной строки в другую, я рекомендую следующую схему:

md5(<len1>+str1+<len2>+str2...)

Здесь len1 - представление фиксированной длиныдлины ул1.Для md5 было бы наиболее целесообразно использовать четырехбайтовое значение типа int (при условии, что вы знаете, что у вас не будет строк длиннее 2 ** 31).В качестве альтернативы используйте «десятичную длину #», т. Е. (В нотации Python)

md5(str(len(str1))+"#"+str(len(str2))+"#"+str2+...)

Это не может привести к коллизиям, просто перемещая текст из одной строки в другую, так как длина будет изменяться.

3 голосов
/ 24 января 2011

Раньше я сталкивался с подобной проблемой, и лучшее решение, которое я мог придумать, состояло в том, чтобы использовать не типизируемый символ ascii в качестве разделителя.Посмотрите на "man ascii" и выберите один.Мой фаворит - '\ a', который является символом ASCII для звука "звонка".

2 голосов
/ 13 ноября 2013

вы можете использовать base64 для кодирования вашей строки перед добавлением вашей строки. затем в функции md5 разбейте вашу строку и декодируйте ее. например

public class MutilMd5 {

public static void main(String[] args) throws Base64DecodingException {
    String s1 = "12#3";
    String s2 = "#12345";

    multMd5(Base64.encode(s1.getBytes()) + "#" + Base64.encode(s2.getBytes()));

}

public static void multMd5(String value) throws Base64DecodingException {
    String md5 = "";
    String[] encodeStrings = value.split("#");
    if (encodeStrings != null) {
        for (String encodeString : encodeStrings) {
            System.out.println(new String(Base64.decode(encodeString.getBytes())));
            md5 = md5 + DigestUtils.md5Hex(encodeString);
        }
    }

    System.out.println(md5);
}

}

вывод

12 # 3

12345

13094636ff02b51be53c496d04d39bc2375704c2e00da07d2c9acc7646b2a844

2 голосов
/ 11 ноября 2013

Собирая все ответы вместе, вот класс с одним открытым и статическим методом, который эффективно решает поставленный вопрос. Не стесняйтесь комментировать, критиковать или использовать этот код по своему усмотрению (общественное достояние и все) ...

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5Summer is a utility class that abstracts the complexity of computing
 * the MD5 sum of an array of Strings.
 * <p>
 * Submitted as an answer to the StackOverflow bounty question:
 * <a href="/3833010/vychislit-hesh-md5-dlya-dannyh-iz-neskolkih-chastei-neskolko-strok">
 * compute md5 hash of multi part data (multiple strings)</a>
 * <p>
 * This solution uses the 'fast' "byte[] to hex string" mechanism described here in
 * <a href="http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java">
 * Convert from byte array to hex string in java</a>.
 * <p>
 * The MD5 sum is always calculated by converting the inputStrings to bytes based on
 * the UTF-8 representation of those Strings. Different platforms using this class
 * will thus always calculate the same MD5sum for the same Java Strings.
 * <p>
 * Using a ThreadLocal for storing the MessageDigest instance significantly reduces the amount of time spent
 * obtaining a Digest instance from the java.security subsystem.
 * <p>
 * <i>Copyright - This code is released in to the public domain</i>
 */
public final class MD5Summer {

    /**
     * Calculate the MD5 sum on the input Strings.
     * <p>
     * The MD5 sum is calculated as if the input values were concatenated
     * together. The sum is returned as a String value containing the
     * hexadecimal representation of the MD5 sum.
     * <p>
     * The MD5 sum is always calculated by converting the inputStrings to bytes based on
     * the UTF-8 representation of those Strings. Different platforms using this class
     * will thus always calculate the same MD5sum for the same Java Strings.
     * 
     * @param values The string values to calculate the MD5 sum on.
     * @return the calculated MD5 sum as a String of hexadecimal.
     * @throws IllegalStateException in the highly unlikely event that the MD5 digest is not installed.
     * @throws NullPointerException if the input, or any of the input values is null.
     */
    public static final String digest(final String ...values) {
        return LOCAL_MD5.get().calculateMD5(values);
    }

    /**
     * A Thread-Local instance of the MD5Digest saves construct time significantly,
     * while avoiding the need for any synchronization.
     */
    private static final ThreadLocal<MD5Summer> LOCAL_MD5 = new ThreadLocal<MD5Summer>() {
        @Override
        protected MD5Summer initialValue() {
            return new MD5Summer();
        }   
    };

    private static final char[] HEXCHARS = "0123456789abcdef".toCharArray();
    private static final Charset UTF8 = Charset.forName("UTF-8");


    private final MessageDigest md5digest;

    /**
     * Private constructor - cannot create instances of this class from outside
     */
    private MD5Summer () {
        // private constructor making only thread-local instances possible.
        try {
            md5digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            // MD5 should always be available.
            throw new IllegalStateException("Unable to get MD5 MessageDigest instance.", e);
        }
    }

    /**
     * Private implementation on the Thread-local instance.
     * @param values The string values to calculate the MD5 sum on.
     * @return the calculated MD5 sum as a String of hexadecimal bytes.
     */
    private String calculateMD5(final String ... values) {
        try {
            for (final String val : values) {
                md5digest.update(val.getBytes(UTF8));
            }
            final byte[] digest = md5digest.digest();
            final char[] chars = new char[digest.length * 2];
            int c = 0;
            for (final byte b : digest) {
                chars[c++] = HEXCHARS[(b >>> 4) & 0x0f];
                chars[c++] = HEXCHARS[(b      ) & 0x0f];
            }
            return new String(chars);
        } finally {
            md5digest.reset();
        }
    }

}

Я сравнил результаты этой программы с программой Linux md5sum со следующим небольшим тестом:

public class MD5Tester {
//    [rolf@rolfl ~/md5data]$ echo "Frodo Baggins" >> frodo
//    [rolf@rolfl ~/md5data]$ echo "Bilbo Baggins" >> bilbo
//    [rolf@rolfl ~/md5data]$ cat frodo bilbo 
//    Frodo Baggins
//    Bilbo Baggins
//    [rolf@rolfl ~/md5data]$ cat frodo bilbo | md5sum 
//    a8a25988435405b9a62634c887287b40 *-
//    [rolf@rolfl ~/md5data]$ 


    public static void main(String[] args) {
        String[] data = {"Frodo Baggins\n", "Bilbo Baggins\n"};
        String md5data = MD5Summer.digest(data);
        System.out.println("Expect a8a25988435405b9a62634c887287b40");
        System.out.println("Got    " + md5data);
        if (!"a8a25988435405b9a62634c887287b40".equals(md5data)) {
            System.out.println("Data does not match!!!!");
        }
    }
}
2 голосов
/ 10 ноября 2013

для строк, я думаю, + решение Коли Кляйна о добавлении не типизируемых символов является лучшим.

Если вам нужно решение, которое работает и для двоичных данных, или вы не уверены, что строка donне содержит этих символов, вы можете использовать рекурсивный хеш, например:

md5(md5(str1)+md5(str2)+md5(str3)+...+)

зависит от объема данных, для решения которого может потребоваться много ресурсов (недавно я профилировал программу и обнаружил, что 97%пора рассчитывать sha1, поэтому я должен предупредить вас ..)

2 голосов
/ 24 января 2011

Только не отделяйте их.Это хеш-метод: их не нужно разделять ...

MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = ...;
for (String toHash: stringsToHash) {
  md5.update(toHash.getBytes("UTF-8"));
}
md5.digest(bytes);
1 голос
/ 14 ноября 2013

Поскольку вы используете строку в качестве входных данных, и мы знаем, что строки не имеют символа NULL, поэтому могут использовать символ NULL в качестве разделителя для всех строк. Вы проверяете ввод для NULL символа во время аутентификации.

md5(String1+NULL+String2+NULL+String3....)

Экономит ваше время на несколько Md5.

1 голос
/ 14 ноября 2013

Насколько я понимаю, вы хотите хэшировать списки строк, гарантируя, что никакие два разных списка не дадут одинакового результата. Это можно решить, не думая о хэш-функции вообще.

Вам нужна функция String f(List<String> l), где никакие два входных значения не приводят к одному и тому же выводу (функция инъективная от List<String> до String). Благодаря этому вы можете передать выходные данные своей хеш-функции и быть уверенными, что не будет никаких коллизий, поскольку сама хеш-функция обеспечивает это (обратите внимание, MD5 был сломан несколько лет назад, поэтому он может не подходить). Вот 2 способа реализации f:

Преобразование в набор символов из набора

Самый простой способ - просто сопоставить каждый ввод с подмножеством набора символов String, который не включает ваш символ-разделитель:

public static String hex(String s) {
    try {
        String o = "";
        for(byte b: s.getBytes("utf-8"))
        o += String.format("%02x", b&0xff);
        return o;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public static String f(String... l) {
    if (l.length == 0) return "";
    String o = hex(l[0]);
    if (l.length == 1) return o;
    for (int i = 1; i < l.length; i++) o += "#" + hex(l[i]);
    return o;
}
f("a#","b") => 6123#62
f("a","#b") => 61#2362

Длина префикса

Это также довольно просто, но имеет тот недостаток, что его нельзя переписать для работы в потоке.

public static String prefix(String s) {
    return s.length() + "." + s;
}

public static String f(String... l) {
    if (l.length == 0) return "";
    String o = prefix(l[0]);
    if (l.length == 1) return o;
    for (int i = 1; i < l.length; i++) o += "#" + prefix(l[i]);
    return o;
}
f("a#","b") => 2.a##1.b
f("a","#b") => 1.a#2.#b
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...