2011-01-03 13 views
1

Dies ist eine Folge zu this question.Seriennummerngenerierung ohne Benutzerdaten

Die akzeptierte Antwort ist im Allgemeinen ausreichend, erfordert jedoch, dass der Benutzer persönliche Informationen (z. B. Name) zum Erzeugen des Schlüssels bereitstellt.

Ich frage mich, ob es möglich ist, verschiedene Schlüssel basierend auf einem gemeinsamen Startwert zu generieren, so dass das Programm prüfen kann, ob diese Schlüssel zu bestimmten Produkten gehören, aber ohne dass dieser Prozess für den Endbenutzer offensichtlich wird.

Ich meine, es könnte ein Hash der Produkt-ID plus einige zufällige Folge von Zeichen sein, aber das würde den Benutzer erlauben, potenzielle neue Schlüssel zu erraten. Es sollte eine Art Algorithmus geben, der schwer zu erraten ist.

Antwort

6

Das Problem mit symmetrischen/asymmetrischen/Hash-Standardalgorithmen (MDx, SHAx, RSA usw.) ist, dass sie nicht mit sehr kurzen Strings wie 20/30 Zeichen langen Product Key Strings arbeiten können. Wenn Sie Ihren Benutzern ~ 1000 Zeichen lange Zeichenfolgen geben können, dann würde ich für diese Algorithmen gehen.

Wenn Sie nicht können, ist hier ein C# -Code, der in der Lage ist, einen "fast-total-zufälligen" kurzen Product Key zu erstellen, aber mit einem versteckten Byte darin. Sie können alles, was Sie wollen, in dieses "versteckte Byte" setzen und der Schlüssel folgt einem Algorithmus, der nicht so einfach zu erraten ist. Sie können eine in String übergeben validieren & das Byte wieder gelesen das TryReadKey() wie folgt aus:

string key = BuildKey(myHiddenByte); // give that to the end user 
... 
byte hidden; 
if (!TryReadKey(key, out hidden)) 
{ 
    Console.WriteLine("key is not ok"); 
} 
else 
{ 
    Console.WriteLine("key is ok and hidden byte is " + hidden); 
} 

Der Algorithmus verwendet Steganography Prinzipien, ist sicherlich nicht kugelsicher und verbessert werden könnte, kann aber nützlich sein. Beachten Sie, dass die Schlüsselgenerierung einige Zeit in Anspruch nehmen kann. Es produziert Tasten wie folgt aus (beachten Sie das erste Wort 6 Zeichen lang ist, sind die anderen fünf Zeichen lang):

KZGMB0-XYJC2-YRKH3-8LD8G-5FUZG 
YKU93K-M34PD-E5PL0-QM91J-QLDZF 
DH27H9-NCW8E-EMGPL-YCJXJ-N2PRG 
WDAKDE-G56NR-6BA3R-0JE6U-625EB 
6D5EJ0-NJDAK-EMGZR-Z6ZDF-JHJGF 
.... 

Hier ist der Code:

