Using Trigger Codes for Telephone Activations

Trigger code validation is a means of achieving software activation through the manual exchange of numeric values (in other words, it is a challenge-response mechanism). Review the overview on trigger codes for additional, high-level details on how this works and how to generate activation codes.

Important

Keep in mind that any challenge-response mechanisms such as trigger code validation can be reverse engineered (similar to how "key generators" are available on the Internet for many popular software applications). Therefore, it is best to use Protection PLUS 5 SDK's standard online and manual activation features when possible, which offer much stronger security than trigger codes.

Adding Support for Trigger Codes to your Application

Before you can add support for any form of activation, your application must first have a license implementation. Once activation has been implemented, the application will also need to include copy protection to ensure it only runs on devices in which it was activated.

Creating an Activation Form

Important

If you are using or planning to use PLUSManagedGui in your application, this content may not be relevant to you since PLUSManagedGui automatically handles common functionality.

To process trigger code activations, you will need to create a form or dialog that will make this kind of activation available to your users. Most activation forms are pretty similar, and can be designed similar to the illustration below.

As a convenience, you can use/import the namespace where the Protection PLUS 4 SDK compatibility methods are located at the top of your relevant source file(s):

C#
using com.softwarekey.Client.Compatibility.ProtectionPLUS4;
VB.NET
Imports com.softwarekey.Client.Compatibility.ProtectionPLUS4

Next, you would implement your activation form, which would have variables, labels, and text boxes for the following values: User Code 1, User Code 2, Activation Code 1, and Activation Code 2. As part of the example code snippets included in this walk-through, we will make the following assumptions:

Of course, you can change the names actually used on your forms at your own discretion. However, we do strongly recommend making sure the text boxes for the User Code 1 and User Code 2 fields are read-only. Additionally, when processing the trigger codes, you want to make sure you use your own member variables for the user code values instead of parsing these values from the text boxes (for security purposes).

Since the User Code values will be relayed to you by your end-user, your new activation form will need to initialize the values displayed on the form. (the Activation Code fields should initially be blank). You can generate the User Code values as shown in the example below, which assumes m_License is your license implementation class object, and also assumes that some TextBox controls have been created on the form.

C#
m_UserCode1 = PLUS4Methods.GenerateUserCode1Value();
m_UserCode2 = PLUS4Methods.GenerateUserCode2Value(m_License.CurrentIdentifiers);
userCode1TextBox.Text = m_UserCode1.ToString();
userCode2TextBox.Text = m_UserCode2.ToString();
VB.NET
m_UserCode1 = PLUS4Methods.GenerateUserCode1Value()
m_UserCode2 = PLUS4Methods.GenerateUserCode2Value(m_License.CurrentIdentifiers)
userCode1TextBox.Text = m_UserCode1.ToString()
userCode2TextBox.Text = m_UserCode2.ToString()

Processing the Activation

Then, when the user clicks the a button (which usually says "OK" or "Activate"), your form would read the values from the fields, and convert the Activation Code 1 and Activation Code 2 values entered by the user to 32 bit integers (you can use Int32.TryParse or Int32.Parse to do this). Once your form has successfully validated and converted these values, it could then call a function similar to the one below to process the trigger code activation.

C#
internal void ProcessTriggerCode()
{
Int32 triggerCodeNumber = 0;
Int32 eventData = -1;

if (false == PLUS4Methods.ValidateTriggerCode(m_ActivationCode1, m_ActivationCode2, m_UserCode1, m_UserCode2, m_TriggerCodeSeed, m_RegKey2Seed, out triggerCodeNumber, out eventData))
{
//TODO: The activation codes failed validation. Add error handling here.
}
else
{
//The activation codes were validated successfully.
//TODO: Add your trigger code processing here! A rough example is outlined below.
switch (triggerCodeNumber)
{
case 1:
//As an example, code could be added here so that trigger code 1 activates your application as a full, non-expiring (or perpetual) license.
break;
case 10:
//As an example, code could be added here so that trigger code 10 activates your application as a time-limited (or lease/periodic) license.
//You could use the value in eventData to determine the number of days until the license expires by setting your license's EffectiveEndDate property to DateTime.UtcNow.AddDays(eventData).
break;
default:
//TODO: An unsupported trigger code number was specified. Add error handling here.
break;
}
}
}
VB.NET
Friend Sub ProcessTriggerCode()
Dim triggerCodeNumber As Int32 = 0
Dim eventData As Int32 = -1

If False = PLUS4Methods.ValidateTriggerCode(m_ActivationCode1, m_ActivationCode2, m_UserCode1, m_UserCode2, m_TriggerCodeSeed, m_RegKey2Seed, triggerCodeNumber, eventData) Then
'TODO: The activation codes failed validation. Add error handling here.
Else
'The activation codes were validated successfully.
'TODO: Add your trigger code processing here! A rough example is outlined below.
Select Case triggerCodeNumber
Case 1
'As an example, code could be added here so that trigger code 1 activates your application as a full, non-expiring (or perpetual) license.
Case 10
'As an example, code could be added here so that trigger code 10 activates your application as a time-limited (or lease/periodic) license.
'You could use the value in eventData to determine the number of days until the license expires by setting your license's EffectiveEndDate property to DateTime.UtcNow.AddDays(eventData).
Case Else
'TODO: An unsupported trigger code number was specified. Add error handling here.
End Select
End If
End Sub

