Skip to main content
Although Devolens (formerly Cryptolens) is a web-based service, you can still use it in applications without a direct Internet connection. This includes applications that connect occasionally and devices that are kept offline.
You can find example projects here. If you prefer a video, please see the walkthrough and its notes. The license checks and error handling described below also apply to those examples.

Idea

You can think of a signed response from Activate or GetKey as a certificate or license file. Your RSA public key verifies its signature. Your application must still check whether that license permits the requested work.
  • Periodic key verification: your application checks online at intervals you choose. Between checks, it can use a saved response that still passes the license checks. If a request fails because of a confirmed connection problem, a previously verified response can be used within your offline allowance.
  • USB stick or other manual delivery: you can deliver a signed file to an air-gapped device. Your application needs to check the file before allowing the user to run the software.
Apply the validation checklist before using either kind of file. A cached response describes the license when it was signed. An offline application cannot immediately discover a later block, renewal, feature change, or device removal.

Implementation

Periodic Key Verification

We can extend the code in the Key Verification tutorial by saving a copy of the signed response. If a later request cannot reach the server, we can check that copy instead. The examples below allow a saved response to be used for up to 3 days from its signed date. This is an example allowance; you can choose a different period and file location for your application. The age check depends on the device’s clock.

Example code

Add the following code after the online response has passed your license checks. result in .NET and license in Java come from the activation example. Saving the response
Checking the saved response If a later online check fails because of a confirmed connection problem, we can load the saved file. This belongs in the connection-failure branch of your application; an API rejection or an SDK error with an unknown cause must not lead here. The refresh and fallback flow shows where this fits. Use the same RSAPubKey as in your activation code. The examples below check the signature and the file’s age. Before enabling licensed features, also apply the other license checks.
In Java, the file operations also need imports from java.io, java.nio.file, and java.nio.charset.StandardCharsets. These checks run when the file is loaded. Recheck its age and any applicable expiry while the application runs. Floating seats have their own renewal deadline.

How it works

Replace the saved response only after the new response passes the license checks. Use the saved response only when the online check fails because of a confirmed connection problem, such as a connection timeout. Do not use it after an API rejection or a failed license check. If the SDK returns an error without saying why, check the request’s error details before assuming the connection failed. Keep licensed features disabled if you cannot identify the cause. Measure age from the signed timestamp (SignDate), not the file’s creation time, modification time, or most recent save. Copying, importing, or saving the same response must not restart its allowance. Recheck age and applicable expiry while the application remains open, including before resuming work after sleep. An outstanding network request must not postpone these checks. If the application cannot connect and has no usable saved response, licensed features must stay disabled. If saving a valid online response fails, the application can still use it while its checks pass, but it has not saved a new copy for offline use.
Choose how often to check online, how long to allow offline use, and where to save the response:The pseudocode below shows how to put these steps together. You will need to implement it in your application; validate_all stands for the license checks, including the response’s age measured from its signed timestamp. The same flow applies at startup, during periodic checks, and when the user changes the license key.
Every stop licensed work branch ends that decision without enabling functionality. Determine when refresh is due from the last verified online response, including after restart; do not reset the schedule merely by reopening or saving the cache. Preserve a known rejection across application restarts: remove the affected cache entry and remember that offline fallback is disabled for that product/license until a fresh, fully validated online response is obtained. Do not let another old copy restore access after rejection.

Remarks

  • Keep the original signed response intact. Use the SDK’s methods to save it as a file or string and verify it when loading. An unsigned list of features or other license properties cannot replace the signed response.
  • The age limits above are in days: HasValidSignature(RSAPubKey, 3) in .NET and LoadFromString(RSAPubKey, contents, 3) in Java. The .NET loader also verifies the signature; the additional call supplies the age limit. Cache age and expiry depend on your time source; see the clock limitation.
  • Request the fields your validation needs. An access token’s returned-field restrictions must not remove product identity, expiry, blocking, features, or activation data that your application relies on.
  • Floating seats have a separate deadline. A cached file cannot extend it. Follow the floating renewal lifecycle.
  • For disconnected deployments that need coordinated concurrent seats, use the License Server guide and its offline floating setup. A cached file alone does not coordinate seats between processes.

