결정적 가이드를 만드는 방법


103

우리의 응용 프로그램에서 우리는 Guid 값을 가진 속성을 가진 Xml 파일을 만들고 있습니다. 이 값은 파일 업그레이드간에 일관성이 있어야했습니다. 따라서 파일의 다른 모든 항목이 변경 되더라도 속성의 guid 값은 동일하게 유지되어야합니다.

한 가지 분명한 해결책은 파일 이름과 사용할 Guid로 정적 사전을 만드는 것이 었습니다. 그런 다음 파일을 생성 할 때마다 파일 이름에 대한 사전을 찾고 해당 guid를 사용합니다. 그러나 이것은 100 개의 파일로 확장 할 수 있고 GUID의 큰 목록을 유지하고 싶지 않기 때문에 실현 가능하지 않습니다.

그래서 또 다른 접근 방식은 파일 경로를 기반으로 Guid를 동일하게 만드는 것입니다. 파일 경로와 응용 프로그램 디렉토리 구조는 고유하므로 Guid는 해당 경로에 대해 고유해야합니다. 따라서 업그레이드를 실행할 때마다 파일은 경로에 따라 동일한 GUID를 가져옵니다. 그런 ' 결정적 가이드 ' 를 생성하는 멋진 방법을 찾았습니다 (Elton Stoneman에게 감사합니다). 기본적으로 다음과 같이합니다.

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는 항상 동일합니다.

이를 수행하는 다른 접근 방식이나 권장되는 방법이 있습니까? 그 방법의 장단점은 무엇입니까?

답변:


151

@bacar에서 언급했듯이 RFC 4122 §4.3 은 이름 기반 UUID를 만드는 방법을 정의합니다. (MD5 해시를 사용하는 것보다)이 작업의 장점은 이름이 지정되지 않은 UUID와 충돌하지 않으며 다른 이름 기반 UUID와 충돌 할 가능성이 매우 적다는 것입니다.

.NET Framework에서 이러한 생성을위한 기본 지원은 없지만 알고리즘을 구현하는 코드를 GitHub에 게시했습니다 . 다음과 같이 사용할 수 있습니다.

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

다른 GUID와의 충돌 위험을 더욱 줄이기 위해 RFC에 정의 된 URL 네임 스페이스 ID를 사용하는 대신 네임 스페이스 ID로 사용할 개인 GUID를 만들 수 있습니다.


5
@Porges : RFC4122가 잘못되었으며 C 코드를 수정하는 오류가 있습니다 ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). 이 구현이 RFC4122 및 해당 정오표를 완전히 준수하지 않는 경우 추가 세부 정보를 제공하십시오. 표준을 따르고 싶습니다.
Bradley Grainger

1
@BradleyGrainger : 나는 그것을 알아 차리지 못했습니다. RFC를 읽을 때 정오표를 확인하는 것을 항상 기억해야합니다 ... :)
porges

3
@Porges : 천만에요 / 문제 없습니다. 정오표에서 수정 한 내용으로 RFC를 제자리에서 업데이트하지 않는다는 생각이 들었습니다. 문서 끝에있는 링크조차도 독자가 정오표를 검색하는 것을 기억하는 것보다 훨씬 더 도움이 될 것입니다 ( RFC 기반 구현을 작성 하기 전에 ...).
Bradley Grainger

1
@BradleyGrainger : HTML 버전을 사용하는 경우 헤더에서 정오표에 대한 링크가 있습니다 (예 : tools.ietf.org/html/rfc4122) . 항상 HTML 버전으로 리디렉션하는 브라우저 확장이 있는지 궁금 ...
porges

2
여기 당신은 .NET .NET REPO이 기여 고려해야입니다 : github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System
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를 생성하지 않습니다 (아래에서도 언급 한 bacar). 버전 및 유형 필드가 올바르게 설정되지 않았습니다.
MarkusSchaber

3
MD5의 길이가 이미 16 바이트이므로 SHA1 대신 MD5CryptoServiceProvider를 사용하는 것이 효과적이지 않습니까?
Brain2000

20

Rob이 언급했듯이 메서드는 UUID를 생성하지 않고 UUID처럼 보이는 해시를 생성합니다.

UUID 의 RFC 4122 는 특히 결정적 (이름 기반) UUID를 허용합니다. 버전 3 및 5는 md5 및 SHA1 (각각)을 사용합니다. 대부분의 사람들은 아마도 무작위 버전 4에 익숙 할 것입니다. Wikipedia 는 버전에 대한 좋은 개요를 제공합니다. (여기서 '버전'이라는 단어의 사용은 UUID의 '유형'을 설명하는 것처럼 보입니다. 버전 5는 버전 4를 대체하지 않습니다.)

python uuid 모듈 , boost.uuid (C ++) 및 OSSP UUID를 포함하여 버전 3/5 UUID를 생성하기위한 몇 가지 라이브러리가있는 것 같습니다 . (나는 .net을 찾지 못했습니다)


