2011-01-03 8 views
1

これはthis questionのフォローアップです。ユーザーデータのないシリアル番号の生成

一般的に受け入れられた回答で十分ですが、ユーザーはキーを生成するための個人情報(例:名前)を入力する必要があります。

一般的なシードに基づいて異なるキーを生成することが可能かどうか、プログラムがその特定の製品に属しているかどうかを確認する方法で、このプロセスをエンドユーザーに明らかにさせることはできません。

つまり、プロダクトIDにいくつかのランダムな文字列を加えたハッシュになる可能性がありますが、ユーザーは新しいキーを推測することができます。推測が難しいアルゴリズムがあるはずです。

答えて

6

標準的な対称/非対称/ハッシュアルゴリズム(MDx、SHAx、RSAなど)の問題は、20/30文字の長いプロダクトキー文字列のような非常に短い文字列では動作できないということです。あなたのユーザーに〜1000文字の長い文字列を与えることができれば、これらのアルゴリズムに行くだろう。

できない場合は、「ほぼ完全にランダム」な短いプロダクトキーを作成することができるが、その中に隠しバイトがあるC#コードがあります。あなたは "隠されたバイト"にあなたが望むものを置くことができ、キーは推測しにくいアルゴリズムに従います。あなたは、文字列&に渡され、このようなTryReadKey()を使用してバイトを再読検証することができます。

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); 
} 

アルゴリズムはSteganography原則を使用していますが、確かに防弾ではなく、改善することができますが、便利です。鍵の生成には時間がかかることがありますが、それは正常です。これは、(他の5文字の長、6文字の長さで最初の単語に注意してください)このようなキーを生成します。ここでは

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 
.... 

はコードです:

// 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

これは私が探していたソリューションのように見えます。ありがとうサイモン! – SharpAffair

0

鍵で生成されたデジタル署名を公開鍵でプログラムに挿入することができます。

関連する問題