PLUSManaged: Adding Basic Copy Protection

For a high-level primer on copy-protection, please see our blog post "Product Activation: Fingerprints, Copy Protection, Disconnected Computers".

A major aspect of software licensing is copy protection. This prevents an end-user from activating a license on one system and copying that application to another system to gain additional unauthorized licenses. A machine fingerprint is generated and saved in each license file. This fingerprint consists of several attributes known as system identifiers, which may consist of the network adapter's MAC address, the computer name, or other uniquely identifiable data. Each time an application runs, it generates the fingerprint for the current system and compares it to that stored in the license file. If enough of the individual system identifiers match, it is reasonable to believe it is the same system and the application is authorized to run. Otherwise, the user must reactivate. The system fingerprint is also included in the activation request and sent to SOLO Server. If the product option is configured to "Allow Reactivations on Same Computer" then SOLO Server will look for a previously activated installation with a matching fingerprint, and if found, will allow the system to reactivate.

While creating your license implementation class, you should have created a Validate method. This is where you can implement your copy protection logic by adding validation of information in the license and the system running your application.

Adding System Identifier Validation

System identification is the primary means of preventing a license from working on a system other than the one on which it was activated. In other words, if you were to activate an application on Computer A, copy Computer A's license file to Computer B, the application would prevent the application from running on Computer B using Computer A's license file.

Note

More detailed information on each system identifier can be found in the PLUSManaged API documentation under the com.softwarekey.Client.Licensing namespace.

Basic System Identifier Validation

 To add basic system identifier validation, we can create a Validate method to look like the following:

C#
public bool Validate()

{
SystemIdentifierValidation systemIdValidation = new SystemIdentifierValidation(
this.AuthorizedIdentifiers,
this.CurrentIdentifiers,
SystemIdentifierValidation.REQUIRE_EXACT_MATCH);

if (!systemIdValidation.Validate())

{
this.LastError = systemIdValidation.LastError;

return false;
}



return true;
}
VB.NET
Public Function Validate() As Boolean
Dim systemIdValidation As New SystemIdentifierValidation(
Me.AuthorizedIdentifiers, _
Me.CurrentIdentifiers, _

SystemIdentifierValidation.REQUIRE_EXACT_MATCH)

If Not systemIdValidation.Validate() Then
Me.LastError = systemIdValidation.LastError

Return False
End If



Return True
End Function

The code above checks to make sure the system's current identifiers are an exact match to the identifiers authorized during activation. If the CurrentIdentifiers do not match the AuthorizedIdentifiers in the license file exactly, then the license is rejected by the example above. If you only use a single type of SystemIdentifierAlgorithm, it is possible for you to replace SystemIdentifierValidation.REQUIRE_EXACT_MATCH to a number representing the minimum number of identifiers that must match instead.  However, if you use several different SystemIdentifierAlgorithm implementations in your application, then you should consider adding your own, customized validation.

Customized Validation and Fuzzy-Matching

It is possible to customize your validation logic to allow for some amount of acceptable change/variation by comparing the hash values of certain types of CurrentIdentifiers against the hash values of the same type of AuthorizedIdentifiers. To implement this in your application, you can add a new method to your license implementation class, which validates the identifiers using custom logic similar to what is shown below.

C#
private bool ValidateIdentifiers()
{
SystemIdentifierValidation systemIdValidation = new SystemIdentifierValidation(
this.AuthorizedIdentifiers,
this.CurrentIdentifiers,
SystemIdentifierValidation.REQUIRE_EXACT_MATCH);

//for each authorized identifier, set the number of matches required for the identifier type
foreach (SystemIdentifier authorizedIdentifier in this.AuthorizedIdentifiers)
{
//set the matching criteria for the identifier type
switch (authorizedIdentifier.Type)
{
case "NicIdentifier":
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_AT_LEAST_ONE_MATCH;
break;
case "HardDiskVolumeSerialIdentifier":
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_EXACT_MATCH;
break;
case "ComputerNameIdentifier":
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_EXACT_MATCH;
break;
}

//validate one identifier at a time using the specified required number of matches set above in the switch statement

if (!systemIdValidation.Validate(authorizedIdentifier.Type)
{
LastError = new LicenseError(LicenseError.ERROR_LICENSE_SYSTEM_IDENTIFIERS_DONT_MATCH);
return false;
}
}


return true;
}
VB.NET
Private Function ValidateIdentifiers() As Boolean

'calculate the number of authorized identifiers of each type, and calculate how many matches are found
For Each authorizedIdentifier As SystemIdentifier In Me.AuthorizedIdentifiers
'update our counters
Select authorizedIdentifier.Type
Case "NicIdentifier"
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_AT_LEAST_ONE_MATCH
Case "HardDiskVolumeSerialIdentifier"
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_EXACT_MATCH
Case "ComputerNameIdentifier"
systemIdValidation.RequiredMatches = SystemIdentifierValidation.REQUIRE_EXACT_MATCH
End Select
 
If Not systemIdValidation.Validate(authorizedIdentifier.Type) Then
LastError = New LicenseError(LicenseError.ERROR_LICENSE_SYSTEM_IDENTIFIERS_DONT_MATCH)
Return False
End If 
Next

Return True
End Function

The example code above assumes three types of SystemIdentifierAlgorithm are used by the application, and requires at least one match for the NIC Identifier and exact matches for the other identifiers. Once your customized validation method is implemented, you can update your Validate method to call to it as shown below.

C#
public bool Validate()
{
if (!this.ValidateIdentifiers())
{
this.LastError = systemIdValidation.LastError;
return false;
}

return true;
}
VB.NET
Public Function Validate() As Boolean
If Not Me.ValidateIdentifiers() Then
Me.LastError = systemIdValidation.LastError
Return False
End If

Return True
End Function