A Shelf Life Check is a type of Check that detects whether an application is being run after a certain date. This gives the application a shelf life, or a limited period of time during which it can be run.
Shelf Life Checks are useful for beta or evaluation software. If a user tries to run the application after the expiration date, the Shelf Life Check can detect this and react by notifying the application or causing the application to exit.
In other words, Shelf Life Checks help detect and react to unauthorized use of your application based on the current date.
Configure Shelf Life Checks
You can configure Shelf Life Checks in two ways:
- In the source code, add and configure the Check attributes directly in your application.
- In the Config Editor, add the Check and configure its properties and locations.
Both methods allow you to specify the properties that determine how the Check operates.
To have Dotfuscator inject Shelf Life Checks into your application, you must first obtain a Shelf Life Activation Key from PreEmptive Solutions.
After you have an Activation Key, in the Config Editor:
- Go to the Checks tab in Dotfuscator’s Config Editor.
- Select Add Shelf Life Check….
- Configure the Check properties.
- Configure the Check locations.
For the full list of available properties, see the ShelfLifeAttribute section in the Check Attributes article.
If you are adding a Tamper Check to a Xamarin Android application, see the Tamper Check for Xamarin Android section in the Enhance Protection After Your First Build article.
Activation Key
A Shelf Life Activation Key is required to inject Shelf Life Checks.
To obtain a data file with an Activation Key, contact PreEmptive Solutions.
After the Activation Key is issued, specify the path to the Activation Key in each Shelf Life Check’s ActivationKeyFile property. Keep a copy of the Activation Key on each build machine.
You only need the Activation Key when Dotfuscator processes the application. The application does not need to access the Activation Key at runtime, so do not distribute the Activation Key outside of the development organization.
Tokens
A Shelf Life Token is a file that contains information about an application’s shelf life, such as the expiration date.
Instead of embedding the expiration information directly into the application, Dotfuscator generates a Token that contains this information. At runtime, the Shelf Life Check uses the Token to determine whether the application has expired.
Dotfuscator Professional provides two ways to use a Token with a Shelf Life Check:
| Token option | Description |
| Embed the Token with the Check | Dotfuscator generates and embeds the Token with the Check in the application. This is the simpler option, but after the application ships, the Token and expiration date cannot be changed or customized. |
| Generate the Token separately | Dotfuscator generates the Token separately, and the application provides the Token to the Check at runtime. This allows the Token to be changed without redistributing the application. For example, it can support expiration date extensions or per-user expiration dates. |
In either scenario, you can use a public/private key pair to help ensure the authenticity of the Token. The Token includes the public key. After the Token is generated, it is signed with the private key. At runtime, the Shelf Life Check verifies that the signature matches the public key.
This helps ensure that the Token has not been modified and can prevent certain kinds of expiration date manipulation.
Embed a Token with the Check
You can have Dotfuscator automatically generate and embed a Token with a Shelf Life Check. This is the simpler option.
To embed a Token with the Check, configure the following properties:
ExpirationDate-
WarningDate, optional
To sign the generated Token, also configure the following properties:
PrivateKeyFile-
PrivateKeyFilePassword, if needed
For details about these and other Shelf Life Check properties, see the ShelfLifeCheckAttribute section in Check Attributes.
Generate a Token Separately
You can also generate the Token manually and have the application provide the Token to the Shelf Life Check at runtime.
Use this option when the application needs to retrieve the Token from a source such as a database or web service. This allows the Token to be changed without redistributing the application. For example, you can use this approach to support expiration date extensions or per-user expiration dates.
To generate a Token manually, see Generate New Shelf Life Token.
To have the Check retrieve the Token dynamically at runtime, configure the ShelfLifeTokenSource properties. These properties specify a source in the application code that provides the Token as a string.
For details about these and other Shelf Life Check properties, see the ShelfLifeCheckAttribute section in Check Attributes.
Generate a New Shelf Life Token
When using a Shelf Life Check with an externally stored Shelf Life Token, you can generate a new Shelf Life Token from Dotfuscator.
To generate a new Shelf Life Token:
- In Dotfuscator, select Tools > Generate Shelf Life Token....
- In the dialog, browse to and select the Shelf Life Activation Key file.
- Optional: Select a PKCS #12 Private Key file to provide additional validation of the Shelf Life Token.
- If you selected a private key file, enter the password in the Private Key File Password field.
- Set the Expiration Date.
- Optional: Set the Warning Date.
- Select Generate.
- Copy the Shelf Life Token Data to the clipboard.
Use the Use Warning Date checkbox to control whether the generated Token includes warning date behavior. Clear this checkbox if you did not enable Warning Date behavior, or if you want to provide an updated Shelf Life Token that disables it.
The Generate button is enabled when the Shelf Life Key information is valid.
Expiration and Warning Dates
Each Shelf Life Token has an expiration date. When a Shelf Life Check runs, it compares the expiration date to the current time. If the expiration date is in the past, the Check determines that the application has expired.
Each Shelf Life Token can also include a warning date. When a Shelf Life Check runs, if the warning date is in the past, the Check determines that the application is in the warning period.
What happens when the application is expired or in the warning period depends on the application notification properties.
For a Token embedded with a Shelf Life Check:
- The expiration date is specified by the Check’s
ExpirationDateproperty. - The warning date is specified by the Check’s
WarningDateproperty.
For Tokens provided at runtime, the expiration and warning dates are specified when the Token is generated.
Shelf Life Token dates are configured as strings in one of the following formats:
| Date format | Description |
| Absolute date | Uses the YYYY-MM-DD format. |
| Relative date | Uses an integer that indicates the number of days from the date Dotfuscator generated the Token. |
For example, if Dotfuscator generates the Token on August 1, 2017, and the application should expire on August 31, 2017, the expiration date can be written as either: 2017-08-31 or 30.
Application Notification
Application Notification works differently for Shelf Life Checks than for other Check types.
Other Check types provide one notification that indicates whether the Check found an unauthorized application state. Shelf Life Checks can provide two notifications:
| Notification | Description |
| Expiration Notification | Notifies the application code whether the application has expired. |
| Warning Notification | Notifies the application code whether the application is in the warning period. |
Expiration Notification
Expiration Notification tells the application code whether the application is being run after its expiration date.
This notification is always used when a Shelf Life Check runs. It is used whether the application is expired, is in the warning period, or is neither.
The following properties determine how the Check notifies the application whether it is expired:
ExpirationNotificationSinkElementExpirationNotificationSinkNameExpirationNotificationSinkOwner
These properties specify a sink in the application code that receives a bool value:
-
trueif the application is expired. -
falseif the application is not expired.
Warning Notification
Warning Notification tells the application code whether the application is being run after its warning date.
If no warning date is specified for the Check, this notification is skipped. If a warning date is specified, this notification is always used when the Check runs.
The following properties determine how the Check notifies the application whether it is in the warning period:
WarningNotificationSinkElementWarningNotificationSinkNameWarningNotificationSinkOwner
These properties specify a sink in the application code that receives a bool value:
-
trueif the application is in the warning period. -
falseif the application is not in the warning period.
Using a String Sink
As an alternative to the bool sinks, either notification can use a Method, Delegate, or MethodArgument sink where the method or delegate has the following signature:
void(string, string)In this case, the Shelf Life Check calls the method or delegate with two parameters:
- Warning date, or
nullif no warning date was specified. - Expiration date.
This allows the application code to react in a more specific way, such as displaying the expiration date to the user.
A string sink is called even if the application is not in the warning period and has not expired. However, a sink for a warning notification is never called if no warning date is specified.
Example
Consider an evaluation software with the following Shelf Life Check and code snippet:
internal class ShelfLifeApplicationNotificationExample
{
private bool applicationHasExpired; // set by Shelf Life Check
internal void SetupApplication()
{
LoadDataFiles();
EnableFreeFeatures();
if (!applicationHasExpired)
{
EnablePaidFeatures();
}
}
private void LoadDataFiles() // location of Shelf Life Check
{
Console.WriteLine("Simulating startup logic...");
}
// EnableFreeFeatures() and EnablePaidFeatures() omitted for brevity
// called by Shelf Life Check
private void LogEvaluationNotice(string warnDateString, string expireDateString)
{
// Use arguments directly as strings...
Console.WriteLine($"This evaluation software has an expiration date of {expireDateString}.");
Console.WriteLine("To purchase the full version, please contact <sales@example.com>.");
// ...or parse to DateTime objects for calculations.
DateTime warnDate = DateTime.Parse(warnDateString);
DateTime expireDate = DateTime.Parse(expireDateString);
int daysUntilWarn = warnDate.Subtract(DateTime.Today).Days;
int daysUntilExpire = expireDate.Subtract(DateTime.Today).Days;
if (daysUntilExpire <= 0)
{
Console.WriteLine("WARNING: The software has expired. Paid features are no longer available.");
}
else if (daysUntilWarn <= 0)
{
Console.WriteLine($"WARNING: The software will expire in {daysUntilExpire} days.");
}
}
}
After Dotfuscator's injection, when other application code calls SetupApplication():
-
SetupApplication()callsLoadDataFiles(). - Before
LoadDataFiles()executes, the Shelf Life Check runs:- The Check determines if the application is expired, in the warning period, or neither.
- As its warning notification, the Check calls the
LogEvaluationNotice(string, string)method, providing the warning date and expiration date as strings. - The
LogEvaluationNotice(string, string)writes information about the evaluation software to the console, then returns control to the Check. - As its expiration notification, the Check sets the
applicationHasExpiredfield totrueif the application has expired, andfalseotherwise. - The Check finishes running and returns control to the
LoadDataFiles()method.
- The
LoadDataFiles()method runs, then returns control toSetupApplication(). -
SetupApplication()callsEnableFreeFeatures(), which then returns control toSetupApplication(). - If
applicationHasExpiredisfalse(i.e., the evaluation has not expired),SetupApplication()callsEnablePaidFeatures(), which then returns control toSetupApplication(). -
SetupApplication()returns control to its caller.
Exit Behavior
Shelf Life Checks do not support Check Actions.
However, you can configure behavior equivalent to the Exit Check Action by setting the Check’s ExpirationNotificationSinkElement property to DefaultAction.
When a Shelf Life Check with this configuration detects expired application usage, it causes the application to exit immediately with exit code 0.
Unsupported Application Types
Dotfuscator can inject Shelf Life Checks into all .NET assemblies except the following:
- Managed C++ assemblies containing native and managed code
- Multi-module assemblies
- .NET Core assemblies
- .NET 5 assemblies
- UWP assemblies
- Xamarin assemblies
- MAUI assemblies
Test Shelf Life Checks
To test how Shelf Life Checks react to expired application usage or usage during the warning period:
- Temporarily configure the Shelf Life Checks with expiration dates and warning dates in the past.
- Build the protected application.
- Run the protected application.
- Exercise all locations of your Shelf Life Checks.
- Observe how the application reacts when it is expired or in the warning period.
- Change the dates back to their intended values before building the application for distribution.