Как создавать детерминированные гиды

В нашем приложении мы создаем файлы Xml с атрибутом, имеющим значение Guid. Это значение должно быть согласованным между обновлениями файлов. Таким образом, даже если все остальное в файле изменится, значение guid для атрибута должно остаться прежним.

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

Таким образом, другой подход заключался в том, чтобы сделать Guid одинаковым на основе пути к файлу. Поскольку наши пути к файлам и структура каталогов приложения уникальны, Guid должен быть уникальным для этого пути. Таким образом, каждый раз, когда мы запускаем обновление, файл получает один и тот же идентификатор в зависимости от его пути. Я нашел один классный способ сгенерировать такие 'Детерминированные гиды '(Спасибо Элтону Стоунману). В основном он делает это:

private Guid GetDeterministicGuid(string input) 

{ 

//use MD5 hash to get a 16-byte hash of the string: 

MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 

byte[] inputBytes = Encoding.Default.GetBytes(input); 

byte[] hashBytes = provider.ComputeHash(inputBytes); 

//generate a guid from the hash: 

Guid hashGuid = new Guid(hashBytes); 

return hashGuid; 

} 

Таким образом, для строки Guid всегда будет одним и тем же.

Есть ли другие подходы или рекомендуемые способы сделать это? Каковы плюсы и минусы этого метода?


person Punit Vora    schedule 15.04.2010    source источник


Ответы (5)


Как упоминалось @bacar, в разделе 4.3 RFC 4122 определяется способ создания UUID. Преимущество этого (по сравнению с использованием хеша MD5) заключается в том, что они гарантированно не будут конфликтовать с UUID, не основанными на именах, и имеют очень (очень) небольшую вероятность столкновения с другими UUID на основе имен.

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

Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);

Чтобы еще больше снизить риск конфликтов с другими идентификаторами GUID, вы можете создать частный идентификатор GUID для использования в качестве идентификатора пространства имен (вместо использования идентификатора пространства имен URL, определенного в RFC).

person Bradley Grainger    schedule 14.04.2011
comment
Обратите внимание, что, хотя это и полезно, реализация не совсем корректна для RFC4122, поэтому, если вы пытаетесь быть совместимым с другой реализацией, у вас возникнут проблемы (попробуйте пример в коде C в приложении RFC). - person porges; 10.07.2013
comment
@Porges: RFC4122 неверен и содержит опечатки, исправляющие код C (rfc -editor.org/errata_search.php?rfc=4122&eid=1352). Если эта реализация не полностью соответствует RFC4122 и содержащимся в ней исправлениям, пожалуйста, предоставьте дополнительные сведения; Я бы хотел, чтобы он соответствовал стандарту. - person Bradley Grainger; 10.07.2013
comment
@BradleyGrainger: Я этого не заметил, спасибо / извините! Я всегда должен не забывать проверять исправления при чтении RFC ... :) - person porges; 10.07.2013
comment
@Porges: Пожалуйста, нет проблем. Поразительно, что они не обновляют RFC на месте с исправлениями из опечаток. Даже ссылка в конце документа была бы гораздо полезнее, чем полагаться на то, что читатель не забудет поискать исправления (надеюсь, до написания реализации на основе RFC ...). - person Bradley Grainger; 10.07.2013
comment
@BradleyGrainger: если вы используете версию HTML, у нее есть ссылка на ошибку из заголовка, например tools.ietf.org/html/rfc4122. Интересно, есть ли расширение браузера, которое всегда перенаправляет на версию HTML ... - person porges; 11.07.2013
comment
Вам следует подумать о том, чтобы внести свой вклад в .NET. Репозиторий .NET находится здесь: github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System - person sapphiremirage; 15.06.2017
comment
Github был идеальным для меня, спасибо. Эта суть является копией модификаций, которые я сделал, чтобы удалить все ненужные части, не относящиеся к руководствам пространства имен. gist.github.com/angularsen/92a3ba9d9a94d250accd257f9f5a3d54 - person angularsen; 24.11.2020

Это преобразует любую строку в Guid без необходимости импорта внешней сборки.

public static Guid ToGuid(string src)
{
    byte[] stringbytes = Encoding.UTF8.GetBytes(src);
    byte[] hashedBytes = new System.Security.Cryptography
        .SHA1CryptoServiceProvider()
        .ComputeHash(stringbytes);
    Array.Resize(ref hashedBytes, 16);
    return new Guid(hashedBytes);
}

Есть гораздо лучшие способы сгенерировать уникальный Guid, но это способ последовательного обновления строкового ключа данных до ключа данных Guid.

person Ben Gripka    schedule 21.02.2012
comment
Считал, что этот фрагмент полезен при использовании уникального идентификатора в базе данных для федеративного распространения. - person Gleno; 18.12.2012
comment
Предупреждение! Этот код не генерирует действительные Guids / UUID (как также упоминается ниже бакар). Неправильно указаны ни версия, ни поле типа. - person MarkusSchaber; 31.03.2016
comment
Разве не было бы столь же эффективным использовать MD5CryptoServiceProvider вместо SHA1, поскольку длина MD5 уже составляет 16 байт? - person Brain2000; 14.04.2016

Как упоминает Роб, ваш метод не генерирует UUID, он генерирует хэш, который выглядит как UUID.

RFC 4122 по UUID, в частности, позволяет использовать детерминированные (основанные на имени) UUID. - Версии 3 и 5 используют md5 и SHA1 (соответственно). Большинство людей, вероятно, знакомы с версией 4, которая случайна. Википедия дает хороший обзор версий. (Обратите внимание, что использование слова «версия» здесь, кажется, описывает «тип» UUID - версия 5 не заменяет версию 4).