// need 32 characters, so we can use a 5-bit base encoding 
    //        0   1   2   3 
    //       
    private const string KeyChars = "ABCDEFGHJKLMNPQRTUWXYZ"; 
    private const int MinSum = 2000; 
    private const int Mask = 0xFFFFF; // beware, this can have a dramatic influence on performance 

    /// <summary> 
    /// Builds a key and hides data in it. 
    /// Key format is XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX. 
    /// </summary> 
    /// <param name="hiddenData">The hidden data.</param> 
    /// <returns>The build key</returns> 
    public static string BuildKey(byte hiddenData) 
    { 
     int index; 
     Guid guid; 
     uint[] word = new uint[4]; 
     byte[] array; 
     while (true) 
     { 
      // we use guid initial randomness characteristics 
      guid = Guid.NewGuid(); 
      array = guid.ToByteArray(); 

      int sum = array.Aggregate(0, (current, b) => current + b); 

      // a simple check to make sure the guid is not filled with too much zeros... 
      if (sum < MinSum) 
       continue; 

      // build a 32-bit word array 
      for (int i = 0; i < 4; i++) 
      { 
       word[i] = BitConverter.ToUInt32(array, i * 4) & Mask; 
      } 

      // Here we check the guid follows some algorithm. if it doesn't we skip it 
      // the algorithm presented here is to check one of the 32-bit word is equal to the sum of the others 
      // (modulo a mask otherwise it takes too long!) 
      // 
      // This algorithm can be changed at your will (and change the TryReadKey as well) 

      if ((word[0] + word[1] + word[2]) == word[3]) 
      { 
       index = 3; 
       break; 
      } 

      if ((word[0] + word[1] + word[3]) == word[2]) 
      { 
       index = 2; 
       break; 
      } 

      if ((word[0] + word[3] + word[2]) == word[1]) 
      { 
       index = 1; 
       break; 
      } 

      if ((word[3] + word[1] + word[2]) == word[0]) 
      { 
       index = 0; 
       break; 
      } 
     } 

     // hidden info is also xor'd with other words hi order (except the one we masked) 
     // so it's not too easy to guess 
     hiddenData = (byte)(hiddenData^array[0]^array[4]^array[8]^array[12]); 

     // rebuild words without mask 
     for (int i = 0; i < 4; i++) 
     { 
      word[i] = BitConverter.ToUInt32(array, i * 4); 
     } 

     // hidden info is stored in the block that is the sum of other blocks, in hi order byte (outside the mask) 
     word[index] &= 0x00FFFFFF; 
     word[index] |= ((uint)hiddenData) << 24; 

     // rebuild the array back 
     for (int i = 0; i < 4; i++) 
     { 
      byte[] ui = BitConverter.GetBytes(word[i]); 
      for (int j = 0; j < 4; j++) 
      { 
       array[i * 4 + j] = ui[j]; 
      } 
     } 

     // now use 5-bits encoding 
     int encodingBitIndex = 0; 
     StringBuilder key = new StringBuilder(); 
     while (encodingBitIndex < 128) 
     { 
      byte encodingByte = Get5Bits(array, encodingBitIndex); 
      if ((key.Length > 0) && (key.Length % 6) == 0) 
      { 
       key.Append('-'); 
      } 
      key.Append(KeyChars[encodingByte]); 
      encodingBitIndex += 5; 
     } 
     return key.ToString(); 
    } 

    /// <summary> 
    /// Validates the specified key and reads hidden data from it. 
    /// </summary> 
    /// <param name="key">The key.</param> 
    /// <param name="hiddenData">The hidden data.</param> 
    /// <returns>true if the key is valid and hidden data was read; false otherwise.</returns> 
    public static bool TryReadKey(string key, out byte hiddenData) 
    { 
     hiddenData = 0; 
     if (key == null) 
      return false; 

     key = key.Replace("-", string.Empty); 
     if (key.Length != 26) 
      return false; 

     byte[] array = new byte[16]; 
     int encodingBitIndex = 0; 
     foreach (char t in key) 
     { 
      byte b = 255; 
      for (byte k = 0; k < KeyChars.Length; k++) 
      { 
       if (KeyChars[k] != t) continue; 
       b = k; 
       break; 
      } 
      if (b == 255) // char not found 
       return false; 

      Put5Bits(array, encodingBitIndex, b); 
      encodingBitIndex += 5; 
     } 

     // quick sum check 
     int sum = array.Aggregate(0, (current, b) => current + b); 

     // add 256 because we changed the hidden byte 
     sum += 256; 
     if (sum < MinSum) 
      return false; 

     uint[] word = new uint[4]; 
     for (int i = 0; i < 4; i++) 
     { 
      word[i] = BitConverter.ToUInt32(array, i * 4) & Mask; 
     } 

     // This must match BuildKey algorithm 

     int index; 
     if ((word[0] + word[1] + word[2]) == word[3]) 
     { 
      index = 3; 
     } 
     else if ((word[0] + word[1] + word[3]) == word[2]) 
     { 
      index = 2; 
     } 
     else if ((word[0] + word[3] + word[2]) == word[1]) 
     { 
      index = 1; 
     } 
     else if ((word[3] + word[1] + word[2]) == word[0]) 
     { 
      index = 0; 
     } 
     else 
      return false; 

     // reread word & extract hidden byte back 
     word[index] = BitConverter.ToUInt32(array, index * 4); 
     hiddenData = (byte)(word[index] >> 24); 
     hiddenData = (byte)(hiddenData^array[0]^array[4]^array[8]^array[12]); 
     return true; 
    } 

    // get 5 bits from a byte buffer at an arbitrary bit index 
    private static byte Get5Bits(byte[] buffer, int bitIndex) 
    { 
     int r = bitIndex % 8; 
     if (r < 4) 
      return (byte)(((buffer[bitIndex/8]) & (0xFF >> r)) >> (3 - r)); 

     byte b0 = (byte)((buffer[bitIndex/8] & (0xFF >> r)) << (r - 3)); 
     if ((1 + (bitIndex/8)) == buffer.Length) // last 
      return (byte)(buffer[buffer.Length - 1] & 0x7); 

     if ((bitIndex/8) < 16) 
      return (byte)(b0 | buffer[1 + (bitIndex/8)] >> (11 - r)); 

     return b0; 
    } 

    // put 5 bits into a byte buffer at an arbitrary bit index 
    private static void Put5Bits(byte[] buffer, int bitIndex, byte value) 
    { 
     int r = bitIndex % 8; 
     if (r < 4) 
     { 
      buffer[bitIndex/8] |= (byte)((value << (3 - r))); 
     } 
     else 
     { 
      if ((1 + (bitIndex/8)) == buffer.Length) // last 
      { 
       buffer[buffer.Length - 1] |= (byte)(value & 0x7); 
      } 
      else if ((bitIndex/8) < 16) 
      { 
       buffer[bitIndex/8] |= (byte)((value >> (r - 3))); 
       buffer[1 + (bitIndex/8)] |= (byte)((value << (11 - r))); 
      } 
     } 
    } 
+0

Das sieht aus wie Lösung, die ich suchte. Danke Simon! – SharpAffair

0

Sie können eine digitale Signatur verwenden, die von Ihrem Schlüssel generiert wird und mit einem öffentlichen Schlüssel in Ihr Programm eingefügt wird.