While the code snippet above only gives high-level guidance on how to implement trigger code processing, the PLUSManagedGui samples include fully-functioning source code that supports trigger code processing for various types of licenses (including perpetual and time-limited licenses).

Delayed Trigger Codes

Important

If you are using or planning to use PLUSManagedGui in your application, this content may not be relevant to you since PLUSManagedGui automatically handles common functionality.

A delayed trigger code is simply where you allow the user to process a trigger code activation after having closed and re-launched your application. The problem this solves primarily revolves around how the activation form initializes the User Code 1 value, as this value is randomized. Although this is not typically an issue with telephone activations, this can pose a challenge when the user might email, fax, or text the user code values, close the application, and re-launch the application to complete the activation later. If your application needs to support this kind of delay between when the user sends the user code values and when the user completes the activation, your application may need to implement this feature by storing the user code 1 value somewhere. This can be stored in a hidden file or registry key of your choosing, and it is strongly recommended that you consider encrypting the value as well.

Storing the User Code 1 Value

One way to store the User Code 1 value is to simply store it in your license file.  The only drawback to this approach, however, is that it may be possible that issues could arise with this approach when allowing users to refresh online (when an Installation ID is present in the license file), and when using aliases (which could restore old values to the license file). Consequently, storing this value separately from the license file is the preferred approach.

Below is an example of how you could store the user code 1 value in the registry. (Note that you should consider using encryption as well if you decide to use this approach.)

C#
private void InitializeUserCode1()
{
string keyPath = @"Software\Company Name\Application Name";
string keyValue = "SessionCode";
string sessionCode = "";

//Open the registry key for reading so we may try to retrieve a previously saved value.
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyPath, false))
{
if (null != key)
{
sessionCode = (string)key.GetValue(keyValue);
int.TryParse(sessionCode, out m_UserCode1);
key.Close();
}
}
if (0 == m_UserCode1)
{
m_UserCode1 = PLUS4Methods.GenerateUserCode1Value();

//Open the registry key for writing so we may store the value for later use.
using (RegistryKey wkey = Registry.CurrentUser.OpenSubKey(keyPath, true))
{
if (wkey != null)
{
wkey.SetValue(keyValue, m_UserCode1.ToString(), RegistryValueKind.String);
wkey.Close();
}
else
{
//The key does not exist, so try to create it.
using (RegistryKey newKey = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (newKey != null)
{
newKey.SetValue(keyValue, m_UserCode1.ToString(), RegistryValueKind.String);
newKey.Close();
}
}
}
}
}
}
VB.NET
Private Sub InitializeUserCode1()
Dim keyPath As String = "Software\Company Name\Application Name"
Dim keyValue As String = "SessionCode"
Dim sessionCode As String = ""

'Open the registry key for reading so we may try to retrieve a previously saved value.
Using key As RegistryKey = Registry.CurrentUser.OpenSubKey(keyPath, False)
If Not key Is Nothing Then
sessionCode = key.GetValue(keyValue)
Integer.TryParse(sessionCode, m_UserCode1)
key.Close()
End If
End Using

If 0 = m_UserCode1 Then
m_UserCode1 = PLUS4Methods.GenerateUserCode1Value()

'Open the registry key for writing so we may store the value for later use.
Using wkey As RegistryKey = Registry.CurrentUser.OpenSubKey(keyPath, True)
If Not wkey Is Nothing Then
wkey.SetValue(keyValue, m_UserCode1.ToString(), RegistryValueKind.String)
wkey.Close()
Else
'The key does not exist, so try to create it.
Using newKey As RegistryKey = Registry.CurrentUser.CreateSubKey(keyPath)
If Not newKey Is Nothing Then
newKey.SetValue(keyValue, m_UserCode1.ToString(), RegistryValueKind.String)
newKey.Close()
End If
End Using
End If
End Using
End If
End Sub

You may then replace the line of code that previously initialized m_UserCode1 to call the method above. Of course, the above code is just an example, and it is strongly recommend that you specify your own registry location (unique to each application), and that you extend this example code to encrypt the value stored in the registry.

Clearing the User Code 1 Value

When storing the user code 1 value for alter use, it is pivotal that the stored value is cleared any time the user attempts to process activation codes, even if that attempt fails. This not only prevents users from re-using activation codes that were previously issued (which would be dangerous when an activation does something like increment the number of network seats, or days left for a license), but it also helps deter "brute-force" attacks (where a hacker or malicious script tries all possible values until it eventually gets the correct value).

Below is an example method that could be used to clear the value stored in the registry.

C#
private void ClearSessionCode()
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Company Name\Application Name"))
{
if (null != key)
{
key.DeleteValue("SessionCode");
key.Close();
}
}
}
VB.NET
Private Sub ClearSessionCode()
Using key As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Company Name\Application Name")
If Not key Is Nothing Then
key.DeleteValue("SessionCode")
key.Close()
End If
End Using
End Sub

Your application would then call the above method after calling PLUS4Methods.ValidateTriggerCode, regardless of its result.