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


107

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

Одним из очевидных решений было создание статического словаря с именем файла и Guids, которые будут использоваться для них. Затем всякий раз, когда мы генерируем файл, мы ищем в словаре имя файла и используем соответствующий 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 всегда будет одним и тем же.

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

Ответы:


154

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

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

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

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


5
@Porges: RFC4122 неверен и содержит опечатки, исправляющие код C ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Если эта реализация не полностью соответствует RFC4122 и содержащимся в ней исправлениям, пожалуйста, предоставьте дополнительные сведения; Я бы хотел, чтобы он соответствовал стандарту.
— Брэдли Грейнджер

1
@BradleyGrainger: Я этого не заметил, спасибо / извините! Я всегда должен не забывать проверять исправления при чтении RFC ... :)
— porges

3
@Porges: Пожалуйста / без проблем. Поразительно, что они не обновляют RFC на месте с исправлениями из опечаток. Даже ссылка в конце документа была бы намного полезнее, чем полагаться на то, что читатель вспомнит о поиске ошибок (надеюсь, перед написанием реализации на основе RFC ...).
— Брэдли Грейнджер

1
@BradleyGrainger: если вы используете версию HTML, у нее есть ссылка на ошибку из заголовка, например tools.ietf.org/html/rfc4122 . Интересно, есть ли расширение для браузера, чтобы всегда перенаправлять на версию HTML ...
— porges

3
Вам следует подумать о том, чтобы внести свой вклад в .NET.
— sapphiremirage

29

Это преобразует любую строку в 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.


Этот фрагмент кода оказался полезным при использовании уникального идентификатора в базе данных для федеративного распространения.
— Gleno

6
Предупреждение! Этот код не генерирует действительные Guids / UUID (как также упоминается ниже бакар). Ни версия, ни поле типа не установлены правильно.
— MarkusSchaber

3
Разве не было бы столь же эффективным использовать MD5CryptoServiceProvider вместо SHA1, поскольку длина MD5 уже составляет 16 байт?
— Brain2000,

21

Как упоминает Роб, ваш метод не генерирует 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)


1
Это именно то, что нужно для оригинального плаката. У UUID уже есть алгоритм, позволяющий начать со строки и преобразовать ее в GUID. UUID версии 3 хеширует строку с помощью MD5, а версия 5 хеширует ее с помощью SHA1. Важным моментом при создании «guid» является сделать его «уникальным» по сравнению с другими GUID. Алгоритм определяет два бита, которые необходимо установить, а также один полубайт, установленный на 3 или 5, в зависимости от версии 3 или 5.
— Ян Бойд,

2
Что касается использования слова «версия», RFC 4122 §4.1.3 гласит: «Версия более точно является подтипом; опять же, мы сохраняем термин для совместимости».
— Брэдли Грейнджер,

11
Я разместил код C # для создания GUID v3 и v5 на GitHub: github.com/LogosBible/Logos.Utility/blob/master/src/…
— Брэдли Грейнджер,

@BradleyGrainger, я получаю предупреждение Побитовый оператор ИЛИ используется в операнде с расширенным знаком; сначала рассмотрите приведение к меньшему беззнаковому типу
— Себастьян

1
Это уже не по теме! Предложите перенести отдельные отчеты об ошибках библиотеки на GitHub.
— bacar

3

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

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

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


2
Вы утверждаете, что «хэши имеют гораздо более высокую вероятность столкновения ... чем Guid, созданный с помощью Guid.NewGuid.». Вы можете подробнее рассказать об этом? С математической точки зрения количество битов, которое можно установить, одинаково, и как MD5, так и SHA1 являются криптографическими хэшами, специально разработанными для снижения вероятности (случайных и преднамеренных) хеш-коллизий.
— MarkusSchaber

Я бы сказал, что основное отличие - это криптографическая карта хэшей из одного бесконечного пространства в другое фиксированное пространство с использованием функции. Визуализация хэша, который отображает строки переменной длины в 128 бит, тогда как Guid генерирует псевдослучайные 128 бит. Псевдослучайная генерация не зависит от начального ввода, а, скорее, путем равномерной генерации вывода в пространстве вывода с использованием случайности, засеянной аппаратными средствами или другими способами.
— Thai Bui,

1

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

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

string stringHash = BitConverter.ToString(hashBytes)

Спасибо за ваш вклад, но это все еще дает мне строку, и я ищу GUID ...
— Пунит Вора,

Хорошо, назовите свой хэш "GUID", проблема решена. Или это реальная проблема , что вы нужны в Guidобъект?
— user7116

я бы хотел, чтобы это было так просто .. :) но да, мне нужен объект 'GUID'
— Пунит Вора

6
«GUID по самой своей природе недетерминированы» - это верно только для определенных типов («версий») GUID. Однако я согласен с тем, что «одевание хэша md5 в качестве GUID не дает хорошего GUID» по другим причинам, изложенным @Bradley Grainger и @Rob Fonseca-Ensor, и мой ответ на этот вопрос.
— бакар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.