我想使用RSA密钥设计产品密钥,但我不知道我的想法是否安全。
我对设计产品密钥的想法:
我有一对钥匙 例如
公钥是:
MIIBvTCCASYCCQD55fNzc0WF7TANBgkqhkiG9w0BAQUFADAjMQswCQYDVQQG…
私钥是:
MIICWwIBAAKBgQDRhGF7X4A0ZVlEg594WmODVVUIiiPQs04aLmvfg8SborHss5gQ…
我创建了这样的产品密钥:
(开始使用eaxmple的私钥)MIICWwIBAAKBgQDRh
将其从软件中的私钥中删除
当用户没有产品密钥时无法解码密钥
答案 0 :(得分:9)
这不是您应该如何使用公钥/私钥来生成产品密钥。
更好的实施
您可以创建描述已启用功能的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;
}