2013-08-21 22 views
5

Ho questo codice per generare un hash SHA-1:Come posso generare un Guid da un array di byte SHA-1?

SHA1 sha1 = SHA1CryptoServiceProvider.Create(); 
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString); 
Byte[] hash = sha1.ComputeHash(myStringBytes); 

C'è un modo per trasformare hash in un Guid (tipo 5, immagino, per essere coerenti con SHA-1)?

+0

Questo è un pò fuorviante in quanto un hash e un GUID sono cose diverse. Un guid dovrebbe darti una stringa unica/diversa ogni volta che richiedi un nuovo guid. Gli hash ti danno risultati coerenti basati sullo stesso input. – Justin

+0

Sarà un GUID deterministico e ripetibile, basato su un hash, che intende generare valori univoci a seconda dell'input. Maggiori informazioni: http://waterjuice.org/2013/06/type-3-and-5-guids/ –

+3

@Justin: stai fraintendendo ciò che l'OP sta chiedendo. Un GUID non è altro che un identificatore univoco globale; questo è ciò che GUID sta per. Esistono molti modi per generare un GUID. Ad esempio, è possibile creare un GUID in base all'ora e alla posizione correnti; questo è globalmente unico. (Principalmente). Questo è un GUID di tipo uno. È possibile creare un GUID statisticamente univoco con un generatore di numeri casuali; questo è un GUID di tipo quattro. Un hash crittograficamente forte ** di una stringa univoca ** è anche una fonte di univocità e può essere utilizzato per costruire un GUID. –

risposta

4

È possibile utilizzare questo C# code basato su rfc4122.

Per evitare link rot, un certo codice qui:

public static Guid Create(Guid namespaceId, string name) 
{ 
    if (name == null) 
     throw new ArgumentNullException("name"); 

    // convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3) 
    // ASSUME: UTF-8 encoding is always appropriate 
    byte[] nameBytes = Encoding.UTF8.GetBytes(name); 

    // convert the namespace UUID to network order (step 3) 
    byte[] namespaceBytes = namespaceId.ToByteArray(); 
    SwapByteOrder(namespaceBytes); 

    // comput the hash of the name space ID concatenated with the name (step 4) 
    byte[] hash; 
    using (HashAlgorithm algorithm = SHA1.Create()) 
    { 
     algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0); 
     algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); 
     hash = algorithm.Hash; 
    } 

    // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12) 
    byte[] newGuid = new byte[16]; 
    Array.Copy(hash, 0, newGuid, 0, 16); 

    // set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8) 
    newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4)); 

    // set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10) 
    newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80); 

    // convert the resulting UUID to local byte order (step 13) 
    SwapByteOrder(newGuid); 
    return new Guid(newGuid); 
} 

/// <summary> 
/// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). 
/// </summary> 
public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); 

/// <summary> 
/// The namespace for URLs (from RFC 4122, Appendix C). 
/// </summary> 
public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); 

/// <summary> 
/// The namespace for ISO OIDs (from RFC 4122, Appendix C). 
/// </summary> 
public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); 

// Converts a GUID (expressed as a byte array) to/from network order (MSB-first). 
internal static void SwapByteOrder(byte[] guid) 
{ 
    SwapBytes(guid, 0, 3); 
    SwapBytes(guid, 1, 2); 
    SwapBytes(guid, 4, 5); 
    SwapBytes(guid, 6, 7); 
} 

private static void SwapBytes(byte[] guid, int left, int right) 
{ 
    byte temp = guid[left]; 
    guid[left] = guid[right]; 
    guid[right] = temp; 
} 
2

Come sottolineato da Justin, un Guid dovrebbe essere unico ogni volta, mentre un hash darà sempre un risultato coerente per lo stesso valore.

Ora mi piacerebbe aggiungere a questo e dire che sia Guidi che gli hash (la maggior parte, se non tutti gli algoritmi) sono soggetti a collisioni, anche se il mio istinto è che l'hashing è soggetto a un livello maggiore di collisioni rispetto a Guids ... anche se questo potrebbe essere soggetto alle dimensioni dell'hash (cioè 128 bit, 256 bit, 512 bit, ecc.).

Un altro problema che si presenterà è che il byte[] da un hash SHA1 è lungo 20 byte, mentre un Guid è lungo 16 byte, pertanto, la creazione di un Guid da un hash SHA1 non sarà accurata.

Esempio:

string myString = "Hello World"; 
SHA1 sha1 = SHA1CryptoServiceProvider.Create(); 
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString); 
Byte[] hash = sha1.ComputeHash(myStringBytes); 
Console.WriteLine(new Guid(hash.Take(16).ToArray())); 

L'esempio precedente creerà un GUID dal hash, anche se utilizza LINQ per ottenere 16 byte dalla matrice hash (da cui l'inesattezza ... gli ultimi 4 byte sono semplicemente omesso)

MD5 è un hash da 16 byte, quindi sembra che potrebbe essere più adatto per la conversione a Guid di SHA1.

Esempio:

string myString = "Hello World"; 
MD5 md5 = MD5.Create(); 
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString); 
Byte[] hash = md5.ComputeHash(myStringBytes); 
Console.WriteLine(new Guid(hash)); 

Questo produce un accurato Guid dal hash MD5, anche se io stato, tutto questo è, è una rappresentazione GUID del hash MD5 ... non ci dovrebbe essere alcun effettivo modifica dei dati byte[].

+0

In primo luogo, si sta fraintendendo il motivo per cui è ragionevole utilizzare un hash crittografico ** di una stringa univoca ** per generare un GUID. Vedi il mio commento sopra. Secondo, questo codice è sbagliato; non imposta il numero di versione del GUID, che è un requisito documentato del formato GUID. –

+0

@EricLippert, Condider il testo qui: http://en.wikipedia.org/wiki/Universally_unique_identifier#Version_5_.28SHA-1_hash.29 - Dato che questo indica che un GUIDv5 è SHA1 che è stato troncato da 160 bit a 128 bit, I immagino che il mio primo esempio di codice sia più vicino al requisito dell'OP del mio secondo esempio? - Presumibilmente è necessario un po 'più di ri-lavoro per impostare anche il numero di versione? – series0ne

+0

Questo è corretto. –