使用序列号时需要考虑一些事情。
在我的搜索中,我看到了一些链接,指向使用简单的Guid.NewGuid();方法,然后对字符串进行一些转换,以制作自定义样式的序列号。这很容易做到,但是将责任放在您产品所有者身上,需要在数据库中跟踪序列号,并且在一天结束时,有可能会有人通过使用Guid.NewGuid();自己找到有效的序列号。如果地球上的每个人都开始同时生成Guids,则碰撞机会变得非常大。
有一种解决方案可以通过在Guid.NewGuid();之上使用更复杂的算法来使碰撞事件变得不太可能。
为此,我倾向于使用:
- Guid.NewGuid();(仅前16个字符,减去-(连字符)
- 一个不断增加或更改的值。(Nonce)(int将起作用:i++等)
- 您将在网络中保持私有和安全的秘密盐。
- 难度因素:从比特币借用这个原则。
好的,让我们想象一下我正在从GUID中获取前16位数字。
然后将其与Nonce和秘密盐结合,
然后使用SHA256从这些值中派生哈希。
然后,我可以使用难度因子来确定哈希是否以我所需的0或其他字符开头。
例如:如果哈希值有六个0作为前缀,则我会保存所有数据,因为我刚刚找到了一个相当安全的序列号。
当我说安全时,我的意思是我已经找到了一个序列号,当与产品密钥(Nonce)组合并与秘密盐一起使用时,它会产生符合我的生产标准的哈希。
以下是一些示例代码-非常粗略,因为我很无聊。
想法是您的应用程序可以将产品密钥和序列号发送到激活服务器。服务器知道秘密盐。然后,它返回true或false以确定生成的哈希是否符合安全要求。
如果不符合:则序列号无效,或者不适用于提供的密钥。
如果具有所需的0:则为有效序列号。
Guid theGuid;
string Hash = "";
int iAccess = 0;
string PrivateSalt = "Alpha";
string SourceString = "";
string guidString;
while (true)
{
theGuid = Guid.NewGuid();
guidString = theGuid.ToString().Replace("-", "").Substring(0,16);
SourceString = guidString + "|" + iAccess.ToString() + "|" + PrivateSalt;
byte[] data = Encoding.Default.GetBytes(SourceString);
Hash = Crypto.GenerateSHA256(data);
if (Hash.StartsWith(GetDiff()))
{
break;
}
iAccess++;
}
Console.WriteLine(SourceString+" Gives hash "+Hash);
string s1, s2, s3, s4;
s1 = guidString.Substring(0, 4);
s2 = guidString.Substring(4, 4);
s3 = guidString.Substring(8, 4);
s4 = guidString.Substring(12, 4);
string serial = s1 + "-" + s2 + "-" + s3 + "-" + s4;
Console.WriteLine(serial + " :" + SourceString + " Gives hash " + Hash);
GetDiff() 基本上只是一个字符串:"000000";
这个方法的示例输出如下:
d9c9-f6f0-45be-427a :d9c9f6f045be427a|15135|Alpha Gives hash 000000f718f69c8389d496e01d1e992946fe1b8cf72bc4200a7a2b800b40aa0a
fe49-70b9-08d8-40df :fe4970b908d840df|9096414|Alpha Gives hash 000000e29cfccfb54d1e7edc816feb084f1a2cd11a20c3132a965f9048fc9bf4
7f58-0636-c853-4f0a :7f580636c8534f0a|12297217|Alpha Gives hash 0000007bb44f39a964bbe985885451c3dc0e037fcd12951261404e48819bf89b
6f65-82d3-d95b-4882 :6f6582d3d95b4882|15064854|Alpha Gives hash 000000f1a3bed79e441108cfd26d8733d3fc10f5cd66d234ed35fe2b769663a3
edee-b8b7-9f6f-40ab :edeeb8b79f6f40ab|17782415|Alpha Gives hash 000000b70b96e7b008a96a860efc572fe868154ae81e67b9397249a51f2db71c
0948-4bb3-7de4-4054 :09484bb37de44054|21105690|Alpha Gives hash 000000ec7317eccd5fd9bb701759a2b0e77d37099347d9d665f4b492a69ca3ec
bbf5-5119-bf4e-463c :bbf55119bf4e463c|21715642|Alpha Gives hash 000000a134c886d01606da83cd5e8f672fddb6aa061968e9f08202c781514b16
80f6-c9c5-0ddf-436d :80f6c9c50ddf436d|26450310|Alpha Gives hash 00000092305b2956381c23dacba5b8ff9a37ab994148b37677732dc2a0650386
0a4f-143b-b5f5-48ca :0a4f143bb5f548ca|33691865|Alpha Gives hash 00000054ecdae57c6ec686b6084faf68ae49a78f7c07bbe8e51357d76de63870
你可以通过在前缀中添加更多的0来增加难度。这意味着查找串行组合将需要更长时间,但也使其更安全。
显然,您会将这些数据组合存储在某个地方,以便在激活期间,您可以比较序列代码和产品密钥(Nonce)。
在我的示例中:我使用序列密钥(16位数字)、递增整数和单词Alpha作为秘密盐。
这使得生成序列密钥变得缓慢且需要大量的CPU计算,但使验证它们非常快速。
IsSerialValid("edee-b8b7-9f6f-40ab", 17782415);
public bool IsSerialValid(string serialCode, int ProductCode)
{
string SourceString = serialCode.Replace("-", "") + "|" + ProductCode.ToString() + "|" + "Alpha";
byte[] data = Encoding.Default.GetBytes(SourceString);
string Hash = Crypto.GenerateSHA256(data);
if (Hash.StartsWith(GetDiff()))
{
return true;
}
return false;
}
秘密盐可以是一个代码短语,它映射到您可能正在开发的不同产品。这使您可以在多个产品线上重复使用产品密钥(Nonce)值。