USB stick (air-gapped)

Apply the same validation checklist to manually delivered files. The example below accepts files less than 365 days old; choose an allowance for your application and arrange replacement files accordingly. USB delivery does not restart that allowance or eliminate the clock-rollback limitation. If the license should be tied to a device, set Maximum Number of Machines greater than 0 and activate that device’s machine code before issuing the file. Your application then checks for that code when loading the file. A machine check is only useful when machine locking is enabled on the license.

Obtaining the license file / certificate

A file contains a signed response from Activate or GetKey. Activate can register the target device; GetKey only retrieves existing information and does not establish machine binding.
  • Activation Forms let customers submit a license key and machine code to obtain a file.
  • Dashboard: on the product page, use the yellow button beside the license key. You can activate a new machine or use Download activation file without activating one. Download a license file from the dashboard
  • API: request a signed response using Activate or GetKey, verify it, and serialize it with the SDK before delivering it.
Use a file format supported by the receiving SDK. For Java and other non-.NET SDKs, select Other languages when generating an activation file. For many devices at a customer site, see the manual activation tools.

Example code

Configure the maximum number of machines The examples below load ActivationFile20180606.skm. Replace this filename with the delivered file’s name, or let the customer select it. Use your own RSA public key and the same machine-code method as in your activation code. The examples use version 2, as in the Key Verification tutorial. This example assumes node-locking is enabled and the file was issued for this device. If the application has recorded an earlier rejection, use the manual replacement process below before accepting another file.
The public key is enough to verify the signature; the offline device does not need an access token. Apply the other license checks before enabling licensed features, and keep checking expiry and the file’s age while the application runs.
The following pseudocode includes storing an accepted file and handling a previously recorded rejection:
If the application previously received an explicit rejection, recover through the same manual delivery process:
  1. On a connected computer, have the license administrator resolve the cause, such as an expired or blocked license, and issue a new signed file for the intended product, license, and device.
  2. Transfer that replacement file to the air-gapped device and verify its signature and all applicable license checks.
  3. Clear the recorded rejection only through your application’s controlled replacement process, after confirming that the file was issued to resolve it. Reimporting an older file must not clear the rejection. A changed filename, a copied file, or a newer timestamp alone is not approval.
The licensed device can stay offline throughout this process. Your application or license administrator needs to approve the replacement; loading the file with the SDK does not do this or clear a recorded rejection automatically.

FAQ

Yes. A saved response still needs to satisfy the same license rules as an online response:
  • Check that ProductId matches your application’s product. A license for another product on the same account can also have a valid signature.
  • Check expiry according to your licensing model and enable only the features included in the license. The file’s age limit is separate from license expiry.
  • Reject a license whose saved data reports Block=true. An offline application cannot discover a block added after the file was signed.
  • Check the machine code when machine locking is enabled. The manual-file example shows this check and assumes Maximum Number of Machines greater than 0.
See Key Verification for more about these checks.
Only if your application already has a particular license key to check against. For example, if the user enters or selects a key, use its matching cached file and compare the file’s Key with that key. This prevents a different license’s file from being used after the user switches keys. Do not compare against a fixed example key.If the user supplies a license file directly, they do not need to enter its key separately. You can read the key from the signed file after verifying it, while still checking the product and the other license rules above.
Use Activate to register a device or renew a floating seat. GetKey only retrieves license information, including blocked licenses; it does not activate or renew a seat. See the method comparison for permissions and validation requirements.
No. The age check needs to know the current time. A signed license file cannot tell the application whether the device’s clock is correct.In the Java SDK, LicenseKey.LoadFromString first verifies the signature. With a positive age limit, it then compares the signed SignDate plus the allowed number of days against System.currentTimeMillis(), the device’s clock.For example, LoadFromString(RSAPubKey, licenseString, 30) checks a 30-day cache age using that clock. Moving the clock backwards can make an old file pass the age check. The signature protects the signed timestamp from modification; it does not make the device clock trustworthy.Cache age and license expiry are separate checks. In Java, the age limit uses SignDate and does not replace checking the license’s Expires value. If your application needs to prevent clock rollback from extending offline use, it will need another way to obtain time it can trust.