1
이것이 바로 원래 포스터가 추구하는 것입니다. UUID에는 문자열로 시작하여 GUID로 변환하는 알고리즘이 이미 있습니다. UUID 버전 3은 MD5로 문자열을 해시하고 버전 5는 SHA1로 문자열을 해시합니다. "guid"를 만드는 데있어 중요한 점은 다른 GUID에 대해 "고유"하게 만드는 것입니다. 알고리즘은 설정해야하는 두 비트를 정의하고 버전 3 또는 5인지에 따라 니블이 3 또는 5로 설정됩니다.
Ian Boyd

2
"버전"이라는 단어의 사용과 관련하여 RFC 4122 §4.1.3은 "버전이보다 정확하게 하위 유형이며 호환성이라는 용어를 유지합니다."라고 말합니다.
Bradley Grainger 2011


@BradleyGrainger, 부호 확장 피연산자에 사용되는 경고 비트 또는 연산자가 표시됩니다. 먼저 작은 부호없는 타입으로 캐스팅을 고려
세바스찬

1
이것은 주제에서 벗어나고 있습니다! 개별 lib 버그 보고서를 GitHub로 이동할 것을 제안합니다.
bacar

3

클래스의 인스턴스 Guid와 전역 적으로 고유 한 식별자를 구분해야합니다. "결정적 GUID"는 실제로 해시입니다 (를 호출하여 입증 됨 provider.ComputeHash). 해시는를 통해 생성 된 Guid보다 충돌 (동일한 해시를 생성하기 위해 발생하는 두 개의 다른 문자열) 가능성이 훨씬 높습니다 Guid.NewGuid.

따라서 접근 방식의 문제는 두 개의 다른 경로가 동일한 GUID를 생성 할 가능성에 대해 괜찮아 야한다는 것입니다. 주어진 경로 문자열에 대해 고유 한 식별자가 필요한 경우 가장 쉬운 방법 은 문자열을 사용하는 것 입니다. 사용자에게 가려진 문자열이 필요한 경우 암호화하십시오. ROT13 또는 더 강력한 것을 사용할 수 있습니다.

순수한 GUID가 아닌 것을 GUID 데이터 유형에 통합하려고하면 향후 유지 관리 문제가 발생할 수 있습니다.


2
"해시는 Guid.NewGuid를 통해 생성 된 Guid보다 충돌 가능성이 훨씬 더 높습니다."라고 주장합니다. 그것에 대해 자세히 설명해 주시겠습니까? 수학적 관점에서 볼 때 설정할 수있는 비트 수는 동일하며 MD5와 SHA1은 모두 (우연 및 의도적) 해시 충돌 가능성을 낮추기 위해 특별히 설계된 암호화 해시입니다.
MarkusSchaber

주요 차이점은 함수를 사용하여 하나의 무한 공간에서 다른 고정 공간으로의 암호화 해시 맵입니다. 가변 길이 문자열을 128 비트로 매핑하는 해시를 이미징하는 반면 Guid는 의사 랜덤 128 비트를 생성합니다. 의사 난수 생성은 초기 입력에 의존하지 않고 하드웨어 또는 기타 수단에서 시드 된 임의성을 사용하여 출력 공간에서 균일하게 출력을 생성하는 방식입니다.
Thai Bui

2

MD5는 약합니다. SHA-1로도 똑같은 일을하고 더 나은 결과를 얻을 수 있다고 믿습니다.

BTW는 개인적인 의견 일뿐 md5 해시를 GUID로 꾸미는 것은 좋은 GUID가되지 않습니다. 본질적으로 GUID는 비 결정적입니다. 이것은 속임수처럼 느껴집니다. 스페이드를 스페이드라고 부르고 입력의 문자열로 렌더링 된 해시라고 말하면 안됩니다. 새로운 guid 라인 대신 다음 라인을 사용하여이를 수행 할 수 있습니다.

string stringHash = BitConverter.ToString(hashBytes)

입력 해 주셔서 감사합니다.하지만 여전히 문자열을 제공하고 GUID를 찾고 있습니다.
Punit Vora

좋습니다. 해시를 "GUID"라고 부르세요. 문제가 해결되었습니다. 아니면 진짜 문제는 당신이 있다는 것입니다 필요Guid 객체를?
user7116

나는 그것이 그렇게 간단했으면 좋겠다 .. :) 그래, 나는 'GUID'객체가 필요하다
Punit Vora

5
"본질 상 GUID는 결정적이지 않습니다."-이는 특정 유형의 GUID ( '버전')에만 해당됩니다. 그러나 나는 @Bradley Grainger와 @Rob Fonseca-Ensor가 작성한 다른 이유로 "md5 해시를 GUID로 꾸미는 것은 좋은 GUID를 만들지 않는다"는 데 동의합니다. 그리고이 질문에 대한 제 답변입니다.
bacar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.