这不是您应该将公钥/私钥用于产品密钥的方式。
更好的实施
您可以创建一个 XML 文档来描述启用的功能。或者只是简单的客户姓名和日期。
<license>
<name>Acme Co.</name>
<expiration>20120304</expiration>
</license>
您使用私钥对 XML 文档进行签名,并将散列和签名与文档一起存储。然后,您可以使用之前生成的公钥验证产品中的 XML 文档。
签署文件的结果:
<license>
<name>Acme Co.</name>
<expiration>20120304</expiration>
<signature>
<hash>1230u4woeifhljksdkvh23p9</hash>
<value>sdvrrvLJbmyscoVMg2pZZAtZJbBHsZFUCwE4Udv+u3TfiAms2HpLgN3cL
NtRlxyQpvWt1FKAB/SCk1jr0IasdfeDOOHhTUTyiv2vMJgCRecC1PLcrmR9ABhqk
itsjzrCt7V3eF5SpObdUFqcj+n9gasdfdQtlQeWcvKEcg=</value>
</signature>
</license>
如果用户更改许可文件的内容,则签名将不再匹配。他们也将无法重新签署文档,因为他们无权访问您的私钥。这很重要,您将 PUBLIC 密钥与您的产品一起发送,而不是私钥。
快捷键实现
- 选择产品代码(一些随机字符串来识别您的产品
- 在产品代码中添加已知盐
- 根据用户名创建一个加密密钥。检查以确保其有效。
- 用新密钥加密盐+产品代码
- 根据加密结果构建人类可读的密钥。
它看起来像 1234-1234-1234-1234
C#解决方案:
/// <summary>
/// Provides the ability to generate and validate license keys based
/// on a product code.
/// </summary>
public class LicenseKeyManager
{
/// <summary>
/// Construct a new LicenseKeyManager
/// </summary>
public LicenseKeyManager()
{
crypto = new DESCryptoServiceProvider ();
}
/// <summary>
/// Set or get the product code. Once the product code
/// is manually set it will no longer be automatically
/// generated for this instance. Suggested to not
/// set the product code unless generating keys.
///
/// In this instance the ProductCode is a string
/// that identifies the machine that the license needs
/// to be generated on. This prevents the distribution
/// of keys among friends.
/// </summary>
public String ProductCode
{
set
{
productCode = value;
}
get
{
if (productCode == null)
productCode = Ethernet.MacAddress.Replace (":", "");
return productCode;
}
}
/// <summary>
/// A salt that can be added to the product code to ensure that
/// different keys are generated for different products or
/// companies.
/// Once set the salt cannot be retrieved from this object.
/// </summary>
public String Salt
{
set
{
salt = value;
}
}
/// <summary>
/// Validate a license key
/// </summary>
/// <param name="name">Name associated with the license key</param>
/// <param name="key">The license key</param>
/// <returns>True if the license key is valid</returns>
public bool IsValidKey (String name, String key)
{
if (name == null || key == null) return false;
String license = CreateLicense (name);
return license.CompareTo (key) == 0;
}
/// <summary>
/// Create a new license key associated with the given name. The key
/// will be the same if this method is reinvoked with the same name and
/// product code.
/// </summary>
/// <param name="name">Name to associate with the license key</param>
/// <returns>New License Key</returns>
public String CreateLicense (String name)
{
String licenseSource = ProductCode;
if (salt != null)
licenseSource = salt + licenseSource;
byte[] license = Encrypt(licenseSource, name);
if (license.Length > 16)
{
byte[] tmp = new byte[16];
Array.Copy (license, tmp, 16);
license = tmp;
}
else if (license.Length < 16)
{
byte[] tmp =
new byte[] {
36, 36, 36, 36, 36, 36, 36, 36,
36, 36, 36, 36, 36, 36, 36, 36};
Array.Copy (license, tmp, license.Length);
license = tmp;
}
StringBuilder sb =
new StringBuilder ();
String base64License =
Convert.ToBase64String (license).ToUpper();
base64License = base64License.Replace ('+', 'F');
base64License = base64License.Replace ('/', 'A');
// Format the license key in a human readable format.
// We dont need all of the license key just enough
// so that it isn't predictable. This key won't be
// used in decrypting the license, only in comparision
// similar to that when hasing passwords.
sb.AppendFormat (
"{0}{1}{2}{3}-{4}{5}{6}{7}-" +
"{8}{9}{10}{11}-{12}{13}{14}{15}",
base64License[0], base64License[1],
base64License[2], base64License[3],
base64License[4], base64License[5],
base64License[6], base64License[7],
base64License[8], base64License[9],
base64License[10],base64License[11],
base64License[12],base64License[13],
base64License[14],base64License[15]);
return sb.ToString();
}
private byte[] GetLegalKey(string Key)
{
string sTemp = Key;
crypto.GenerateKey();
byte[] bytTemp = crypto.Key;
int KeyLength = bytTemp.Length;
if (sTemp.Length > KeyLength)
sTemp = sTemp.Substring(0, KeyLength);
else if (sTemp.Length < KeyLength)
sTemp = sTemp.PadRight(KeyLength, ' ');
return ASCIIEncoding.ASCII.GetBytes(sTemp);
}
private byte[] Encrypt(string Source, string Key)
{
// use UTF8 unicode conversion for two byte characters
byte[] byteIn = UTF8Encoding.UTF8.GetBytes(Source);
// set the private key
crypto.Key = GetLegalKey(Key);
crypto.IV = iv;
// create an Encryptor from the Provider Service instance
ICryptoTransform encryptor = crypto.CreateEncryptor();
// convert into Base64 so that the result can be used in xml
return encryptor.TransformFinalBlock (
byteIn, 0, byteIn.Length);
}
private static byte[] iv = new byte[] {63,63,63,63,63,63,63,63};
private String productCode;
private String salt;
private SymmetricAlgorithm crypto;
}