Подтвердить что ты не робот

Вычислять хеш md5 данных из нескольких частей (несколько строк)

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

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

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

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

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

4b9b3361

Ответ 1

Это может быть лучше:

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

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

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

Ответ 5

Просто не разделяйте их. Это хэш-метод: нет смысла их разделять...

MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = ...;
for (String toHash: stringsToHash) {
  md5.update(toHash.getBytes("UTF-8"));
}
md5.digest(bytes);

Ответ 6

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

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

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

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

Ответ 7

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

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="http://stackoverflow.com/questions/4785275/compute-md5-hash-of-multi-part-data-multiple-strings">
 * 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 {
//    [[email protected] ~/md5data]$ echo "Frodo Baggins" >> frodo
//    [[email protected] ~/md5data]$ echo "Bilbo Baggins" >> bilbo
//    [[email protected] ~/md5data]$ cat frodo bilbo 
//    Frodo Baggins
//    Bilbo Baggins
//    [[email protected] ~/md5data]$ cat frodo bilbo | md5sum 
//    a8a25988435405b9a62634c887287b40 *-
//    [[email protected] ~/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!!!!");
        }
    }
}

Ответ 8

вы можете использовать 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

Ответ 9

Вы можете использовать класс MessageDigest для генерации кода. Если бы я был вами, если на этапе препроцесса я знаю длину каждой строки, я бы передал их как уникальную String. Если строки, которые вы передаете, имеют разные случайные длины, которые не могут быть известны, я бы сделал их один за другим, но мне нужно знать, хорошо ли синхронизированы исходный объект и объект получения, чтобы узнать длину сообщений, которые они представляют проходя между собой.

private static final char[] HEXADECIMAL = { '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public  String hash(String stringToHash)  {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] bytes = md.digest(stringToHash.getBytes());
        StringBuilder sb = new StringBuilder(2 * bytes.length);
        for (int i = 0; i < bytes.length; i++) {
            int low = (int)(bytes[i] & 0x0f);
            int high = (int)((bytes[i] & 0xf0) >> 4);
            sb.append(HEXADECIMAL[high]);
            sb.append(HEXADECIMAL[low]);
        }
        return sb.toString();
    } catch (NoSuchAlgorithmException e) {
        //exception handling goes here
        return null;
    }
}

Ответ 10

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

Вам нужна функция String f(List<String> l), где нет двух входных значений в том же выпуске (injective от List<String> до String). При этом вы можете дать результат своей хеш-функции и быть уверенным, что не будет никаких столкновений, насколько это делает сама функция хэша (примечание MD5 было нарушено много лет назад, поэтому это может быть не подходящий выбор). Вот два способа реализации 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

Ответ 11

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

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

Сохраняет время многократного Md5.