Question

I want design a product key using RSA key but I don't know if my idea is secure.

my idea for design product key:

I have a pair key for example

public key is : MIIBvTCCASYCCQD55fNzc0WF7TANBgkqhkiG9w0BAQUFADAjMQswCQYDVQQG…

private key is : MIICWwIBAAKBgQDRhGF7X4A0ZVlEg594WmODVVUIiiPQs04aLmvfg8SborHss5gQ…

I create product key like this:
(begin of private key for eaxmple) MIICWwIBAAKBgQDRh

And remove it from private key in software

When user doesn’t have a product key cannot decode key

Was it helpful?

Solution

That's not how you should use Public/Private keys for a product key.

Better Implementation

You can create a XML Document describing the features that are enabled. Or just something simple with the Customer Name and Date.

<license>
    <name>Acme Co.</name>
    <expiration>20120304</expiration>
</license>

You use the private key to sign the the XML document and store the hash and signature with the document. You can then verify the XML document in your product using the Public key you generated earlier.

Result of signing the document:

<license>
    <name>Acme Co.</name>
    <expiration>20120304</expiration>
    <signature>
        <hash>1230u4woeifhljksdkvh23p9</hash>
        <value>sdvrrvLJbmyscoVMg2pZZAtZJbBHsZFUCwE4Udv+u3TfiAms2HpLgN3cL
      NtRlxyQpvWt1FKAB/SCk1jr0IasdfeDOOHhTUTyiv2vMJgCRecC1PLcrmR9ABhqk
      itsjzrCt7V3eF5SpObdUFqcj+n9gasdfdQtlQeWcvKEcg=</value>
    </signature>
</license>

If the user changes the contents of the license file then the signature will no longer match. They will also not be able to re-sign the document because they do not have access to your private key. This is important, you ship the PUBLIC key with your product not the private key.

Short Key Implementation

  1. Select a Product Code (Some random string to identify your product
  2. Add a known salt to the product code
  3. Create an encryption key based on the User Name. Checks to make sure it's valid.
  4. Encrypt the salt+product code with the new key
  5. Build human readable key from the encrypted results.

It will look like 1234-1234-1234-1234

C# Solution:

/// <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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top