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)));
}
}
}
Das sieht aus wie Lösung, die ich suchte. Danke Simon! – SharpAffair