Кажется, существует несколько библиотек для генерации UUID версии 3/5, включая модуль uuid python, boost.uuid (C ++) и OSSP UUID. (Я не искал никаких .net)

person bacar    schedule 11.06.2010
comment
Это именно то, что нужно на оригинальном плакате. У UUID уже есть алгоритм, позволяющий начать со строки и преобразовать ее в GUID. UUID версии 3 хеширует строку с помощью MD5, а версия 5 хеширует ее с помощью SHA1. Важный момент при создании guid - сделать его уникальным по сравнению с другими GUID. Алгоритм определяет два бита, которые должны быть установлены, а также полубайт установлен на 3 или 5, в зависимости от версии 3 или 5. - person Ian Boyd; 04.04.2011
comment
Относительно использования слова «версия» в RFC 4122 §4.1.3 говорится: «Версия - это, точнее, подтип; опять же, мы сохраняем термин совместимость. - person Bradley Grainger; 14.04.2011
comment
Я разместил код C # для создания GUID v3 и v5 на GitHub: github.com/LogosBible/Logos.Utility/blob/master/src/ - person Bradley Grainger; 14.04.2011
comment
@BradleyGrainger, я получаю предупреждение Оператор побитового ИЛИ используется в операнде с расширенным знаком; сначала рассмотрите приведение к меньшему беззнаковому типу - person Sebastian; 13.11.2012
comment
@SebastianGodelet: Не могли бы вы быть более конкретными? Какой файл, какой номер строки, какую версию компилятора C # вы используете и т. Д.? Когда я создаю проект Logos.Utility (который находится на уровне предупреждения 4) в VS2012 Express, я получаю 0 предупреждений и 0 ошибок. - person Bradley Grainger; 14.11.2012
comment
@BradleyGrainger, Logos.Utility / src / Logos.Utility / GuidUtility.cs Строка 63 newGuid[6] = (byte) ((newGuid[6] & 0x0F) | (version << 4));, я думаю, что здесь жалуется R #, я изменил на: newGuid[6] = (byte) (newGuid[6] & 0x0F | (byte)version << 4); и теперь никаких предупреждений - person Sebastian; 14.11.2012
comment
Это уже не по теме! Предложите перенести отдельные отчеты об ошибках библиотеки на GitHub. - person bacar; 16.11.2012

Вам необходимо различать экземпляры класса Guid и идентификаторы, которые уникальны в глобальном масштабе. «Детерминированный гид» на самом деле является хешем (о чем свидетельствует ваш вызов provider.ComputeHash). Хэши имеют гораздо более высокую вероятность коллизий (две разные строки создают один и тот же хеш), чем Guid, созданный с помощью Guid.NewGuid.

Таким образом, проблема с вашим подходом заключается в том, что вы должны быть в порядке с возможностью того, что два разных пути будут создавать один и тот же GUID. Если вам нужен идентификатор, уникальный для любой заданной строки пути, проще всего использовать строку. Если вам нужно, чтобы строка была скрыта от пользователей, зашифруйте ее - вы можете использовать ROT13 или что-то более мощное ...

Попытка вставить что-то, что не является чистым GUID, в тип данных GUID может привести к проблемам с обслуживанием в будущем ...

person Rob Fonseca-Ensor    schedule 19.04.2010
comment
Вы утверждаете, что у хэшей гораздо больше шансов столкнуться ... чем у Guid, созданного с помощью Guid.NewGuid ... Вы можете подробнее рассказать об этом? С математической точки зрения количество битов, которое можно установить, одинаково, и как MD5, так и SHA1 являются криптографическими хэшами, специально разработанными для снижения вероятности (случайных и преднамеренных) хеш-коллизий. - person MarkusSchaber; 31.03.2016
comment
Я бы сказал, что основное отличие - это криптографическая карта хэшей из одного бесконечного пространства в другое фиксированное пространство с использованием функции. Визуализация хэша, который отображает строки переменной длины в 128 бит, тогда как Guid генерирует псевдослучайные 128 бит. Псевдослучайная генерация не полагается на начальный ввод, а, скорее, путем равномерной генерации вывода в пространстве вывода с использованием случайности, засеянной аппаратными средствами или другими способами. - person Thai Bui; 03.12.2019

MD5 слаб, я считаю, что вы можете сделать то же самое с SHA-1 и получить лучшие результаты.

Кстати, просто личное мнение, одевание хэша md5 в качестве GUID не делает его хорошим GUID. GUID по самой своей природе недетерминированы. это похоже на чит. Почему бы просто не назвать вещи своими именами и просто сказать, что это строковый хэш ввода. вы можете сделать это, используя эту строку, а не новую строку guid:

string stringHash = BitConverter.ToString(hashBytes)
person ryber    schedule 15.04.2010
comment
Спасибо за ваш вклад, но это все еще дает мне строку, и я ищу GUID ... - person Punit Vora; 19.04.2010
comment
Хорошо, назовите свой хэш GUID, проблема решена. Или настоящая проблема в том, что вам нужен объект Guid? - person user7116; 19.04.2010
comment
я бы хотел, чтобы это было так просто .. :) но да, мне нужен объект 'GUID' - person Punit Vora; 19.04.2010
comment
GUID по самой своей природе недетерминированы - это верно только для определенных типов («версий») GUID. Однако я согласен с тем, что использование хэша md5 в качестве идентификатора GUID не является хорошим идентификатором GUID по другим причинам, как указано в @Bradley Grainger и @Rob Fonseca-Ensor, а также в моем ответе на этот вопрос. - person bacar; 06.05.2011