# Introduction
Source: https://help.cryptolens.io/api-reference/introduction
Our Web API reference is currently available at [https://app.cryptolens.io/docs/api/v3/](https://app.cryptolens.io/docs/api/v3/)
For [AI-assisted integrations](/getting-started/build-with-ai) or full API reference search, use [**llms-full.txt**](https://app.cryptolens.io/docs/api/v3/llms-full.txt)**.** It contains the complete Devolens, formerly Cryptolens, API documentation in an LLM-friendly format.
# Basics of Cryptolens
Source: https://help.cryptolens.io/basics/index
Basic concepts of Cryptolens
Cryptolens is a software licensing platform that does the following:
* **Licensing**: keeping track of all instances of your software. For example, you can restrict which features a customer should have access to, on how many devices they should be able to run the application, keep track usage of individual features as well as ensure that a license stops working after the expiration date.
* **Payment processing**: automating selling of your software, either through Cryptolens (supports Stripe & PayPal) or by integrating it with external payment providers (e.g. FastSpring or your own system).
* **Analytics of usage data**: Analyse the usage of your software globally or on user level.
## Cryptolens Platform
Cryptolens platform is accessed through the [app.cryptolens.io](http://app.cryptolens.io). This is the central place where you can control all your applications. It allows you to:
* Implement licensing
* Process payments
* Analyse usage
## Cryptolens Client API
To make sure that your applications can communicate with our server, you can use one of our [client libraries](https://help.cryptolens.io/web-api/skm-client-api). A client library provides an implementation of all the methods of the Web API as well as several other methods that are useful. A client library is not necessary to be able to verify a license, but it makes it easier.
By adding a small code snippet (read more [here](/examples/key-verification)), it enables you to translate your licensing model into simple logic, as shown below:
```c# theme={null}
if(license.HasFeature(1)
.HasNotExpired()
.IsValid())
{
// do something
}
else
{
// invalid license.
}
```
## Next
* [Learn about ‘products’ and ‘keys’](/basics/product-and-keys)
# Products and Key
Source: https://help.cryptolens.io/basics/product-and-keys
Products and Keys in Cryptolens
## Key
A (license) key is a 20 letters long string, eg. `MUYVD-LSEBY-CXHRQ-XFAGY`, that has license information associated with it. For example, it can store info such as:
* Trial key or subscription (time-limited)
* Customer information
* Custom variables
These are just some of the examples of what a key can do.
## Product
A Product is a way to organize your keys. Normally, you will name the product the same as your application. For serial key generation purposes, you will need to specify an algorithm: either [SKGL or SKM15](https://help.cryptolens.io/web-interface/skgl-vs-skm15). We recommend to use the default SKM15.
## Next
* [Learn the basics about our Web API](/basics/webapi)
# Web API
Source: https://help.cryptolens.io/basics/webapi
## Talking to Web API
Web API can be thought of as a channel that can be used to talk to Cryptolens Platform from your application and third party services. It contains a wide range of methods, which allow you to activate keys, create new ones, analyse usage data, and so much more.
### Cryptolens Client APIs
This is a set of client libraries that you can add into your project to faciliate license key verification. You can find out more [here](https://help.cryptolens.io/web-api/skm-client-api).
### Direct Communication
If you are targeting a language for which there is no client library, you can still take advantage of the functionality of Cryptolens’s Web API. For example, in order to retrieve key information in JSON, you can call
```
https://app.cryptolens.io/api/key/GetKey?token={accesstoken}&ProductId=1234&Key=MUYVD-LSEBY-CXHRQ-XFAGY&Sign=True
```
You can find more [here](https://app.cryptolens.io/docs/api/v3/).
## Next steps
You’ve now learned about the basic concepts of Cryptolens. Here’s what’s next:
1. [Get started with a pratical implementation](https://help.cryptolens.io/getting-started/index)
2. [Learn more about possible licensing models](https://help.cryptolens.io/licensing-models/licensetypes)
3. [Understand GDPR and its implications](https://help.cryptolens.io/legal/GDPR)
# Changelog
Source: https://help.cryptolens.io/changelog/index
Updates are available on the following page: [https://app.cryptolens.io/App/Changes](https://app.cryptolens.io/App/Changes)
# Data analysis and reports
Source: https://help.cryptolens.io/examples/data-analysis
Example of how to analyse various data sources in Cryptolens
## Introduction
There are several data sources you can use to analyse the usage of your application and generate audit reports. We will describe them in more detail below and provide several examples of how they can be analysed.
## Data sources
* [Web API Log](https://app.cryptolens.io/docs/api/v3/model/WebAPILog) - contains a list events that are created each time a license changes (eg. activation or change of expiration date). Data object changes are also logged in this log. Note, this log contains minimal information about the event.
* [Events Log](https://app.cryptolens.io/docs/api/v3/model/EventObject) - contains events that were registered using [Register Event](https://app.cryptolens.io/docs/api/v3/RegisterEvent) as well several other internal events.
* [Object Log](https://app.cryptolens.io/docs/api/v3/model/ObjectLog) - contains a list of events that are created when certain object types are added or modified to allow you to track all changes made by you and your team members.
## Analysis
We suggest to review the [following repository](https://github.com/Cryptolens/reporting-tools) to check if the report that you need to create is already available. As always, please let us know should you have any questions at [support@cryptolens.io](mailto:support@cryptolens.io).
The easiest way to analyse the logs is using our [Python SDK](https://github.com/Cryptolens/cryptolens-python) in combination with [Anaconda](https://www.anaconda.com/) to manage the packages. Let’s look at how we can get some basics stats on the ratio of successful vs. unsuccessful requests. First, we need to load the relevant data:
```python theme={null}
from datetime import datetime, timedelta, date
from licensing.models import *
from licensing.methods import Key, Helpers
import pandas as pd
import matplotlib.pyplot as plt
from tools import *
month_back = int(datetime.datetime.timestamp(datetime.datetime.today() - datetime.timedelta(days=30)))
logs = []
ending_before=0
"""
Loading the data
"""
while True:
res = Key.get_web_api_log(token=get_api_token(), order_by="Id descending", limit = 1000, ending_before=ending_before)
if res[0] == None:
break
logs = logs + res[0]
if res[0][-1]["time"] < month_back:
break;
ending_before = res[0][-1]["id"]
logs = pd.DataFrame(logs)
logs = logs[logs["time"]>month_back]
```
Once we have the data in a pandas DataFrame, let’s create the plot:
```python theme={null}
def success(state):
return state % 100 // 10 - 1
logs["Success"] = success(logs["state"])
logs[logs["Success"]== 0] = "Success"
logs[logs["Success"]== 1] = "Failure"
logs["Success"].value_counts(normalize=True).plot(kind="bar", title='Successful vs. unsuccessful API requests')
```
This will create the following plot:
# Data collection
Source: https://help.cryptolens.io/examples/data-collection
Explains how data is being collected and how additional data can be supplied for better insights
## Idea
By default, Cryptolens [logs](https://app.cryptolens.io/docs/api/v2/WebAPILog) many of the requests made to the Web API. This includes all [data object methods](https://app.cryptolens.io/docs/api/v3/Data) and all [key methods](https://app.cryptolens.io/docs/api/v3/Key) except for [Get Key](https://app.cryptolens.io/docs/api/v3/GetKey). However, these logs contain limited information and may not capture all events that could be useful from an analytics standpoint.
When you deploy your product, it can for instance be useful to know the OS it’s running on or what features customers use the most, etc. Although these are useful on their own, when you link them together, you can get even better insights that can aid you in business critical decision making. For example, if you link transaction data with the OS data, you can see which OS brings you most of the revenue. If you see that a certain OS version does not bring in significant revenues, you can use this as basis to stop supporting it.
In Cryptolens, additional data is referred to as **events** and can be gathered using [analytics methods](https://app.cryptolens.io/docs/api/v3/AI) in the Web API. We will cover these in the implementation section.
## Implementation
### In the app
To register an event, you can call the [AI.RegisterEvent](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.AI.html#SKM_V3_Methods_AI_RegisterEvent_System_String_SKM_V3_Models_RegisterEventModel_) method. Most of the parameters are optional, but it’s useful to at least supply a `FeatureName` and either `Key` or `MachineCode`. If you just have one product, `ProductId` is not necessary, but it’s useful if you have multiple products.
The code snippet below can be used to register event, in our case that the user started `YearReportGenerator` module.
```c# theme={null}
AI.RegisterEvent("access token with RegisterEvent permission",
new RegisterEventModel { EventName = "start", FeatureName = "YearReportGenerator", Key= "AAAA-BBBB-CCCC-DDDD", MachineCode = Helpers.GetMachineCode(), ProductId = 3,
Metadata = Helpers.GetOSStats() });
```
If you are on a platform that does not support this method, you can always send in data using by calling the URL below:
```
https://app.cryptolens.io/api/ai/RegisterEvent?token=&EventName=start&FeatureName=YearReportGenerator&Key=AAAA-BBBB-CCCC-DDDD&ProductId=3&MachineCode=&
```
### Integration with payment providers
In order to track transactions, we can use the same [AI.RegisterEvent](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.AI.html#SKM_V3_Methods_AI_RegisterEvent_System_String_SKM_V3_Models_RegisterEventModel_) method, but supply different set of parameters.
To record the value, we can use the parameters `Value` and `Currency`. This can either be accomplished by calling the Web API directly or `AI.RegisterEvent` method (in case your backend is in .NET).
Let’s assume your customer has have paid 30 usd for the product. In that case, you can call the following url:
```
https://app.cryptolens.io/api/ai/RegisterEvent?token=&Value=30&Currenc
```
# Introduction
Source: https://help.cryptolens.io/examples/index
In this section, we have listed several examples to help you get started. The focus is on applications targeting .NET, however Cryptolens does work with other languages too. You can find more examples in repositories associated with respective [client API](https://help.cryptolens.io/web-api/skm-client-api).
If you want to explore other licensing models, we recommend to review [licensing models](https://help.cryptolens.io/licensing-models/licensetypes) section, where all examples have code suggestions in .NET.
Depending on the platform (eg. Unity, Android, etc), we recommend to review [this page](https://help.cryptolens.io/getting-started/index) for platform specific advice.
## Inside your application
* [Key verification](/examples/key-verification) - prepared example for key verification (C#, [VB.NET](http://VB.NET), Java, Python, C++).
* [Offline key verification](/examples/offline-verification) - prepared example for offline key verification (C#, [VB.NET](http://VB.NET)).
* [Creating verified trials](/examples/verified-trials) - prepared example showing how to create verified trials (C#, [VB.NET](http://VB.NET)).
## External services
* [Key generation](/examples/key-generation) - key generation (useful when integrating with third party payment services).
## Licensing models
* [Perpetual licensing (try & buy)](/licensing-models/perpetual)
* [Recurring payments (SaaS)](https://help.cryptolens.io/licensing-models/subscription)
# Integration with n8n
Source: https://help.cryptolens.io/examples/integration-with-n8n
Devolens (formerly Cryptolens) offers a n8n integration (node) that you can use in a similar way as our [Zapier app](/examples/zapier).
You can find out more about the methods that are supported on [this page](https://n8n.io/integrations/cryptolens-devolens/). If any method that you need is missing, please let our support team know.
## Use cases
Similar to Zapier, the goal of the n8n integration is allow you to integrate Cryptolens with other services. Typically, our customers use it to automate:
1. License key delivery upon a successful payment.
2. Populate a CRM when a new license is created.
3. Create a new license for a lead in a CRM.
4. Sending automatic reminders to customers when their license is about to expire.
### Examples
Our n8n node was released recently and we are still working on documenting how it can be used. Please feel free to reach out to us, help (at) [devolens.com](http://devolens.com) or through the chat box.
Below, we have listed a few use cases and the general idea of how to implement them.
**License key delivery when a payment succeeds**
For this example, let's assume you are using [Stripe Checkout](https://stripe.com/en-se/payments/checkout) to process payments. Once a customer has activated a subscription, you can capture this event using [Stripe's n8n node](https://n8n.io/integrations/stripe/) and call Cryptolens to issue a license. Once the license is issued, you can call an email service (e.g. [Send email service](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.sendemail/)) to send the license to a customer.
**Populate a CRM once a license is issued**
Our n8n integration contains an action called Get Web API Log (in the AI tab) that you can use to capture events. To capture all events related to issuance of a license, you can filter on States 3010 and 3011. Once you have the event, you can call the CRM that you are using.
# Key Generation
Source: https://help.cryptolens.io/examples/key-generation
Example of how to create new keys using our API.
Being able to create new license keys through external applications is important if you want to integrate Cryptolens with an external providers (eg. for payments, distribution etc).
For example, if you already have a web store in place and want to keep using it, you can still use Cryptolens for eg. software licensing.
This article describes the way you can generate keys by a simple web request through another website or an application.
The goal is to automate software distribution by allowing your customers to receive valid license keys upon successful payments.
## Generating a Key
Once you have enabled key generation, you are able to generate a new key. The simplest way is to assign all parameters (can be found here) to get a link that will return the specific key you want. A quick way to get that link without actually reading the Web API documentation is as following:
1. Select the product: [https://app.cryptolens.io/Product](https://app.cryptolens.io/Product)
2. Press Create New Key
3. Fill in all the parameters (such as set time, features, etc.)
4. Press on the \> button.
5. Copy the link in the textbox.
Done! If you try to visit the link, you will notice that a new key will be returned. If you prefer to get a json object instead, please add \&json=true .
# Key verification
Source: https://help.cryptolens.io/examples/key-verification
This guide shows the steps to perform a simple key verification using one of our SDKs/libraries.
The code below should be included whenever you want to verify a license key, which normally occurs during app start (eg. Form\_Load for desktop apps). In addition, you can invoke it whenever a user updates the license key. In some licensing models, this check needs to be called periodically.
## Getting started
First, we need to install the client SDK / library
### **Install Nuget package**
In Visual Studio package manager
```powershell theme={null}
PM> Install-Package Cryptolens.Licensing
```
Using dotnet CLI
```bash theme={null}
dotnet add package Cryptolens.Licensing
```
**If you are targeting Mac, Linux or Unity/Mono, we recommend to use the cross platform version of that package.**
In Visual Studio package manager
```powershell theme={null}
PM> Install-Package Cryptolens.Licensing.CrossPlatform
```
Using dotnet CLI
```bash theme={null}
dotnet add package Cryptolens.Licensing.CrossPlatform
```
### Python 3
```bash theme={null}
pip install licensing
```
### Python 2
Please copy `cryptolens_python2.py` file into your project folder. The entire library is contained in that file.
> In the examples below, please disregard the imports and use only the following one:
```python theme={null}
from cryptolens_python2 import *
```
If you create a plugin for Autodesk Revit or use IronPython 2.7.3 or earlier, please also add the line below right after the import:
```python theme={null}
HelperMethods.ironpython2730_legacy = True
```
There are two pre-compiled jar files available in the [package repository](https://github.com/Cryptolens/cryptolens-java): `cryptolens.jar` and `cryptolens-android.jar`. If your application is cross platform or if you would like to have as few dependencies as possible (e.g., without `slf4j`), we recommend to use `cryptolens-android.jar` instead.
If you choose to use `cryptolens-android.jar`, `GetMachineCode` and `IsOnRightMachine` need to be called with the version parameter set to 2. For example, `Helpers.GetMachineCode(2)` or `Helpers.IsOnRightMachine(license, 2)`. If your application will run on an Android device, we recommend to use a different way to obtain the machine code, which is described [here](https://help.cryptolens.io/getting-started/ios-android).
Run the following command in the terminal to install the SDK.
```bash theme={null}
npm add cryptolens
```
In order to get started with the library, start by running the following command in the terminal:
```bash theme={null}
go get github.com/Cryptolens/cryptolens-golang/cryptolens
```
The source code for the C++ client SDK is available at the following repository [https://github.com/cryptolens/cryptolens-cpp](https://github.com/cryptolens/cryptolens-cpp). There are working examples showing how to use the library in the `examples` folder.
### Visual Studio
For Visual Studio a tutorial is available in the `tutorial` folder in the repository which describes how to use the library with Visual Studio.
### CMake
The root of the respitory contains a `CMakeLists.txt` file which can be used to build the library.
```c# C# theme={null}
using SKM.V3;
using SKM.V3.Methods;
using SKM.V3.Models;
```
```c# VB.NET theme={null}
Imports SKM.V3
Imports SKM.V3.Methods
Imports SKM.V3.Models
```
```python Python 3 theme={null}
from licensing.models import *
from licensing.methods import Key, Helpers
```
```python Python 2 theme={null}
# Python 2 library is currently contained in a single file,
# cryptolens_python2.py. You need to download it and place in the
# same folder where you have the rest of your code. It can then
# be imported as follows:
from cryptolens_python2 import *
```
```javascript Java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
```
```javascript Node Js theme={null}
const key = require('cryptolens').Key;
const Helpers = require('cryptolens').Helpers;
```
```go Go theme={null}
import "github.com/Cryptolens/cryptolens-golang/cryptolens"
```
```cpp C++ theme={null}
#include
#include
// The library uses a configuration class to select which dependencies
// are used for various functions, such as making http requests and
// cryptographic functions. The configuration classes provided by the
// library are also parameterized by a machine code computer class
// which is responsible for computing the machine code for a device.
//
// The library provides includes pre-defined configuration classes:
// For Unix based systems:
// Configuration_Unix_IgnoreExpires
// Configuration_Unix_CheckExpires
// For Windows systems:
// Configuration_Windows_IgnoreExpires
// Configuration_Windows_CheckExpires
//
// In this example we will use the MachineCodeComputer_static where the
// machine code can be set by a call to the set_machine_code() method.
//
// For more details on configurations and machine code computer classes
// see the README.md for the library.
//
// The following lines should be updated to reflect the chosen
// configuration and machine code computer classes.
#include
#include
namespace cryptolens = ::cryptolens_io::v20190401;
// The following line sets up a type alias for the handle class
// which will be used to interact with the library.
//
// This line should be updated to reflect the chosen configuration and
// machine code computer classes.
using Cryptolens =
cryptolens::basic_Cryptolens<
cryptolens::Configuration_Class
>;
```
The following script will verify the license key with the server. To get it to work, you need to change the **ProductId**, the **RSAPubKey**, **Access Token** and **licenseKey**. More information can be found below the code snippet.
```c# C# theme={null}
var licenseKey = "GEBNC-WZZJD-VJIHG-GCMVD"; // <-- remember to change this to your license key
var RSAPubKey = "enter the RSA Public key here";
var auth = "access token with permission to access the activate method";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
Key = licenseKey,
ProductId = 3349, // <-- remember to change this to your Product Id
Sign = true,
MachineCode = Helpers.GetMachineCodePI(v: 2)
});
if (result == null || result.Result == ResultType.Error ||
!result.LicenseKey.HasValidSignature(RSAPubKey).IsValid())
{
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
Console.WriteLine("The license does not work.");
}
else
{
// everything went fine if we are here!
Console.WriteLine("The license is valid!");
}
Console.ReadLine();
```
```VB.NET VB.NET theme={null}
Dim licenseKey = "GEBNC-WZZJD-VJIHG-GCMVD" ' <-- remember to change this to your license key
Dim RSAPubKey = "enter the RSA Public key here"
Dim auth = "access token with permission to access the activate method"
Dim result = Key.Activate(token:=auth, parameters:=New ActivateModel() With {
.Key = licenseKey,
.ProductId = 3349, ' <-- remember to change this to your Product Id
.Sign = True,
.MachineCode = Helpers.GetMachineCodePI(v:=2)
})
If result Is Nothing OrElse result.Result = ResultType.[Error] OrElse
Not result.LicenseKey.HasValidSignature(RSAPubKey).IsValid Then
' an error occurred or the key is invalid or it cannot be activated
' (eg. the limit of activated devices was achieved)
Console.WriteLine("The license does not work.")
Else
' everything went fine if we are here!
Console.WriteLine("The license is valid!")
End If
```
```python Python 3 theme={null}
RSAPubKey = "enter the RSA Public key here"
auth = "access token with permission to access the activate method"
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code=Helpers.GetMachineCode(v=2))
if result[0] == None:
# an error occurred or the key is invalid or it cannot be activated
# (eg. the limit of activated devices was achieved)
print("The license does not work: {0}".format(result[1]))
else:
# everything went fine if we are here!
print("The license is valid!")
```
```java Java theme={null}
String RSAPubKey = "enter the RSA Public key here";
String auth = "access token with permission to access the activate method";
LicenseKey license = Key.Activate(auth, RSAPubKey,
new ActivateModel(3349, // <-- remember to change this to your Product Id
"ICVLD-VVSZR-ZTICT-YKGXL", // <-- remember to change this to your license key
Helpers.GetMachineCode(2)));
if (license == null) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
```javascript Node Js theme={null}
var RSAPubKey = "Your RSA Public key, which can be found here: https://app.cryptolens.io/User/Security";
var result = key.Activate(token="access token with permission to access the activate method", RSAPubKey, ProductId=3349, Key="GEBNC-WZZJD-VJIHG-GCMVD", MachineCode=Helpers.GetMachineCode());
result.then(function(license) {
// success
// Please see https://app.cryptolens.io/docs/api/v3/model/LicenseKey for a complete list of parameters.
console.log(license.Created);
}).catch(function(error) {
// in case of an error, an Error object is returned.
console.log(error.message);
});
```
```go Go theme={null}
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("License key activation failed!")
return
}
```
```cpp C++ theme={null}
// Some dependencies like curl may require additional setup code, see the examples
cryptolens::Error e;
Cryptolens cryptolens_handle(e);
// Setting up the signature verifier with credentials from "Security Settings"
// on cryptolens.io
cryptolens_handle.signature_verifier.set_modulus_base64(e, "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==");
cryptolens_handle.signature_verifier.set_exponent_base64(e, "AQAB");
// This line is only for MachineCodeComputer_static and sets the machine code used
cryptolens_handle.machine_code_computer.set_machine_code(e, "5bccbfb6567abdcf998b7c74190183ac315720a4fd4da56bac4f31be571bbb30");
// This makes a call to the Cryptolens Web API to check if the license key is valid
cryptolens::optional license_key =
cryptolens_handle.activate
( e // Object used for reporting if an error occured
, "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=" // Cryptolens Access Token
, 3646 // Product id
, "MPDWY-PQAOW-FKSCH-SGAAU" // License Key
);
if (e) {
// An error occured. For more details on how errors are reported see
// the library documentation and examples.
}
```
**.NET(C#/VB.NET) only**: If your application will run in Unity/Mono, Rhino/Grasshopper or on a platform other than Windows, we recommend to use a different version of Key.Activate.
**RSAPubKey** is found at the bottom of [this page](https://app.cryptolens.io/docs/api/v3/quickstart), under *RSA Public Key* section and it's the same for the entire account. You don't need to change it.
**Access token** can be created on [this page](https://app.cryptolens.io/User/AccessToken#/). For the code to work, please check "Activate" permission and leave other fields unchanged. Typically, an access token stays the same for all your products, however, you can always create a new one and make it product specific.
**ProductId** is found on the product page and if you add the key verification script to a new product, this value needs to be changed in the code.
**LicenseKey** is found on the product page and contains 20 characters of the following format AAAA-BBBB-CCCC-DDDD. Each end user/customer should normally receive their unique license key. The license key should not be hard coded inside the application. Instead, there should be a way for the user to enter it inside the application or other means.
Using license keys is only one of the way to authenticate customers. You can also use license files (see [Offline verification](/examples/offline-verification) tutorial for more information) as well as username and password (see [User verification](/examples/user-verification) tutorial).
## Troubleshooting
### General
In most cases, this is because some of the required parameters are missing. These are:
* `RSAPubKey`
* `auth`
* `ProductId`
It can also be that the `licenseKey` is missing. Please check [the beginning of the tutorial](https://help.cryptolens.io/examples/key-verification#examples) on how to find them.
Note, if you have blocked a license key, it will also return a null result.
### .NET specific (C#/VB.NET)
In some Windows environments (e.g. when developing Excel addins), it might not be feasible to call Helpers.GetMachineCode on .NET Framework 4.6. The reason for this is the call we make to `System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform`. To fix this, we have added a boolean flag in `Helpers` class. Before calling `Helpers.GetMachineCode` or `Helpers.IsOnRightMachine`, please set `Helpers.WindowsOnly=True`.
```c# theme={null}
Helpers.WindowsOnly = true;
var machineCode = Helpers.GetMachineCode();
```
If the approach above does not work, please try the following call instead:
```c# theme={null}
var machineCode = SKGL.SKM.getMachineCode(SKGL.SKM.getSHA1);
```
This means that [Cryptolens.Licensing](https://help.cryptolens.io/web-api/skm-client-api) library was not included into the project. It can be easily added using **NuGet packager manager**, which you can find by right clicking on the project:
If you are
If your clients are able to visit `app.cryptolens.io` and `api.cryptolens.io` in Microsoft Edge but unable to activate the application, the issue could be both that they are using a proxy, connect to Active directory or that their IT department has blocked TLS of certain versions.
To fix these issues, we recommend to:
1. Update to the latest version of the SDK.
2. If you run a version of .NET Framework prior to .NET Framework 4.7, we recommend to manually specify which TLS should be used. Before any call to the API (for instance, Key.Activate performs an API call), we recommend to add the following line:
```
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
```
Ideally, you should try to pick the highest available TLS version, but it is important to also test that such TLS version is supported in the .NET Framework vesion that you use.
If possible, the best approach is to run the latest version of .NET Framework. If that is not possible, please use at least .NET Framework 4.7. In other cases, the workaround above can be used (i.e. manually specifying the TLS version).
Some customers have reported an error with the right version of Newtonsoft.Json not being found. It seems to be localized to those that target .NET Framework 4.8, and the following error is shown:
```
System.IO.FileLoadException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.
```
The error is thrown when a wrong version of Newtonsoft.Json is installed in the same project as Cryptolens.Licensing library. To fix this, you need to make sure that Newtonsoft.Json is uninstalled completely and then re-install Cryptolens.Licensing.
### Python
This error occurs when the timestamp for the expiration date received from the server exceeds the limit in Python. This typically occurs when the **Period** is set to a excessively large value, often to prevent the license from expiring.
While Cryptolens requires a period (defaulted to 30) during the creation of a new license, this does not mark the license as time-limited. You can learn more about it [here](https://help.cryptolens.io/web-interface/keys-that-dont-expire). In essence, a license is treated as time-limited by either enforcing this in the Python code (e.g. if F1=true, the license is time-limited and so we check the expiration date against the current date, to see that it is still valid) or on the server side. On the server side, you can, for example, set up a feature that will automatically block expired licenses. You can read more about it [here](https://help.cryptolens.io/faq/index#blocking-expired-licenses).
In sum, to solve this issue, you can either follow one of the methods described above or set the period to a smaller value.
This error is thrown when the urllib library (a built in library in Python that we use to send HTTP requests) is unable to locate the CA files on the client machine. From our experience, this error occurs exclusively on Macs where the Python environment is incorrectly installed.
To solve this temporarily for **testing purposes**, you could temporary disable SSL verifications as described in [here](https://github.com/Cryptolens/cryptolens-python#ssl-verification), however, we do not recommend this in a production scenario. Instead, a better solution is to fix the underlying issue preventing the Python environment from finding the CA files.
This can be accomplished in at least two ways:
**Using certifi**
Before calling any of the API methods (e.g. Key.activate), you can add the following code:
```python theme={null}
import certifi
os.environ['SSL_CERT_FILE'] = certifi.where()
```
Please note that this requires `certifi` package to be installed.
**Running a script in the Python environment**
An alternative is to run script in their environment that should fix the issue. You can read more about it in this thread: [#65](https://github.com/Cryptolens/cryptolens-python/issues/65)
**Summary**
The key takeaway is that it is better to address the issue with missing CA on the user side, since this issue will typically be user-specific. If that is not possible, you can use the code above to manually set the path to CA files. Although we have mentioned turning off SSL verification temporarily, it should not be used in production. `Key.activate` takes care of signature verification internally, but some other methods do not.
If you have customers who want to use a proxy server, we recommend enabling the following setting before calling any other API method, such as Key.Activate.
```python theme={null}
HelperMethods.proxy_experimental = True
```
This will ensure that the underlying HTTP library (urllib) used by Cryptolens Python SDK will use the proxy configured on the OS level. The goal is to make this default behaviour in future versions of the library, once enough feedback is collected.
SSL verification can temporarily be disabled by adding the line below before any call to Key.Activate.
```python theme={null}
HelperMethods.verify_SSL = False
```
The Cryptolens Python SDK will verify that the license information has not changed since it left the server using your RSA Public Key. However, we recommend to keep this value unchanged.
### C++
How errors are reported in the C++ library is described in the `README.md` file, which is also available [here](/libraries/cpp). If you are on a Unix based system, you can find a summary of all curl errors [here](/libraries/cpp/libcurl-errors).
# Manual activation of a large number of machines
Source: https://help.cryptolens.io/examples/manual-activation
Example of how you can activate a large number of machines on site.
## Introduction
**Note**: All related files and scripts can be found on [this page](https://github.com/Cryptolens/admin-tools/tree/master/manual-activation).
If you do not want your customers to automatically be able to activate new machines, you can ask their IT dept to send you the list of machine codes of the computers where your software will run in advance, so that you can activate these on your end. In this folder, we have compiled the scripts that can be useful to support this use case. To sum up, we need to solve two problems:
1. Easily compute the machine code on the end user device.
2. Automatically go through the list and activate all the devices.
### Computing the machine code
In order to obtain the machine code of the current machine, IT dept can run a script that obtains the machine code and friendly name of the device.
### PowerShell script
The `machinecodescript.ps1` PowerShell script gives the same result as calling [Helpers.GetMachineCodePI()](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Helpers.html#SKM_V3_Methods_Helpers_GetMachineCodePI) in the [.NET client library](https://github.com/cryptolens/cryptolens-dotnet). The script will also append the machine name next to the machine code.
In order to run this script, they may need to change the execution policy to “remote signed”, which can be done with the following command:
```bash theme={null}
set-executionpolicy remotesigned
```
If they cannot change the execution policy to `removesigned`, you can use the cmd script below:
### Cmd script
The `machinecodescriptsha256.bat` cmd script gives the same result as calling [Helpers.GetMachineCodePI(v:2)](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Helpers.html#SKM_V3_Methods_Helpers_GetMachineCodePI) in the [.NET client library](https://github.com/cryptolens/cryptolens-dotnet). The same machine code will also be generated in the [Python client](https://github.com/Cryptolens/cryptolens-python), provided that [Helpers.GetMachineCode](https://help.cryptolens.io/api/python/#licensing.methods.Helpers.GetMachineCode) is called with `v=2`.
### Verifying the license key
In the [key verification](https://help.cryptolens.io/examples/key-verification) example, we use Activate to verify a license. This method will automatically activate new machines, as long as the max number of machines limit has not been reached. To prevent activation of new machines, [GetKey](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html#SKM_V3_Methods_Key_GetKey_System_String_SKM_V3_Models_KeyInfoModel_) method can be used instead. It is called with the same parameters as [Activate](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html#SKM_V3_Methods_Key_Activate_System_String_SKM_V3_Models_ActivateModel_). The machine code parameter does not need to be provided.
**Note:** when using GetKey, it is important to check that the license key is not blocked. Activate does that by default, but when using GetKey, it needs to be done on the client side.
### Script to automatically activate all the machines
You can activate the list of devices using the following extension: [https://app.cryptolens.io/extensions/ActivateDevices](https://app.cryptolens.io/extensions/ActivateDevices)
Each device/machine code should be separated by a new line. A line can contain either a machine code or a machine code together with the friendly name, separated by a tab (\t) character.
# Migrate to Cryptolens from a third party licensing system
Source: https://help.cryptolens.io/examples/migrate
How to migrate to Cryptolens from a third party licensing system
In this post we will cover the steps necessary to migrate to Cryptolens licensing solution from a different licensing provider.
## The idea
In sum, the idea is to issue new licenses to existing clients using Cryptolens and allow the software to be used with the old system for a limited period of time. Practically, there are at least two ways it can be done. We will cover the general idea below and then discuss the practical side further down in the article.
**Method 1:** In the first method, you could replace the old third party licensing solution with Cryptolens in a new release of your software, as well as provide all existing customers with a license key from Cryptolens. This way, those users who have not upgraded yet would not be affected.
**Method 2:** Similar to the first method, except that you could keep the old system alongside Cryptolens in the new release and phase out the old system over a longer period of time.
If you the old licensing system requires an active subscription to verify licenses, there would be a point at which old customers would be forced to upgrade. You can choose how long this period should be based on how many users have upgraded to the latest release. If the licensing system that you used before does not require a subscription to work, then no further action is necessary and existing users using older versions of your software would be unaffected.
## Practical implementation
**Q: How to re-issue licenses to existing users so that they can activate newer versions of the software that use Cryptolens?**
This depends on the number of customers and licenses that you have in the old system. If it’s just a few, the easiest way would be to issue the licenses manually. However, if you have hundreds of licenses and customers, we would suggest to use our Web API to automate it. There are two ways this can be accomplished:
**Method 1:** You can iterate through all the licenses in your old licensing system and then call [CreateKey](https://app.cryptolens.io/docs/api/v3/CreateKey) method to issue a license in Cryptolens.
**Method 2:** If you have a website already, you could create a page where customers can enter their old license key and then obtain a new license key from Cryptolens. To obtain a new license key from Cryptolens, you can call [CreateKey](https://app.cryptolens.io/docs/api/v3/CreateKey) method. It’s important to keep track of the old licenses that have already received a new license in Cryptolens, so that if a customer attempts to enter the same license again, they receive the previously issued license key in Cryptolens.
# Offline verification
Source: https://help.cryptolens.io/examples/offline-verification
An example of how to verify/activate licenses without access to the internet. In the end of the tutorial, an example is shown on how to to create a license file, aka activation file or certificate. Such a file can be used by your clients to verify a license offline.
Although Cryptolens is a web-based service, we can still use it to protect applications that have no direct access to the internet (eg. air-gapped devices). This is especially useful if you plan to sell your software to larger enterprises.
You can find example projects [here](https://github.com/Cryptolens/Examples/tree/master/offline-verification).
If you prefer to watch a video, please check out [this post](https://cryptolens.io/2024/07/offline-license-verifications/). You can also find the notes used in the video tutorial on [this page](https://help.cryptolens.io/content/offline-verification.pdf).
## Idea
To verify licenses without internet access, Cryptolens uses public-key cryptography to sign each response that is sent to your application. You can think of each of these responses as a **certificate** or **license file**.
> The reason why we had to include your RSA public key in the [Key Verification](https://help.cryptolens.io/examples/key-verification) tutorial was to verify the signature provided by Cryptolens.
So, to be able to verify licenses offline, all you have to do is to provide your customers with this response/certificate. There are many different ways of setting this up, which we have outlined below:
* **Key verification once or periodically** - the easiest way of delivering this **certificate** is to require your users to perform [verification](https://help.cryptolens.io/examples/key-verification) once (and then keep using a cached version of the certificate) or to [verify](https://help.cryptolens.io/examples/key-verification) the license key periodically (eg. you try to verify the license each time the application starts and if it fails you use the cached certificate, provided it has not expired).
* **Using USB stick** - if your customers want to use your application on an [air-gapped](https://en.wikipedia.org/wiki/Air_gap_\(networking\)) device, you can deliver the certificate on a USB stick or by other means. Once your software sees this file, it will know that it is a valid license.
## Implementation
We have outlined two examples with detailed code examples:
* [Periodic key verification](https://help.cryptolens.io/examples/offline-verification#periodic-key-verification)
* [Example code](https://help.cryptolens.io/examples/offline-verification#example-code)
* [How it works](https://help.cryptolens.io/examples/offline-verification#how-it-works)
* [Remarks](https://help.cryptolens.io/examples/offline-verification#remarks)
* [USB stick (air-gapped)](https://help.cryptolens.io/examples/offline-verification#usb-stick-air-gapped)
* [Obtaining the license file / certificate](https://help.cryptolens.io/examples/offline-verification#obtaining-the-license-file--certificate)
* [Example code](https://help.cryptolens.io/examples/offline-verification#example-code-1)
### Periodic Key Verification
If you have already used the code in the [Key Verification](https://help.cryptolens.io/examples/key-verification) tutorial, you only need to add few lines of code to get it to work. The additional code that has to be added is shown below (we will explain how it works later in the tutorial):
**Note:** The value `3` passed as a parameter to `HasValidSignature` (in .NET) or `LoadFromString` (in Java) is used to specify the expiration date of the license file. In this case, users can only be offline for at most 3 days until they need to reconnect again.
#### Example code
```c# C# theme={null}
// ...
if (result == null || result.Result == ResultType.Error ||
!result.LicenseKey.HasValidSignature(RSAPubKey).IsValid())
{
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
// -------------------new code starts -------------------
// we will try to check for a cached response/certificate
var licensefile = new LicenseKey();
if (licensefile.LoadFromFile("licensefile")
.HasValidSignature(RSAPubKey, 3)
.IsValid())
{
Console.WriteLine("The license is valid!");
}
else
{
Console.WriteLine("The license does not work.");
}
// -------------------new code ends ---------------------
}
else
{
// everything went fine if we are here!
Console.WriteLine("The license is valid!");
// -------------------new code starts -------------------
// saving a copy of the response/certificate
result.LicenseKey.SaveToFile("licensefile");
// -------------------new code ends ---------------------
}
```
```VB.NET VB.NET theme={null}
' ...
If result Is Nothing OrElse result.Result = ResultType.[Error] OrElse
Not result.LicenseKey.HasValidSignature(RSAPubKey).IsValid Then
' an error occurred or the key is invalid or it cannot be activated
' (eg. the limit of activated devices was achieved)
' -------------------New code starts -------------------
' we will try to check for a cached response/certificate
Dim licensefile As New LicenseKey()
If licensefile.LoadFromFile("licensefile").HasValidSignature(RSAPubKey, 3).IsValid() Then
Console.WriteLine("The license is valid!")
Else
Console.WriteLine("The license does not work.")
End If
' -------------------new code ends ---------------------
Else
' everything went fine if we are here!
Console.WriteLine("The license is valid!")
' -------------------New code starts -------------------
' saving a copy of the response/certificate
result.LicenseKey.SaveToFile("licensefile")
' -------------------New code ends ---------------------
End If
```
```java Java theme={null}
// ...
String currentMachineId = "test"; // usually you can call Helpers.GetMachineCode() to obtain this value. Other platforms, such as Android, require a different identifier.
if (license == null || !Helpers.IsOnRightMachine(license, currentMachineId)) {
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
// -------------------new code starts -------------------
// we will try to check for a cached response/certificate
String contents = "";
try {
contents = new String(Files.readAllBytes(Paths.get("licensefile.skm")));
} catch (IOException e) {
e.printStackTrace();
return;
}
LicenseKey licenseFile = LicenseKey.LoadFromString(RSAPubKey, contents, 3);
if(licenseFile != null && Helpers.IsOnRightMachine(licenseFile, currentMachineId)) {
System.out.println("Offline mode");
System.out.println("The license is valid!");
System.out.println("It will expire: " + licenseFile.Expires);
} else {
System.out.println("The license does not work.");
}
// -------------------new code ends ---------------------
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
// -------------------new code starts -------------------
// saving a copy of the response/certificate
try {
PrintWriter pw = new PrintWriter("licensefile.skm");
pw.println(license.SaveAsString());
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// -------------------new code ends ---------------------
}
```
#### How it works
The code examples above will always try to access Cryptolens to check a license key. If a request to Cryptolens would fail, we check for an **cached** version of the response/certificate and that it has **\*not expired** (eg. the last successful access was at most 3 days ago).
#### Remarks
The examples use methods `SaveToFile`and `LoadFromFile`, which are useful abstractions so that you do need to worry about serialization and storage. In some cases, it can be useful to store the `LicenseKey` in some other way. In that case, you just to need serialize the `LicenseKey` object. You can also use [settings variables](https://help.cryptolens.io/web-api/dotnet/v401#storing-a-license-key-in-a-file).
Note, you can perform the verification once with Cryptolens and then keep using the cached license file. We don’t recommend this though. It’s better to have a longer expiration date on the cached certificates instead.
### USB stick (air-gapped)
In order to check a license key on an [air-gapped device](https://en.wikipedia.org/wiki/Air_gap_\(networking\)), we need to first load the certificate and then verify it. In the example code, we also pass in `365` as a parameter to `HasValidSignature` (in .NET) or `LoadFromString` (in Java). This is used to ensure that users need to update the license file becomes invalid in 365 days, requiring the users to obtain a new license file.
Note, on an air-gapped device, it’s important to check that the certificate is locked to to that device (aka machine code locking), which is why we have included `IsOnRightMachine()` call.
#### Obtaining the license file / certificate
The license file can be obtained in a variety of ways. In the end, a license file/certificate is just the response from the Key.Activate or GetKey method. We have listed three ways to obtain such a file:
* **Activation Forms** is a page hosted by Cryptolens that allows your clients to activate a device by providing a license key and the machine code. You can read more about them [here](/feature/activation-form/index).
* **Dashboard** can be used to download a license file. It can be done by navigating to the product page, and clicking on the yellow button next to the license key. A new window will open where you can either activate a new machine or download license file without activating (using *Download activation file* link).
* **API** can also be used to activate a new machine or obtain the license file. For example, you could call Key.Activate or Key.GetKey on your end, save the license key to file (using the built in methods) and then send this file to your client.
Note: if you plan to activate many machines on customer site, please check out the [following guide](https://github.com/Cryptolens/admin-tools/tree/master/manual-activation) that provides scripts and other tools to help your larger clients to generate a list of machines to activate.
#### Example code
For the code below, we need to ensure that:
* **machine code locking** is enabled. You can change this for existing keys by selecting the key and providing the value in the box below (remember to save):
* **activation forms** or another way of obtaining the certificate is present (which we discussed in *Obtaining the license file / certificate*). Activation Forms are easy to set up and can be done [here](https://app.cryptolens.io/ActivationForms). All your customer has to do is to enter the license key and machine code in the form and then move the file into the same directory as your application.
Note, the name of the files returned by the activation form can differ, so it’s better to ask the customer about the name of it or ask them to rename the file obtained from the activation with the one hardcoded in the code below:
```c# C# theme={null}
var RSAPubKey = "{enter the RSA Public key here}";
var licensefile = new LicenseKey().LoadFromFile("ActivationFile20180606.skm");
if(licensefile.HasValidSignature(RSAPubKey, 365).IsOnRightMachine(SKGL.SKM.getSHA256).IsValid())
{
// if you have multiple products, make sure the license file has correct product id.
//if(licensefile.ProductId != 123)
//{
// Console.WriteLine("This license file is not for this product.");
// return;
//}
Console.WriteLine("License verification successful.");
}
else
{
Console.WriteLine("The license file is not valid or has expired.");
Console.WriteLine("Please obtain a new one here: https://app.cryptolens.io/Form/A/onp4cDAc/222");
Console.WriteLine("Your machine code is: " + Helpers.GetMachineCode());
}
```
```VB.NET VB.NET theme={null}
Dim RSAPubKey = "{enter the RSA Public key here}"
Dim licensefile = New LicenseKey().LoadFromFile("ActivationFile20180606.skm")
If (licensefile.HasValidSignature(RSAPubKey, 365).IsOnRightMachine(AddressOf SKGL.SKM.getSHA256).IsValid()) Then
' if you have multiple products, make sure the license file has correct product id.
'If (licensefile.ProductId <> 123) Then
' Console.WriteLine("This license file is not for this product.")
' Return
'End If
Console.WriteLine("License verification successful.")
Else
Console.WriteLine("The license file is not valid or has expired.")
Console.WriteLine("Please obtain a new one here: https://app.cryptolens.io/Form/A/onp4cDAc/222")
Console.WriteLine("Your machine code is: " + Helpers.GetMachineCode())
End If
```
```java Java theme={null}
String currentMachineId = "test"; // usually you can call Helpers.GetMachineCode() to obtain this value. Other platforms, such as Android, require a different identifier.
String contents = "";
try {
contents = new String(Files.readAllBytes(Paths.get("ActivationFile20180606.skm")));
} catch (IOException e) {
e.printStackTrace();
return;
}
LicenseKey licenseFile = LicenseKey.LoadFromString(RSAPubKey, contents, 365);
if(licenseFile != null && Helpers.IsOnRightMachine(licenseFile, currentMachineId)) {
// if you have multiple products, make sure the license file has correct product id.
/*if(licenseFile.ProductId != 123) {
System.out.println("This license file is not for this product.");
return;
}*/
System.out.println("License verification successful.");
System.out.println("The license is valid!");
System.out.println("It will expire: " + licenseFile.Expires);
} else {
System.out.println("The license file is not valid or has expired.")
System.out.println("Please obtain a new one here: https://app.cryptolens.io/Form/A/onp4cDAc/222")
System.out.println("Your machine code is: " + currentMachineId)
}
```
# Reverse proxy
Source: https://help.cryptolens.io/examples/reverse-proxy
Explains how you can connect to the Cryptolens API using your own domain
## Idea
The purpose of using a reverse proxy is to provide a custom URL and IP address for verifying license keys. For example, this can be useful if the IP must originate from a specific geographical location or if you want to use your own domain. A reverse proxy can accomplish both.
## Getting started
To get started, the following steps are recommended:
1. Obtain a Linux VM.
2. Install Nginx.
3. Change the `default.conf` in the `/etc/nginx/conf.d` to the one in this repository. The key is to include all the "location" blocks (and add more if necessary), as well as to set the domain name that you will be using.
4. If you link a subdomain to the VM, you can use Let's Encrypt to manage the SSL certificate.
The contents of the **default.conf** is shown below:
```default.conf expandable theme={null}
server {
server_name localhost api.yourdomain.com;
#listen [::]:80 default_server;
#access_log /var/log/nginx/host.access.log main;
location /api/key/activate {
proxy_pass https://api.cryptolens.io/api/key/Activate;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Powered-By;
proxy_hide_header Request-Context;
proxy_hide_header Set-Cookie;
}
location /api/key/Activate {
proxy_pass https://api.cryptolens.io/api/key/Activate;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Powered-By;
proxy_hide_header Request-Context;
proxy_hide_header Set-Cookie;
}
location /api/key/createtrialkey {
proxy_pass https://api.cryptolens.io/api/key/CreateTrialKey;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Powered-By;
proxy_hide_header Request-Context;
proxy_hide_header Set-Cookie;
}
location /api/key/CreateTrialKey {
proxy_pass https://api.cryptolens.io/api/key/CreateTrialKey;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Powered-By;
proxy_hide_header Request-Context;
proxy_hide_header Set-Cookie;
}
}
```
The repository with the code is available at: [https://github.com/Cryptolens/reverse-proxy](https://github.com/Cryptolens/reverse-proxy)
# User verifications
Source: https://help.cryptolens.io/examples/user-verification
A simple implementation of user verifications.
This article is a draft and is continuously being updated.
Instead of authenticating users using their license key, which was described in the [key verification](/examples/key-verification) tutorial, you can authenticate them using their username and password. This can be preferred in the following use cases:
1. When your customers have multiple licenses.
2. When your service offers a web-version and a desktop version.
In case it is the first use case when customers have many licenses and you want to issue them one single license, please check out [customer secret](https://help.cryptolens.io/web-interface/customer-secret) tutorial.
When you set up user authentication using Cryptolens, you can manage the entire process yourself from your backend, including creation of new users, restoring password, etc. Similar to [key verification](/examples/key-verification), your customers will not have to interact with Cryptolens dashboard and you have control over the entire user experience.
If you would prefer Cryptolens to manage the user experience, you can use [the following](https://help.cryptolens.io/licensing-models/user-login-intro) approach instead.
## Getting started
You can read more about all the methods that are available in the [Web API documentation](https://app.cryptolens.io/docs/api/v3/UserAuth). The goal of this tutorial is to focus on the authentication method that retrieves all licenses that belong to a user.
To authenticate the user inside your application, we can use the code below. When `Login` method is called, it will return all the licenses that belong to your customer (assuming that the User is linked to a Customer in Cryptolens).
The code examples below require an access token with either **UserAuthNormal** or **UserAuthAdmin** permission. When running the code inside your application, please assign the access token **UserAuthNormal** permission only.
In **C#**, the following code could be used as an example:
```c# C# theme={null}
var result = UserAuth.Login("access token with UserAuthNormal permission", new LoginUserModel { UserName = "username", Password = "password" });
if(!Helpers.IsSuccessful(result))
{
// error
}
var license = result.LicenseKeys.FirstOrDefault(x => x.ProductId == 123);
if(license != null)
{
// no license for the product found
}
// proceed with activation as usual (if you want to use node-locking etc).
Key.Activate("", new ActivateModel { ProductId = 123, Key = license.Key, MachineCode = Helpers.GetMachineCodePI(v: 2) });
```
```python Python theme={null}
# res will be a tuple, where the first item contains the list of licenses or None.
res = User.login("access token with UserAuthNormal permission", "username","password")
if res == None:
print("No licenses found")
# find the first license object that comes from product 123.
# if you want to obtain all licenses from product with id 123 (just an example), you
# can use licenses = [obj for obj in res[0] if obj['productId'] == 123]
license = next((obj for obj in res[0] if obj['productId'] == 123), None)
if license != None:
# extract the license key and call Key.activate as described in the key verification tutorial.
```
# Trial keys
Source: https://help.cryptolens.io/examples/verified-trials
Introduction to verified trial keys
A trial key allows your users to evaluate some or all parts of your software for a limited period of time. The goal of trial keys is to set it up in such a way that you don’t need to manually create them, while still keeping everything secure.
In Cryptolens, all trial keys are bound to the device that requested them, which helps to prevent users from using the trial after reinstalling their device.
You can define which features should count as trial by [editing feature definitions](https://help.cryptolens.io/web-interface/feature-definitions) on the product page.
Note: the duration of the trial is 15 days by default. You can change that by creating an access token that has **FeatureLock** set to the number of days the trial should be valid. We recommend to create a separate access token for `Key.CreateTrialKey` since the feature lock can interfere with other methods, such as Activate (where it acts as mask for fields).
## Example
In order to create a trial key, you only need to call `Key.CreateTrialKey`. This method will either return a new trial key or an existing one (if this command was called before).
Once that is done, you can perform [key verification](https://help.cryptolens.io/examples/key-verification) as usual.
**Note**, it’s very important to check `Helpers.IsOnRightMachine(activate.LicenseKey)` is true, in order to make sure that the trial belongs to the right machine.
The code below is an example of how trial key creation can be set up together with a key verification afterwards.
```c# C# theme={null}
var newTrialKey = Key.CreateTrialKey("access token", new CreateTrialKeyModel { ProductId= 3941, MachineCode =Helpers.GetMachineCode() });
if(newTrialKey == null || newTrialKey.Result == ResultType.Error)
{
Assert.Fail("Something went wrong when creating the trial key");
}
var activate = Key.Activate("access token",
new ActivateModel {
ProductId = 3941,
Sign = true,
MachineCode = Helpers.GetMachineCode(),
Key = newTrialKey.Key, Metadata = true
});
if(activate == null || activate.Result == ResultType.Error)
{
Assert.Fail("Something went wrong when verifying the trial key");
}
// now we can verify some basic properties
if (Helpers.IsOnRightMachine(activate.LicenseKey) && activate.Metadata.LicenseStatus.IsValid)
{
// license verification successful.
return;
}
```
```python Python theme={null}
from licensing.models import *
from licensing.methods import Key, Helpers
trial_key = Key.create_trial_key("WyIzODQ0IiwiempTRWs4SnBKTTArYUh3WkwyZ0VwQkVyeTlUVkRWK2ZTOS8wcTBmaCJd", 3941, Helpers.GetMachineCode(v=2))
if trial_key[0] == None:
print("An error occurred: {0}".format(trial_key[1]))
RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB"
auth = "WyIyNTU1IiwiRjdZZTB4RmtuTVcrQlNqcSszbmFMMHB3aWFJTlBsWW1Mbm9raVFyRyJd=="
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key=trial_key[0],\
machine_code=Helpers.GetMachineCode(v=2))
if result[0] == None or not Helpers.IsOnRightMachine(result[0], v=2):
print("An error occurred: {0}".format(result[1]))
else:
print("Success")
license_key = result[0]
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
# Integrate with Zapier
Source: https://help.cryptolens.io/examples/zapier
By using our Zapier app, you can connect Cryptolens to 2000+ other web services.
**Note:** To use our Zapier app, the account needs to be on the Standard/Growth tier, or higher.
## Introduction
[Zapier](https://zapier.com/apps/Cryptolens/integrations) lets you connect Cryptolens to 2,000+ other web services. Automated connections called Zaps, set up in minutes with no coding, can automate your day-to-day tasks and build workflows between apps that otherwise wouldn’t be possible.
Each Zap has one app as the **Trigger**, where your information comes from and which causes one or more **Actions** in other apps, where your data gets sent automatically
We have added a variety of actions and triggers that help you to automate a wide range of licensing tasks. From automatically creating new customer, to triggers that detect issuance of new licenses.
## How do I connect Cryptolens to Zapier?
1. Log in to your [Zapier account](https://zapier.com/sign-up) or create a new account.
2. Navigate to “My Apps” from the top menu bar.
3. Now click on “Connect a new account…” and search for “Cryptolens”
4. Use your credentials to connect your Cryptolens account to Zapier.
5. Once that’s done you can start creating an automation! Use a pre-made Zap or create your own with the Zap Editor. Creating a Zap requires no coding knowledge and you’ll be walked step-by-step through the setup.
6. Need inspiration? See everything that’s possible with [Cryptolens and Zapier](https://zapier.com/apps/Cryptolens/integrations).
If you have any additional questions, you can reach out to [support@cryptolens.io](mailto:support@cryptolens.io) or [contact@zapier.com](mailto:contact@zapier.com).
## Authentication
To authenticate, you will need an [access token](https://app.cryptolens.io/User/AccessToken#/). The scope of permissions depends on the action or trigger that you plan to use.
For triggers, you would need an access token with `Get Web API Log` permission. For the actions, you would need the permission that corresponds to the method name. For example, **Add Customer** method requires `Add Customer` permission.
## Examples
We have listed examples with screenshots of how to set up common Zapier workflows. More will be documented soon.
* [License expiry notification with Zapier](/examples/zapier-license-expiry-notice)
We recommend that you review our[ n8n integration page](/examples/integration-with-n8n) for more examples.
## Popular use cases
We have listed a few example integrations with other services below:
# FAQ
Source: https://help.cryptolens.io/faq
Frequently asked questions about Cryptolens.
## Platform
### Expiry date
When you create a new license, you are asked to set when it should expire. On the product page, every license has a field called `Expires`. The question is, how can we create licenses that never expires?
It turns out that **expiration date will not affect the validity** of a license by default. For example, all code snippets provided in the [key verification tutorial](https://help.cryptolens.io/examples/key-verification) assume that a license is time-unlimited.
This may feel a bit counter-intuitive and we are continuously working on improving the APIs to make this more trivial.
For now, if you see that a code-example contains a call to the `activate` method (which all examples do at the moment), it means that you need to check the expiration date even if you get a successful result. A way to do this is to call `HasNotExpired` (depending on the library) or simply check the `Expire` field. If you plan to support both time-limited and time-unlimited licenses, you can use one of the feature flags as a way to distinguish this. Please read more [here](https://help.cryptolens.io/web-interface/keys-that-dont-expire).
In order to ensure that licenses stop working after that they have expired, you can select “Block Expired Licenses” when editing [feature names](https://help.cryptolens.io/web-interface/feature-definitions). Expired licenses will be blocked within an hour. Note: the following licenses will not be blocked:
* if the license key has a data object with the name **cryptolens\_stripesubscriptionitemid**, since these licenses are managed by Stripe through the recurring billing module.
* if [start countdown upon activation](https://help.cryptolens.io/web-interface/trial-activation) (aka trial activation) is enabled, provided that the maximum number of activations has not been reached. For example, if you have created a license with `start countdown upon activation=true` and `MaxNoOfMachines=1`, it will not be blocked if it has not been activated. This way, you can pre-generate the licenses and ensure that your customer can still activate them, even when the expiration date has passed. However, once it has been activated, it will be blocked after the new expiration date (assuming that the license is marked as time-limited).
If you do not know when your customers will activate the license for the first time but you still want them to use it for a set number of days, you can enable [trial activation](https://help.cryptolens.io/web-interface/trial-activation).
You may noticed that you can [edit feature definitions](https://help.cryptolens.io/web-interface/feature-definitions) in each product. They can be used as a way to help you to keep track of what each feature flag means and they also help our platform understand how to display a certain license in a meaningful way (eg. if F1 stands for a time-limited license and it’s not enabled for a certain license, there won’t be an option to prolong it).
Recently, we have added support in our Web API that takes into account the feature definitions and adjusts the response sent to the client. Our plan is to integrate this into all client APIs so that you can have most of the license logic set up in the dashboard.
### Maximum number of machines
Maximum number of machines is a way to specify how many unique machine codes can be added to a certain license (using the `Activate` method). When the limit is reached, no more machine codes will be added. There are two special cases that is important to keep in mind:
Setting maximum number of machines to zero turns this feature off, i.e. machine codes will not be added to the license. It means users will be able to run the software on any number of machines.
Note, `Helpers.IsOnRightMachine()` will return false if no machine code is registered with the license, which will be the case if maximum number of machines is set to 0. As a solution, please check the `MaxNumberOfMachines` field, ensuring it is not 0, before calling `Helpers.IsOnRightMachine()`.
Let’s say you had maximum number of machines set to 10 and one customer has used up the machine code quota, i.e. they have activated on 10 computers. If you decrease this value to something less than 10, eg. 5, all of the activated machines will still work. That’s because the platform does not know which machine codes should work and which should not. If you would like to remove some of the machines codes, you can click on the yellow button next to each license and remove the machine codes in from the list.
There are two ways you can restrict the number of machines that can use a license:
1. [Node-locked](https://help.cryptolens.io/license-models/node-locked): Either you restrict a license to a certain number of machines indefinitely (i.e. until they are deactivated)
2. [Floating](https://help.cryptolens.io/license-models/floating): or you allow a license to “float” between different machines, as long as at any one point this upper bound of machines is never exceeded
For trial keys, it’s better to use the first approach whereas for paid licenses, either approach will work fine, although floating licenses will be more convenient for your customers and remove the hassle of license deactivation.
There is a difference between how [node-locked](https://help.cryptolens.io/licensing-models/node-locked) and [floating licenses](https://help.cryptolens.io/licensing-models/floating) work, and how the maximum number of machines limit is enforced. For node-locked licenses, the device will be registered with the license key and a list of these devices can be found in the `ActivatedMachines` property of a LicenseKey or in the list of *Activated devices* (in the dashboard). You will need to deactivate unused devices if want to allow new devices to use the license.
In the floating license case, the devices will be registered with the license key temporarily and it will therefore not be possible to list all of them in `ActivatedMachines` property. Only the floating license device that is being activated in the request will be shown.
**To sum up**: in the node-locked case, activated devices will remain activated (unless deactivated at a later point), whereas for a floating device to remain active, it needs to send periodic **heartbeats**, within a time-window. The length of this time-window is specified using `FloatingTimeInterval` variable. If a device fails to send a request (aka heartbeat) within that interval, it will automatically be deactivated and other devices will be able to activate (i.e. use that seat).
To list all devices, including those registered on a floating license model, you can click on the yellow button next to the license key:
The `FloatingTimeInterval` is set to 100 by default and can easily be changed.
**Note** The number of machines activated using node-locking model will not affect the number of machines in the floating license model. In other words, if maximum number of machines is set to **5**, then you can have 5 unique devices registered using the node-locked model and another 5 devices can use the license key concurrently (floating license model).
The **friendly name** is a way to assign a name to an activated device so that both you and your customers can keep track of which activation belongs to which user. Normally, the **machine code** contains the fingerprint of a device, for example, `9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08`. If you customer has many end users (i.e. activated devices), navigating between these device fingerprints can become troublesome. To resolve this, you can provide a `FriendlyName` when you call `Key.Activate`. At the time of writing, this is supported in our .NET client.
There are a couple of ways you can compute the friendly name for a particular user. At this point, our recommendation is to set it to [Environment.MachineName](https://docs.microsoft.com/en-us/dotnet/api/system.environment.machinename?view=netstandard-2.1). For example, if your customers use Active Directory, the machine name will show up in the list of devices (for a particular user) in Active Directory Admin dashboard:
When your customers have a large number of employees, we recommend to give them access to the customer portal with activation/deactivation permission. You can access a generic sign up link [here](https://app.cryptolens.io/Customer/SignUpLink). Once they are logged in, they will be able manage their licenses and activations in the [customer portal](https://app.cryptolens.io/Portal/Admin).
In most cases, an **end user** is defined as a unique device using a certain license. In our examples, we achieve this by calling the `Key.Activate` method with `MachineCode=Helpers.GetMachineCode()`. The `Helpers.GetMachineCode()` will return a device fingerprint that is unique for each device.
However, there are other ways end users can be defined. The choice will depend on your specific licensing model. We will briefly outline several other ways it can be defined:
**Per process**
If your customers will be able to run multiple instances on the same machine, you can define the end user so that includes the process id in addition to the fingerprint of the device. For example, it can be defined in .NET as follows:
```
MachineCode = Helpers.GetMachineCode() + System.Diagnostics.GetCurrentProcess().Id
```
**Per user**
The end user can also be defined as the currently logged in user (for example, Active Directory user), allowing the same user to use their license on multiple machines where they are logged in.
**Per instance**
When the identifier changes frequently (for example, in the case of [docker containers](https://help.cryptolens.io/licensing-models/containers)) or when there is no reliable way to obtain an identifier (for example, in the case with virtual machines), it is better to generate a random identifier each time the application starts, use it only within one session and the discard it. Since this will lead to a large number of new end users being registered with the license, we recommend to apply the [floating license model](https://help.cryptolens.io/licensing-models/floating).
In C# and Python, the GUID can be obtained as follows:
```c# C# theme={null}
var machineCode = Guid.NewGuid();
```
```python Python theme={null}
import uuid
machine_code = uuid.uuid4().hex
```
**Per network location**
You can also use treat all users within the same network as one end users. For example, this would allow all employees within the same geographical location to be treated as one end user.
### Protocols
Cryptolens uses two different protocols to deliver license key information to the client during activation:
* **.NET compatible** (aka LingSign): if you use C# or [VB.NET](http://VB.NET) (unless you use Unity/Mono specific methods, in which case *Other languages* protocol is used)
* **Other languages** (aka StringSign): if you use C++, Java or Python
Most of the clients have methods that allow to load a license key object from file or from String. For example, [LoadFromFile (.NET)](https://help.cryptolens.io/api/dotnet/api/SKM.V3.ExtensionMethods.html#SKM_V3_ExtensionMethods_LoadFromFile_SKM_V3_LicenseKey_), [LoadFromString (Java)](https://help.cryptolens.io/api/java/io/cryptolens/models/LicenseKey.html#LoadFromString-java.lang.String-java.lang.String-int-) or [load\_from\_string (Python)](https://help.cryptolens.io/api/python/#licensing.models.LicenseKey.load_from_string).
### Securing your account with 2FA
We recommend to set up two factor authentication to secure your account. At the moment, we support the TOTP protocol. You can install an authenticator app on your phone or use Yubico authenticator in case you have a Yubico security key. For example, if you have Yubikey, you can active two factor authentication as follows:
1. Visit the [two factor set up page](https://app.cryptolens.io/Account/TwoFactorSetUp).
2. Click on “Enable two step authentication”.
3. Open [Yubico authenticator](https://www.yubico.com/products/services-software/download/yubico-authenticator/) and click on the plus sign to add a new account. If the QR code is visible, Yubico Authenticator will automatically recognize it. We recommend to require touch to access the credentials.
4. Save the backup code in a secure place.
5. You have now successfully enabled two-factor authentication!
## Client APIs / SDKs
Machine codes are used to uniquely identify an end user instances, i.e. a machine code is a device fingerprint. The way it is generated depends on the SDK and the platform, which is listed below.
#### .NET
Prior to v4.0.15, machine codes have used [following code](https://github.com/Cryptolens/cryptolens-dotnet/blob/master/Cryptolens.Licensing/SKM.cs#L1233) to gather device specific information and later hash it using either SHA1 or SHA256. The problem with this method is that it is Windows specific and requires [System.Management](http://System.Management) (which is not supported in Mono when integrating with Unity). To solve this, we opted for platform specific methods to retrieve the UUID. You can read more about how to migrate [here](https://help.cryptolens.io/api/dotnet/articles/v4015.html#platform-independent-machine-code-method). Depending on the platform, the following shell calls are made to retrieve the UUID:
**Windows**
Two methods are currently being used. In older versions of the library (and when v=1), the following command is used in platform independent methods (i.e. with PI extension).
```
cmd.exe /C wmic csproduct get uuid
```
In newer versions, the following method is used (which returns the same UUID) as above.
```
cmd /c powershell.exe -Command \"(Get-CimInstance -Class Win32_ComputerSystemProduct).UUID\"\n"
```
The reason for the switch is that WMIC will no longer be supported in future versions of Windows 11.
**Mac**
```
system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }'
```
**Linux**
If we cannot determine the type of hardware, the following method will be used:
```
findmnt --output=UUID --noheadings --target=/boot
```
If we can detect that the application runs on a Raspberry PI (can be determined by calling `cat /proc/device-tree/model`), we will extract the “Serial” by running the following command:
```
cat /proc/cpuinfo
```
Alternative way to identify Linux instances is to run the command below. Note that it requires sudo access.
```
dmidecode -s system-uuid
```
The old method (prior v4.0.15) is implemented in [Helpers.GetMachineCode](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Helpers.html#SKM_V3_Methods_Helpers_GetMachineCode_System_Boolean_). The new method that uses the UUID from the host OS is implemented in [Helpers.GetMachineCodePI](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Helpers.html#SKM_V3_Methods_Helpers_GetMachineCodePI). If you call `Helpers.GetMachineCodePI(v:2)` in .NET, you will get the same machine code generated on Windows as in the Python library with similar settings.
#### Java
In Java, the [following method](https://github.com/Cryptolens/cryptolens-java/blob/master/src/main/java/io/cryptolens/methods/Helpers.java#L23) is used by default.
Since v1.23, you can generate the same machine code as in the Python library and .NET libraries by calling `Helpers.GetMachineCode(2)` on Windows. It will use the UUID of the device and works in the `cryptolens-android.jar`, which does not depend on `slf4j`.
#### Python
In Python, similar to .NET, we opted for UUID, which can be provided by the OS. You can see the source code [here](https://github.com/Cryptolens/cryptolens-python/blob/master/licensing/methods.py#L167). Note, the machine code will not be the same as in .NET with default parameters. If you call `Helpers.GetMachineCode(v=2)` in Python and `Helpers.GetMachineCodePI(v: 2)` in .NET, the machine code will be the same on Windows.
#### C++
In C++, we use the same method that was used in .NET prior to v4.0.15. The source code can be found [here](https://github.com/Cryptolens/cryptolens-cpp/blob/master/src/MachineCodeComputer_COM.cpp). We are working on shipping a platform independent version in the next release.
#### Plan ahead
Our plan is to introduce a platform independent method to retrieve the UUIDs, in order to ensure that machine codes are the same for all client libraries. Currently, there is a way to get the same machine code on Windows in .NET and Python clients.
If we take the [key verification tutorial](/examples/key-verification) as an example, there are three parameters that are specific to your account: `RSA Public Key`, `Access Token` and `ProductId`.
#### RSA Public Key
The RSA public key is used to verify a license key object sent by Cryptolens. This is especially useful if you want to implement [offline activation](/examples/offline-verification) since we don’t want any of the properties (eg. features and expiration date) to be changed by the user.
> Note: the RSA public key can only be used to verify a license key object, the private key that is used for signing is stored on our server.
#### Access Token
An access token tells Cryptolens who you are (authentication) and what permissions are allowed (authorization). In other words, it can be thought of as username and password combined, but with restricted permission.
It’s recommend to restrict access tokens as much as possible, both in terms of what it can do (eg. Activate or Create Key) and what information it returns (read more [here](https://help.cryptolens.io/legal/DataPolicy#using-feature-lock-for-data-masking)). For example, you should preferably only allow access tokens used in the client application to `Activate` a license key. The permission to `Create Key` should only be accessible in applications that you control, eg. on your server.
So if you have a restricted access token, the chances that an adversary will be able to do any harm is minimal. For example, the worst that an adversary can do is to activate more devices (up to a [certain limit](https://help.cryptolens.io/faq/index#maximum-number-of-machines)), which can be fixed quite easily in the dashboard.
When implementing Cryptolens into your software, you may get different kinds of errors depending if you call the API directly or through one of the client libraries. This section covers the most common errors, why they occur and how to troubleshoot them.
The majority of errors (99%) are caused by a wrong **product id**, **access token** or **RSA Public key**. In some cases, it can also be because an active subscription is missing. Our client libraries might not always show the real error message and instead display a generic error. To find out the real error message from the Web API, you can call the method through curl. For example, to check the access token, you can call curl as shown below:
```
curl https://app.cryptolens.io/api/key/Activate? \
-d token={access token} \
-d ProductId={product id} \
-d Key={license key string}
```
You can also check this with the browser. The call above would translate to:
```
https://app.cryptolens.io/api/key/Activate?token={access token}&ProductId={product id}&Key={license key string}
```
The most common errors and their cause is summarized below:
| Error message | Description |
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Unable to authenticate` | The access token is wrong. It is also shown if the subscription has expired. API access requires an active subscription. |
| `Something went wrong. Please contact support@cryptolens.io and we will try to fix it.` | A temporary server error. Try again with a new request. |
| `No active subscription found. Please contact support.` | No active subscription is associated with the account and thus the API call failed. The error can be fixed by updating your billing information and add a valid payment method on the [billing page](https://app.cryptolens.io/billing). |
| `Access denied` | The access token is correct but it does not have sufficient permission. Make sure that if you, for example, want to call GetKey that the access token has “GetKey” permission. |
| `Not enough permission and/or key not found.` | If you have checked that the product contains the license key and the access token has the right permission to call the method, this error can be fixed by setting Key Lock to -1 when creating the access token (assuming that you are working with data object related methods). |
## Billing
When you sign up for an account, you get 30 days to test the service for free and you can continue using Cryptolens for free on the Free tier after 30 days. If you would like to upgrade to a paid tier after the trial, it’s important to add a valid credit card before 30 days trial has elapsed. After the trial, the account will be downgraded to the Free tier.
### How monthly pricing is computed
To find out how monthly pricing is calculated, please visit the [billing page](https://app.cryptolens.io/Billing) and click on the link **pricing page** that will allow you to estimate your usage.
If your billing page includes the **End users** property, please read the section below to understand how end users are computed.
#### If you are charged for end users
The pricing is entirely based on usage. It’s based on two values: the number of **active licenses** (i.e. those that are not blocked) and the number of active end users.
For example, let’s suppose that you have 20 licenses and 40 end users. In that case, since there are more than 10 end users, the service fee (based on end users) will be 50 (in the standard package). Since there are 20 licenses, the total cost for them will be 20 \* 0.1 = 2. The sum will be 52 per month. If your usage goes down, the price will go down too.
To compute the number of **active end users**, we count the number of unique (license, machine code) pairs in a certain period of time (for node-locked licenses, it’s one year back and for floating licenses it’s a month back). For example, if a license key is set to work on at most one machine, then if each machine verifies the license continuously and there are 10 licenses in total, this will result in 10 end users. If you perform license key verifications periodically or only once, this value will be lower. A unblocked license will always count as at least 1 active end user.
Note: for customers inside the EU, we will add an additional VAT on top of the price if no VAT number has been provided. The currency is EUR for customers inside EEA and USD otherwise.
# Activation Forms
Source: https://help.cryptolens.io/feature/activation-form/index
Activation Forms allow your clients to download an activation file on machine that has internet access, so that they can activate machines that are air-gapped.
## Idea
Normally, when you have internet access, you can call the [key verification](/examples/key-verification) method, which returns a license object/file (signed JSON file). Once you have the license object, you can verify if the customer has the necessary features and if the license has not expired.
If this is not possible, this license file needs to be delivered in a different way to the end user. There are at least three ways it can be done:
1. You can download the license file through the dashboard.
2. You can call Key.Activate on your end, serialise the license object, and send it to your customer. You can also offer your own web interface where customers can obtain the license file.
3. Ask the customer to visit an [activation form](https://app.cryptolens.io/ActivationForms) that you have created to activate their license on their own.
The activation form can be created on [this page](https://app.cryptolens.io/ActivationForms). You only need select the product and activation format that should be used. You can read more about which activation file format to use in [this article](https://help.cryptolens.io/faq#protocols).
For more information, please check out the [offline license verification](/examples/offline-verification) tutorial.
# Customer portal
Source: https://help.cryptolens.io/feature/customer-portal/index
Introduction to the Customer portal offered by Cryptolens
## Introduction
Customer portal lets your customer manage their licenses, for example, de-activating old machines, as well as purchase new licenses (if enabled). You can choose which features you need and enable only those that you need.
## Getting started
To provide a customer access to the customer portal, you can either send them a generic link that automatically creates a [customer object](https://app.cryptolens.io/Customer) or you create the customer object manually and send them a personal link.
* **Provide a generic sign up link** (automatic) - anyone who has access to the link will be able to sign up for an account to access the customer dashboard. You can find this link [**here**](https://app.cryptolens.io/Customer/SignUpLink). We recommend this option if you want to have an **automated** way for your prospects to sign up for the customer portal.
* **Register customers yourself** (manual) - if want to control who can sign up for the customer portal, you can create the customers yourself. When you create a new customer on the [**customer page**](https://app.cryptolens.io/Customer), please select **enable customer association**. This will generate a sign up link similar to the one below, which you can send to a specific customer.
```text theme={null}
https://app.cryptolens.io/Portal/@cryptolens.io/Associate?id=123&auth=activationcode
```
### **Terms of use (optional)**
If you want customers to accept your terms of use, you can do so by adding a new data object (user level) on [**this page**](https://app.cryptolens.io/Data?refType=0) with the name `cryptolens_customtosurl` and StringValue set to the url of your terms of use.
### Accessing the licenses (for clients/end users)
There are several ways your clients can access the licenses in the customer portal inside your application. This can be handy, especially if they have many licenses.
1. Using a [master license / customer secret](/feature/web-ui/customer-secret).
2. [Authenticating using username and password](/licensing-models/user-credentials).
### Payment integration
If you would like to use built in support for Stripe in the customer portal, please review [this guide](/payments/recurring/index).
# License server
Source: https://help.cryptolens.io/feature/license-server/index
**Note**: a seprate subscription is required to use the license server: [https://cryptolens.io/products/license-server/](https://cryptolens.io/products/license-server/)
## **Idea**
**Problem**: Large companies tend to have strict policies that restrict certain machines to have direct internet access. This is a problem if we want license files on them to be up-to-date.
**Solution**: We allow one computer, the *license server*, to have internet access. All the machines in the network will contact the license server and it will in turn contact Cryptolens. Alternatively, the *license server* can store a copy of the license files and work offline.
## **Getting started**
Since v2.12-rc, there are two ways you can configure the server. Either, you can use the [pre-built version](https://github.com/Cryptolens/license-server/releases) of the server or you can use the configuration string from [this page](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False) and build the server yourself.
### **Using the pre-built license server**
You can use our [pre-built binaries](https://github.com/Cryptolens/license-server/releases) for all use cases (including the **local floating license server** feature).
When you use the binaries, you can either store the configuration in the config file (as we describe later), configure the server using environment variables (read more [here](https://github.com/Cryptolens/license-server#alternative-ways-to-configure-the-server)) or pass on the configuration as a command line variable (read more [here](https://github.com/Cryptolens/license-server#providing-the-configuration-string-as-command-line-argument)).
Please keep in mind that the license server will **only read the environment variables** or the **-config command line parameter** if you run it as a service (read more [here](https://github.com/Cryptolens/license-server#providing-the-configuration-string-as-command-line-argument)), unless you set `cryptolens_configurationstring` environment variable with the configuration string from [this page](https://app.cryptolens.io/extensions/LicenseServer).
To sum up, you can start the server in two ways:
1. If you choose to use environment variable, you would need to set `cryptolens_configurationstring` to the configuration string.
2. If you instead want to supply the configuration string as a command line argument, you can start the server using as follows:
```powershell theme={null}
LicenseSerer.exe -config
```
3. By using a `config.json` file (not recommended).
### **Building the server yourself**
If you need the local floating license server and cannot use environment variables or pass the configuration string as a command line argument as described [here](https://github.com/Cryptolens/license-server#alternative-ways-to-configure-the-server), the license server needs to be compiled on your end to create the binaries. All configuration is stored inside the `ConfigurationFromCryptolens` variable in `Program.cs`, which can be created on [this page](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False). In other words, there is no need to provide any arguments when calling the license server or use an external configuration file.
The license server can be compiled on most operating systems and the process is as follows:
#### **Install .NET**
To install .NET, visit [https://dotnet.microsoft.com/en-us/download/dotnet/6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) and download the SDK (i.e. not the runtime).
#### **Configuring the server**
There are three steps involved:
1. Visit [the configuration page](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False) to create a configuration that will make the server work in standard mode.
2. Copy the configuration string and paste it in the `ConfigurationFromCryptolens` variable in `Program.cs`.
3. Environment variables can also be used to change configuration data for a specific user. Please read more [here](https://github.com/Cryptolens/license-server#alternative-ways-to-configure-the-server).
Later in this tutorial, there are examples of calling the license server using command line arguments. We recommend to use the configuration string as described in (1) if possible. If you have any questions, please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io).
#### **Building the server**
To build the server, you can run the following command in the folder that contains the `LicenseServerCore.sln` file:
```
dotnet build LicenseServerCore.sln --configuration Release
```
From now on, you can use the instructions further down on this page to launch the executable.
## **Starting the server**
In order to launch the server, you need to run `LicenseServer.exe` as an administrator (you can download it [here](https://github.com/Cryptolens/license-server/releases)). The default port is 8080 but this can be changed. One way of doing it is to run CMD as an administrator and then type the command below:
```powershell theme={null}
C:\> LicenseServer.exe 5000
```
For newer versions of the license server, you can [this configuration](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False\&Port=5000) to set the port to 5000.
> Please make sure to check that this port is open so that other computers in the network can access it (shown below).
### **Running as a Windows service**
If you would like to run the license server as a service on Windows, you can accomplish that as described [here](https://github.com/Cryptolens/license-server#running-the-license-server-as-a-service).
### **Running on Linux and Mac**
To run the license server on either Linux or Mac, you need to make sure that .NET 5 runtime or later is installed (read more [here](https://dotnet.microsoft.com/download/dotnet/5.0)). Once it is installed, the license server can be started as follows:
```bash theme={null}
dotnet LicenseServer.dll
```
In the rest of the article, you just need to add `dotnet` before launching the server. Everything else is the same. Based on our tests, no sudo access is needed to run the license server on Linux.
## **Allowing ports (Windows only)**
In order to allow other computers in the network to access the license server, you need to open up the desired port in the firewall. If you use the default firewall, it can be opened as follows:
1. Run `wf.msc`
2. Right click on **Inbound Rules** and then click on **Add Rule**
3. Ryle type should be **Port**
4. **Specific local ports** should be checked, and the textbox at the right of it should be set to 5000 (or any other port of your choosing).
5. Click next through the entire wizzard and that should be it.
## **Connect to the server**
Depending on which of our client SDKs you use, there will be a different way to provide the URL of the license server. For example, if you use the [.NET SDK](https://github.com/Cryptolens/cryptolens-dotnet), every API method (such as Key.Activate) will have an extra parameter `LicenseServerUrl`, which can be used to provide the URL of the license server.
## **Additional features**
There are two versions of the license server, `v1.*` and `v2.*`. If you only need the request forwarding feature, `v.1.*` version will suffice. If you want to use the extra features listed below, you can use `v2.*` version instead.
### **Enable caching of licenses**
Since v2.0 there is a way to cache responses to `Key.Activate`, which helps to reduce the number of requests sent to Cryptolens. This is useful especially if your clients would have temporary internet connectivity issues. To enable license caching, you need to specify how long the license server should store each license. Note: if your application uses the `signatureExpirationInterval` parameter in `HasValidSignature`, the lifetime of the cache on the license server needs to be either equal to `signatureExpirationInterval` or less. Otherwise, your client application will throw an error.
As an example, to launch the server that caches licenses for 10 days, it can be started as follows:
```powershell theme={null}
C:\> LicenseServer.exe 8080 10
```
For newer versions of the license server, you can use [this configuration](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False\&CacheLength=10\&Port=8080) instead.
#### **Customers who are permanently offline**
The default behaviour of the server is to *always* attempt to get the latest copy of the license. However, if you know that the license server will not have access to the internet, it is better to enable the *offline mode* so that the license server always reads from cache.
To enable offline mode, you can launch the server as follows (or use [this configuration string](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=True\&LocalFloatingServer=False\&CacheLength=10\&Port=8080)):
```powershell theme={null}
C:\> LicenseServer.exe 8080 10 work-offline
```
In this case, it is a good idea to provide the license files (aka. activation files) that you want to load into the server. You only need to do it once or when the cache needs to be updated. If the license file (with a `.skm` extension) is in the *Downloads* folder, it can be loaded as follows (or use [this configuration string](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=True\&LocalFloatingServer=False\&CacheLength=10\&Port=8080\&ActivationFileFolder=C:%5CUsers%5CUser%20Name%5CDownloads)):
```powershell theme={null}
C:\> LicenseServer.exe 8080 10 work-offline "C:\Users\User Name\Downloads"
```
If you want to load a specific list of files and folders, they can be separated by a semi-colon ';'.
```powershell theme={null}
C:\> LicenseServer.exe 8080 10 work-offline "C:\Users\User Name\Downloads";"C:\temp\file.skm"
```
**Floating licenses offline**
If you want to use [floating licensing](https://help.cryptolens.io/licensing-models/floating) in offline mode (for example, to restrict the maximum number of containers a user can start), it can be done as follows:
1. Visit [https://app.cryptolens.io/extensions/licenseserver](https://app.cryptolens.io/extensions/licenseserver) and copy the "License server configuration" and "RSA Public Key".
2. When verifying the signature inside your application, please use the RSA Public Key on this page instead of the one you would normally use when your application can access our API. This key will only work with the license server that uses the configuration above.
3. In the license server project, paste the value of "license server configuration" to `ConfigurationFromCryptolens` variable in `Program.cs`. In newer versions, you can choose to place the configuration string in an environment variable (`cryptolens_configurationstring`) or by supplying it as a command line argument (**-config**).
4. Compile the license server in release mode.
5. In the release folder, create a new folder called "licensefiles".
6. Visit the [product page](https://app.cryptolens.io/Product) and click on the yellow button next to the license key that belongs to your client (to manage all activations). Now, click on "Download activation file" and put this file into the "licensefiles" folder created earlier.
7. You can now send the license server (in the release folder, including all the files and folders) to your client.
**Note (for .NET users)** For the time being, `Key.Activate` needs to be called the same way as the in the Unity example: [https://help.cryptolens.io/getting-started/unity](https://help.cryptolens.io/getting-started/unity)
```
// call to activate
var result = Key.Activate(token: auth, productId: 3349, key: "GEBNC-WZZJD-VJIHG-GCMVD", machineCode: "foo", floatingTimeInterval: 150, LicenseServerUrl: "http://192.168.0.2:8080");
// obtaining the license key (and verifying the signature automatically).
var license = LicenseKey.FromResponse("RSAPubKey", result);
```
**Note** If the local license server is enabled, floating license status will not be synchronized with Cryptolens, even if online mode is enabled. Moreover, GetKey request will return the information stored on the local license server and sign it using the local license server's private key. This means that if you have enabled floating licensing offline, you need to use public key that was shown on the [configuration page](https://app.cryptolens.io/extensions/LicenseServer) for both `Activate` and `GetKey` requests.
If you need to deactivate machine earlier, you can use the Deactivate method with `Floating=true`, similar to the way it would have been done when calling Cryptolens' Web API:
```
var result = Key.Deactivate("", new DeactivateModel { ProductId = 3349, Key = "key", MachineCode = "machine", LicenseServerUrl = "http://192.168.0.2:8080", Floating = true });
```
To obtain the list of all activated floating machines, you can use the GetKey call:
```
var result = Key.GetKey(token: "", productId: 3349, key: "", LicenseServerUrl: "http://192.168.0.2:8080");
// obtaining the license key (and verifying the signature automatically).
var license = LicenseKey.FromResponse("RSAPubKey", result);
```
**Usage-based licensing offline**
If the license server is set to work offline, it is still possible to collect information about usage (that is stored in data objects) and bill your clients for it. At the time of writing, only data objects associated with a license key can be used.
To get started, please follow the same steps as described in the [floating license offline](https://github.com/Cryptolens/license-server#floating-licenses-offline) section. When creating the configuration file, please make sure that *offline mode* is enabled.
When this is done, all usage information will be stored in the "usage" folder. The structure of the logs is described here: [https://eprint.iacr.org/2021/937](https://eprint.iacr.org/2021/937)
**Note:** The license file in the `licensefiles` folder needs to have the data objects that will be incremented or decremented. Otherwise, the license server will throw an error.
### **Loading settings from a config file**
**Note** In newer versions of the license server, we recommend to create a [configuration string](https://app.cryptolens.io/extensions/LicenseServer?OfflineMode=False\&LocalFloatingServer=False) as described in the beginning of this page.
To make it easier to deploy the license server on customer site, you can add all settings into `config.json` in the same folder as the server. The structure of the configuration file is shown below:
```
{
"Port" : 8080,
"CacheLength": 365,
"OfflineMode" : True,
"ActivationFiles" : ["C:\Users\User Name\Downloads"]
}
```
The `ActivationFiles` can either reference a specific file or a folder. If it references a folder, all files with the `.skm` extension will be loaded.
### **Running the license server as a service**
The license server can run as a Windows service in the background. This can be accomplished as follows (using [sc](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/sc-create)). Note, these commands need to be ran as an Administrator:
```powershell theme={null}
sc create license-server binpath="D:\path\to\licenseserver\LicenseServer.exe" start=auto
net start license-server
```
Note: the path to the license server needs to be absolute. It's important that you provide a configuration string that can be obtained at can be obtained on [https://app.cryptolens.io/extensions/licenseserver](https://app.cryptolens.io/extensions/licenseserver). When creating a new configuration, please set **Activation file folder** to an absolute path. For example, **C:\license-files**.
Once you have the configuration string, you have three ways that you can start the server:
#### **If you have compiled the server yourself**
1. Copy the configuration string that you obtained above into `ConfigurationFromCryptolens` variable and build the License Server yourself.
#### **If you are using the pre-built binaries**
1. Create an environment variable `cryptolens_configurationstring` with the configuration string from above.
2. Supply the configuration string (from above) as a command line argument when registering LicenseServer.exe as a service, as follows:
```
sc create license-server binpath="D:\path\to\licenseserver\LicenseServer.exe -config " start=auto
```
We have tested the license server version that targets .NET Framework 4.6.1.
Below are other useful commands:
```powershell theme={null}
sc stop license-server
sc queryex license-server
sc delete license-server
```
To obtain information about server status, e.g. the IP it is listening or if any errors have occurred, you can use the Windows Event log.
If you need any help, please let us know at [support@cryptolens.io](mailto:support@cryptolens.io).
### **Alternative ways to configure the server**
#### **Using environment variables**
It is also possible to configure the license server using environment variables. For now, the license server will only read the environment variables in two cases:
1. If the `ConfigurationFromCryptolens` in Program.cs is not null or empty.
2. If license server runs as a service (Windows).
For example, if you prefer to use the environment variables, you can set `ConfigurationFromCryptolens` to any string value and then rely on the environment variables. Alternatively, you can create a configuration string at [https://app.cryptolens.io/extensions/licenseserver](https://app.cryptolens.io/extensions/licenseserver) and then set "path to config file" to `USE_ENVIRONMENT_VARIABLES`.
Cryptolens uses the following environment variables:
| **Name** | **Description** |
| :-------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cryptolens_offlinemode` | Specifies if the license server should contact the central server (if set to false) or rely on the cached version if such exists (if set to true). When set to true, the license server will at first try the cache before attempting to contact the license server. |
| `cryptolens_port` | The port to use. |
| `cryptolens_activationfilefolder` | The path to the folder with activation files. Please set it to an absolute path when running the license server as a service. |
| `cryptolens_cachelength` | The amount of days until a new license file should be obtained. |
| `cryptolens_pathtoconfigfile` | The path to the configuration file. This can be useful if you anticipate that your clients might need to change certain properties more often, and then it may be easier to change the file rather than restarting the machine (which is often required for the environment variables to take effect). For now, you can set the port and the folder to the activation files. If you plan to run the server as a service, please set this to absolute path. |
| `cryptolens_cachefolder` | Path to the cache folder. If you plan to run the server as a service, please set this to absolute path. |
| `cryptolens_configurationstring` | An optional parameter that you can use to provide the configuration string that would normally need to be added in the code. The license server will always check for this environment variable at start and it will have priority over command line arguments. |
**Note**: when running the license server as a service, all paths to files and folders need to be **absolute**.
#### **Providing the configuration string as command line argument**
Since version v2.13, you can pass on the configuration string as a command line argument, meaning that you can start the server as follows:
```bash theme={null}
dotnet LicenseServer.exe -config
```
The configuration string can be obtained at [https://app.cryptolens.io/extensions/licenseserver](https://app.cryptolens.io/extensions/licenseserver).
# Messaging API
Source: https://help.cryptolens.io/feature/messaging/index
## Idea
[Messaging API](https://app.cryptolens.io/Message) allow you to broadcast messages to all instances of the application (i.e. all end users), for example to notify about new updates. You can send new messages on the [following page](https://app.cryptolens.io/Message).
## Use cases
* [Updates tracking](/feature/messaging/updates-tracking) - Explains how you can you the Messaging API to notify users about updates.
* [Notifications](/feature/messaging/notifications) - Explains how to create a news feed inside your application.
# Notifications
Source: https://help.cryptolens.io/feature/messaging/notifications
## Idea
Keeping an active relationship with your customers is good, especially when you sell subscriptions rather than a product. One way of doing it is by regularly updating your customers about new features, tips and other useful information.
It can also be useful to send specific messages to various customer groups, for example, you might have some customers running a beta version, in which case notifications can help to keep them updated. This is accomplished by grouping messages into **channels**.
## Implementation
Broadcasting messages to end users is implemented in a similar way as in the [Updates tracking](https://help.cryptolens.io/updates-tracking) tutorial, except that we need to record the date of the last message i.e. `result.Messages[0].Created` so that we can use it as a reference when searching for new notifications. If it is the first time they started it, we can use store `DateTimeOffset.Now.ToUnixTimeSeconds()` and use it to retrieve messages.
The channel is set to **news**, but this can be changed depending on the user preferences, etc.
```c# theme={null}
// this should the last messages that you have read.
// if it does not exist, use DateTimeOffset.Now.ToUnixTimeSeconds(), i.e. current unix time stamp.
// var lastSeenTime = result.Messages[0].Created
var result = Message.GetMessages("token with GetMessages permission", new GetMessagesModel { Channel = "news", Time = lastSeenTime } );
if (!Helpers.IsSuccessful(messages))
{
// we could not check for new message
Console.WriteLine("Sorry, we could not access new messages");
}
else if (result.Messages.Count > 0)
{
// return all new unseen messages
for(int i = 0; i < result.Messages.Count; i++)
{
Console.WriteLine(result.Messages[i].Content);
}
// save this date and use it when calling
//result.Messages[0].Created
}
else
{
// No new messages
Console.WriteLine("No new messages available");
}
Console.Read();
```
### Broadcast a message
New messages can be sent on [this page](https://app.cryptolens.io/Message). You can also use our [API endpoint](https://app.cryptolens.io/docs/api/v3/CreateMessage).
# Updates tracking
Source: https://help.cryptolens.io/feature/messaging/updates-tracking
## Idea
It is usually desired that users always have the latest version of the software. Messages can be used to notify only those users that run older versions of the application, providing details where updates can be obtained and what’s new.
You can also automate the process of updates by providing a link to the new version in the message, and perform the download and update automatically in the application. In this tutorial, we will focus on simply letting users know an update is available and where to get it.
It is also quite common to offers users the possibility to choose what type of updates they want. Some may prefer to only get the stable ones, whereas others want the newest features first and accept that there may be some bugs. We can solve this by grouping our updates notifications into **stable** and **experimental** channels.
## Implementation
### Code
Let’s suppose the user is listening on the stable branch and we want to show them the recent message if such exists. Then, we can use the code below. **Remember** that this method requires a separate [access token](https://help.cryptolens.io/getting-started/access-token) with ‘GetMessages’ permission, which can be created [here](https://app.cryptolens.io/User/AccessToken#/).
```c# C# theme={null}
var currentVersion = 1538610060;
var result = Message.GetMessages("access token with GetMessages permission", new GetMessagesModel { Channel = "stable", Time = currentVersion } );
if (!Helpers.IsSuccessful(messages))
{
// we could not check for updates
Console.WriteLine("Sorry, we could not check for updates.");
}
else if (result.Messages.Count > 0)
{
// there are some new messages, we pick the newest one
// (they are sorted in descending order)
Console.WriteLine(result.Messages[0].Content);
}
else
{
// No messages, so they have the latest version.
Console.WriteLine("You have the latest version.");
}
Console.Read();
```
```VB.NET theme={null}
Dim currentVersion = 1538610060
Dim result = Message.GetMessages("access token with GetMessages permission", New GetMessagesModel() With {
.Channel = "stable",
.Time = currentVersion
})
If Not Helpers.IsSuccessful(result) Then
' we could not check for updates
Console.WriteLine("Sorry, we could not check for updates.")
ElseIf result.Messages.Count > 0 Then
' there are some new messages, we pick the newest one
' (they are sorted in descending order)
Console.WriteLine(result.Messages(0).Content)
Else
' No messages, so they have the latest version.
Console.WriteLine("You have the latest version.")
End If
In Python
```
```python Python theme={null}
currentVersion = 1538610060
res = Message.get_messages("access token with GetMessages permission","stable", currentVersion)
if res[0] == None:
# we could not check for updates
print("Sorry, we could not check for updates.")
elif len(res[0]) > 0:
# there are some new messages, we pick the newest one
# (they are sorted in descending order)
print(res[0][0]["content"])
else:
#No messages, so they have the latest version.
print("You have the latest version.")
```
```java Java theme={null}
long currentVersion = 1538610060;
GetMessagesResult result = Message.GetMessages("access token with GetMessages permission", new GetMessagesModel("stable", currentVersion));
if(!Helpers.IsSuccessful(result)) {
// we could not check for updates
System.out.println("Sorry, we could not check for updates.");
} else if(result.Messages.size() > 0) {
// there are some new messages, we pick the newest one
System.out.println(result.Messages.get(0).Content);
} else{
// No messages, so they have the latest version.
System.out.println("You have the latest version.");
}
```
The variable `currentVersion` is the date when you published this release (in unix timestamp format). Each time you update the application, you need to remember to update this value.
The easiest way to obtain the unix timestamp is by visiting [this link](https://duckduckgo.com/?q=unix+timestamp) or, if you are on Linux or macOS, type the following in the terminal:
```
$ date +%s
```
In .NET, this can be done using `DateTimeOffset` as follows:
```
var currentTime = DateTimeOffset.Now.ToUnixTimeSeconds()
```
In Python, this can be done with `datetime`:
```
import datetime
datetime.datetime.utcnow().timestamp()
```
### Broadcast a message
New messages can be sent on [this page](https://app.cryptolens.io/Message). You can also use our [API endpoint](https://app.cryptolens.io/docs/api/v3/CreateMessage).
# Introduction
Source: https://help.cryptolens.io/feature/reseller-portal/index
Introduction to reseller management, reseller portal and other relevant tools.
**Note:** The reseller portal feature requires a separate subscription. Please contact [support@cryptolens.io](mailto:support@cryptolens.io) for more information.
## Idea
The goal of the reseller portal is to allow **software vendors** to delegate **license key creation** (aka license key issuance) to other users, which are referred to as **resellers**.
## Getting started
To get tailored documentation, please choose an option below:
### Vendor
If you are using Cryptolens to secure your software and would like to allow resellers to help you with the distribution, please select the option below:
[I’m a vendor](/feature/reseller-portal/vendor)
### Reseller
If you have signed up for a reseller account from a link sent by the software vendor, please select the option below:
[I’m a reseller](/feature/reseller-portal/reseller)
# Reseller portal for resellers
Source: https://help.cryptolens.io/feature/reseller-portal/reseller
## Concepts
### License issuance right
A license issuance right gives you the right to issue a specific amount of licenses on behalf of the vendor (who issued the right to you).
## Common actions
### Creating a customer
You can create a customer on [this page](https://app.cryptolens.io/Customer/Create). It is important to select a vendor which this customer should be associated with. If you have an issuance right from that vendor, you will be able to issue licenses to that customer directly.
* **Enable Customer Association** - Whether it should be possible to customers to sign up for a customer portal account. This can be enabled later.
* **Is Public** - If this is set to true, other resellers that are associated with the vendor will see this customer. They will only be able to issue new licenses to the customer. The remaining fields will be read only.
### Issuing a license
There are two ways to issue a license. Either, the license will not be linked to any customer (i.e. just a license key will be created) or it can be linked to a customer.
#### Customer-linked
To issue a license key that will automatically be associated with a customer, you can select a customer on the [customer page](https://app.cryptolens.io/Customer) and then choose which license issuance right you want to use. If a valid license issuance is selected, you can click on ‘Issue’ and a new license key will be issued and associated with the specified customer.
#### Not-linked to a customer
To simply create a license key without associating it with a license key, you can use the link on ‘Issue New License’ from the [main page](https://app.cryptolens.io/ResellerPortal) of the reseller portal:
# Reseller portal for vendors
Source: https://help.cryptolens.io/feature/reseller-portal/vendor
## Getting started
### Prior setup
#### Company profile
It is important to set up company profile with a company page and domain on [this page](https://app.cryptolens.io/User/Profile).
#### License templates
When issuing a **license issuance right**, Cryptolens needs to know the settings to use for the new licenses, eg. features, when it will expire, etc. These can be defined in a [license template](https://help.cryptolens.io/web-interface/license-template).
The easiest way to create a license template is by visiting the key creation page and click on **Save as template**.
### Considerations
When you start using the reseller features, please keep in mind that once a **license issuance right** is issued to a reseller, it will not be possible to:
* remove the license issuance right (although it can be blocked, which will render it unusable).
* remove the reseller from the list of registered resellers.
* remove the **license template** associated with the license issuance right.
### Adding new resellers
In order to grant a user a license issuance right, you need to send them an invite. Once they have accepted it, they will show up in the dashboard and you will be able to manage their license issuance rights.
### Granting issuance right
An issuance right gives the reseller the ability to create new licenses keys on your behalf subject to your conditions. You can specify how long the issuance right will be valid and how many licenses the reseller can issue.
* **Friendly Name** - This is how the reseller will be able to distinguish between issuance rights. A reseller is not able to see the product id or the properties of the license key.
* **License Template** - This contains information about how the license key should be created and which product it should belong to. You can read more about license templates [here](https://help.cryptolens.io/web-interface/license-template).
* **Amount** - This specifies how many licenses they will be able to issue. You can always grant a reseller a new issuance right if they use up an existing one.
* **Expires In** - This specifies how long the issuance right will be valid. If you do not want to impose a time constraint, please set this to a large number.
## Remarks
Below we have summarized some common errors that you or your resellers might encounter.
This error is caused if the license template sets the ResellerId value to a non-zero value. To fix this, please update the license template that is used in an issuance right so that ResellerId is set to zero.
For example, the following configuration is correct:
```
{"ProductId":15189,"Period":30,"F1":false,"F2":false,"F3":false,"F4":false,"F5":false,"F6":false,"F7":false,"F8":false,"Notes":null,"Block":false,"CustomerId":0,"TrialActivation":false,"MaxNoOfMachines":0,"AllowedMachines":null,"NoOfKeys":0,"NewCustomer":false,"AddOrUseExistingCustomer":false,"ResellerId":0,"Name":null,"Email":null,"CompanyName":null,"EnableCustomerAssociation":false,"AllowActivationManagement":false,"AllowMultipleUserAssociation":false}
```
whereas the one below is not, since ResellerId is set to 19579.
```
{"ProductId":15189,"Period":30,"F1":true,"F2":true,"F3":true,"F4":true,"F5":true,"F6":true,"F7":true,"F8":true,"Notes":null,"Block":false,"CustomerId":0,"TrialActivation":false,"MaxNoOfMachines":100,"AllowedMachines":null,"NoOfKeys":1,"NewCustomer":false,"AddOrUseExistingCustomer":false,"ResellerId":19579,"Name":null,"Email":null,"CompanyName":null,"EnableCustomerAssociation":false,"AllowActivationManagement":false,"AllowMultipleUserAssociation":false}
```
You can read more about license templates [here](https://help.cryptolens.io/web-interface/license-template).
# Anomaly detection
Source: https://help.cryptolens.io/feature/web-ui/anomaly-detection
Explains how the anomaly detection module can be used to detect abnormal usage patterns and fraudulent behaviour.
**Note:** This article and some of the features it refers to are still in beta phase. If you find any discrepancies or need help, please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io).
The anomaly detection module allows you to detect abnormal usage patterns of your software. For example, a typical anomaly is when your customers experience problems (e.g., with license verification). You can read more about it in the [announcement on our blog](https://cryptolens.io/2021/01/ai-anomaly-detection-in-software-licensing-logs/).
## Getting started
### Enabling anomaly detection
The anomaly detection module can be enabled on [https://anomaly.cryptolens.io/index.html](https://anomaly.cryptolens.io/index.html). When this is done, it might take some time to train the model and find the anomalies.
### Analysing anomalies with Log Explorer
A summary of all anomalies can be found on the [following page](https://app.cryptolens.io/extensions/SuspectedAnomalies). When a log entry is classified as anomalous, it means that the group of requests that the log entry is part of was classified as anomalous. Not all requests within a group are anomalous.
For log entries that are anomalous, three additional properties will be shown:
* **Group Id (Anomaly)** - A unique identifier of the group of anomalous requests that the log entry is part of.
* **Starting At (Anomaly)** - The ID of the first log entry in the group.
* **Ending At (Anomaly)** - The ID of that log entry in the group.
The *Starting At* and *Ending Before* can be used when calling [Get Web API Log](https://app.cryptolens.io/docs/api/v3/GetWebAPILog) method. For example, to retrieve all the requests in the group, you can set the Web API parameter *StartingBefore* to *StartingAt-1* together with *Limit* set to 25 (which is the size of the group).
Another way is to set *EndingBefore* to *EndingAt+1* with the *Limit* set to 25. In this case, you need to set *OrderBy* to *Time descending*.
### Actions to take for suspected anomalies
Typically, groups of requests where there are many unsuccessful requests for a certain license key will be classified as suspected anomalies. This is usually caused when your customers have issues verify their license. If a license key was recorded, you can use it to find which customer it was. In other cases, you can use information such as the *Machine Code* and the *Friendly Name* as a way to identify the customer.
**Note:** Some requests within a group of suspected anomalies can still be ok. The anomaly detection module looks at requests as a group and will classify a group of requests as anomalous if the usage pattern deviates from what it has seen previously.
### How anomaly detection works
The anomaly detection module works by learning the historical distribution of your Web API logs. It analyses the logs as a group by examining the following parameters:
* Time
* Successful (inferred from *State*)
* IP (anonymised)
* Key
* Product
* State
* MachineCode
* Country
*Note: IP, Key, Product and MachineCode are anonymised when the model is trained*.
# Automatic expiration notice
Source: https://help.cryptolens.io/feature/web-ui/automatic-expiration-notice
Describes the way you can send automatic email expiration notices to your customers.
We’ve recently released a Zapier Action that allows you to customize the behaviour of the license expiry notification. Please refer to [this article](https://help.cryptolens.io/examples/zapier-license-expiry-notice).
## Idea
Automatic expiration notice allows you to send out email notifications to your customers when a license is about to expire. You can either set this up so that an expiration notice is sent a couple of days in advance and/or when the license has been blocked. You can configure when a license key should be treated as time-limited by clicking on *Edit Feature Names* on the product page, as described [here](https://help.cryptolens.io/web-interface/feature-definitions).
In order to be able to send these notifications, a license needs to be associated with a customer. If the customer has signed up for a Cryptolens account, we will use their accounts email address. Otherwise, the email address in the customer object will be used. It’s important that you have the updated customer email in case we are not able to send an email to their account.
## Implementation
To enable automatic expiration notifications, you can click on *Edit Feature Names* on the product page and then select `Automatic expiration notification`. The default behaviour is to send an email notification 7 and 3 in advance and another email when the license key gets blocked (assuming automatic license key blocking is enabled).
If you want to edit how many days in advance an email is sent and whether an email should be sent when the license key gets blocked, you can do so by modifying a data object associated with the product. It’s called `cryptolens_expirationnotice`. By default, its value is `0,3,7`. The string value `0,3,7` means that an expiration notice should be sent 7 and 3 days in advance and the 0 means that when the license gets blocked, a notification will be sent too. For example, if you only want to send expiration notice in advance, you can change this string to `3,7` and if you only want to send confirmations when a license gets blocked, you can can set it to `0`.
It is possible to send a copy of each expiration notice to different email address. This can be accomplished by by adding a data object on the product level with the name `cryptolens_expirationnotice_send_copy` and **StringValue** set to the email address where the copy should be sent.
# Customer secret
Source: https://help.cryptolens.io/feature/web-ui/customer-secret
## Idea
A *Customer Secret* can be thought of as a “password” that you can send to your clients so that they can access their licenses without first creating a Cryptolens account.
At the time of writing, it is possible to use [Get Customer Licenses](https://app.cryptolens.io/docs/api/v3/GetCustomerLicenses) and provide the *Customer Secret* to obtain all the licenses that are associated with that customer. For example, this can be useful if your clients have licenses to multiple products, so that they do not need to remember all of their license keys, just the *Customer Secret*. You can then extract the right license within your application.
## Configuration
### Changing the format
By default, the customer secret will be in the form of a GUID, for example: `6dffda95-05cb-4754-bb49-c4a0be6ea647`.
If you prefer to have shorter customer secrets that resemble a license key, i.e., `WPIGP-UPHHT-INUYJ-MSDTJ`, you can change that as follows:
1. Visit the global data object page [here](https://app.cryptolens.io/Data?refType=0).
2. Create a new data object with the **Name** `cryptolens_customer_secret` and **IntValue** set to `2`.
From now on, all customer secrets will resemble the license key format.
### Rare exception that may be thrown
Please note that we do not check if other customers have the same customer secret, since the likelihood of a collision is practically zero. However, if it this would occur, you would receive `A customer with the same secret exists.` when calling [Get Customer Licenses By Secret](https://app.cryptolens.io/docs/api/v3/GetCustomerLicenses), we would suggest to do the following:
1. Find the second customer with the same customer secret by searching for on the [customer page](https://app.cryptolens.io/Customer).
2. Update customer secret for both of the customers.
3. Inform both clients that it has changed.
# Delete keys
Source: https://help.cryptolens.io/feature/web-ui/delete-keys
At this point, it’s not possible to remove a key permanently for security reasons. However, there are two ways to achieve a similar effect:
* Selecting **OrderBy** to `Block (ascending)` (on the product page), which will list all your non-blocked keys first.
* Navigate to the [Security page](https://app.cryptolens.io/User/Security) and select **Hide Blocked Keys** (remember to **save** changes). In order to ‘remove the key’, you simply **Block** the key on the product page (i.e. by clicking on No in the Block column).
* Delete the entire product. It’s a good idea to have **two products**, one used in **production** and one used in **testing**.
# Export Keys To Spreadsheet
Source: https://help.cryptolens.io/feature/web-ui/export-keys-to-spreadsheet
Note, **this article contains outdated examples**. It will be updated soon.
It is possible to export keys to TSV so that they can be opened in applications such as Microsoft Excel. On the product page, there are two options:
* Export current keys to TSV – exports only keys that are visible on the page (100 keys)
* Export all keys to TSV – exports all keys in the product
## In Excel
If you are using Microsoft Excel 2013, you can simply drag-and-drop the downloaded file into Excel. Otherwise, you should press on “Open Other Workbooks”.
If you get any window after that you have selected the file, simply press next until the wizard is complete.
# Feature Definitions
Source: https://help.cryptolens.io/feature/web-ui/feature-definitions
## Idea
Feature definitions is a way to let Cryptolens know which of your features are used to denote trials and time-limited keys. Cryptolens is in most cases feature-agnostic, eg. it does not differentiate between expired licenses. This is up to the client to decide, based on the feature values.
However, in the dashboard, feature definitions can help Cryptolens to correctly render the status of each license. For example, if the license is not time-limited, you will not be able to extend it in the dashboard.
In order to change the type of each feature, you can visit the product page and click on `Edit Feature Names`. There, you can either select which feature should have the **trial** or **time-limited** property, and also mark the entire product as having time-limited licenses. The latter will ensure that no matter the feature values, the box that allows you to extend the time of a license will always be shown.
If you want to be able to extend the expiration time of all licenses no matter the feature values, please check **Treat all licenses as time-limited**.
## Notes
Recently, [Activate](https://app.cryptolens.io/docs/api/v3/Activate) and [Get Key](https://app.cryptolens.io/docs/api/v3/GetKey) have received a new field called `LicenseStatus`, whose aim is to move more of the license key logic from the client to Cryptolens. To make this possible, Cryptolens needs to know what different features should mean. You can set them up by clicking on `Edit Feature Definitions` on the product page.
## FAQ
In the following FAQ, we have compiled the questions we have received related to defining trial keys, extending licenses, etc.
### Why is it not possible to change the expiration date?
When you try edit license properties on the product, you might not always get the option to change the expiration date as shown below: Instead, you might see something like this:
The reason you might not always see the license key extension box is because the license key is not marked as a time-limited license. Although you will still be able to access the expiration date through the API (as well extend the license duration), we made a design choice earlier to hide it for licenses that are not time-limited in the dashboard.
There are two ways this can be solved:
1. When you are on the product page, you can click on **Edit Feature Names** and then check **Treat all licenses as time-limited**. This will mark all licenses as time-limited and the license extension box will show up when editing all licenses.
2. Enable the feature that is defined as a trial feature for that license. On the **Edit Feature Names** page, you can select which feature is used to denote a time-limited license. By default it’s F1. In other words, if you set `F1=true` for a specific license, it will be marked as time-limited and you will be able to change the expiration date.
**Note:** a license that is marked as time-limited (either when the time-limited feature is set to true or when **Treat all licenses as time-limited** is enabled) will **not** automatically expire. To achieve this, you need to configure that on the **Edit Feature Names** page. In other words, using one of the two options we have suggested will not block the license key in any way or make it invalid, unless you have changed the defaults.
If you have any questions, feel free to reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io).
# Feature Templates
Source: https://help.cryptolens.io/feature/web-ui/feature-templates
Describes the idea behind feature templates so that you can support more than 8 features.
> Originally published on [our blog](https://cryptolens.io/2019/05/support-for-any-number-of-features/).
## Idea
A common request we have received from our customers is to support more than 8 features. Until now, the recommended approach has been to use the notes field or data object fields to store any additional feature information. With this update, the dashboard and several of our clients have built in support for additional features.
In addition to being able to define any number of features, we have also made it possible to define feature hierarchies. For example, we can define the following feature hierarchy:
Now, suppose the user opens ModuleB. With the above setup, we can either check if they have permission to use ModuleB or we can be more specific and require Submodule 1 to be present.
We will go through in more detail how you can get started later in the article. The feature template used for our above example is shown below:
```
["ModuleA", ["ModuleB", ["Submodule 1", "Submodule 2"]], "ModuleC", ["ModuleD", [["ModuleD1", ["Submodule D1", "Submodule D2"]]]]]
```
## Set up
### Defining features
#### Simple hierarchy
Let’s suppose we want to define the following feature hierarchy:
To define it, we can use a JSON array structure shown below:
```
["ModuleA", "ModuleB", "ModuleC"]
```
#### Adding submodules
Suppose now that we want to add sub features to ModuleB. For example, Submodule 1 and Submodule 2. To do that, we introduce a new array instead of the string “Module B”, which has the following structure:
```
["ModuleA", ["Module B", ["Submodule 1", "Submodule 2"]], "ModuleC"]
```
The first element defines name of the module, and the second element should ways be a list of submodules.
#### Another example with sub modules
We can keep adding submodules to submodules in a similar fashion. For example, to express the following features:
the template below can be used:
```
["Module C", ["ModuleD", [["ModuleD1", ["Submodule D2", "Submodule D4"]]]]]
```
#### How to add a template to a product
To add your feature template to a product, you can click on Edit Feature Names on the product page and then scroll down until you see Feature Template.
In the example below, we would get the following feature hierarchy
It’s defined with the following feature template
```
["ModuleA", ["ModuleB", ["Submodule 1", "Submodule 2"]], "ModuleC"]
```
Please check out *Examples* section at the end of this page for more templates.
### Assigning features
Once you have defined the feature template, the page to create a new license key and to edit existing one will have a box that allows you to define them, as shown below. The state will be stored in a data object with the name cryptolens\_features, which we will cover in the next step.
### Verifying features
You can verify that a certain feature exists using `Helpers.HasFeature`. For example, to check if Submodule1 is present, the following code can be used:
```c# theme={null}
Helpers.HasFeature(license, "ModuleB.Submodule 1")
```
If you only want to check if ModuleB is present, without being specific, you can instead use:
```c# theme={null}
Helpers.HasFeature(license, "ModuleB")
```
### Examples
The modules below:
can be expressed as:
```
["ModuleA", "ModuleC", ["ModuleD", [["ModuleD1", ["ModuleD1.1", "ModuleD1.2"]], ["ModuleD2", ["ModuleD2.1", "ModuleD2.2"] ], "ModuleD3", "ModuleD4" ]], "ModuleE"]
```
# Keys that don’t expire
Source: https://help.cryptolens.io/feature/web-ui/keys-that-dont-expire
When you are about to create a new license key, you are prompted to set the **Period** (i.e. the number of days the key should be “valid”), which affects the **expiration date**.
However, while Cryptolens requires a period (defaulted to 30) during the creation of a new license, this does not mark the license as “time-limited”. Instead, what determines if your license has expired is the code on the client side (i.e. an explicit check for the expiry) or a feature in the dashboard (which has to be enabled).
### How to make a license time-limited
In other words, if you would like the license to be time-limited, you can either:
* **In the code** add additional code that checks the difference between the expiration date and the current time. For example, if you use F1 (Feature 1) to mark a license as time-limited, you can first check that it is set to true and then check if the license has expired.
* **In the dashboard** enable an automatic script that will block expired licenses. You can learn more about it [here](https://help.cryptolens.io/faq/index#blocking-expired-licenses).
We recommend to enable automatic blocking of expired licenses in the dashboard for most use cases. If you are planning to use Cryptolens offline, we recommend to implement a time check in the code as well.
### How to issue time unlimited licenses
Since the default behaviour is that the license will keep being valid after expiration, there is no need to take any extra steps at this point.
# KPI Dashboards (beta)
Source: https://help.cryptolens.io/feature/web-ui/kpi-dashboards
Describes how to configure and use the new analytics dashboards
**Note:** This article and some of the features it refers to are still in beta phase. If you find any discrepancies or need help, please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io).
Since the end of 2025, three new dashboards were released with the aim of replacing an older version of Analytics ([https://analytics.cryptolens.io/](https://analytics.cryptolens.io/)).
In this article, we will describe how they can be used and configured.
## Getting started
The three dashboards that we refer to are available on the following links:
* [https://app.cryptolens.io/report/overview](https://app.cryptolens.io/report/overview)
* [https://app.cryptolens.io/report/dailyreport](https://app.cryptolens.io/report/dailyreport)
* [https://app.cryptolens.io/report/licenseactivity](https://app.cryptolens.io/report/licenseactivity)
### Excluding licenses that are used for testing purposes
You can exclude a license or a set of licenses from being processed as follows:
1. Visit [https://app.cryptolens.io/Data?refType=0](https://app.cryptolens.io/Data?refType=0)
2. Add a data object that has the following name: **cryptolens\_excludekeys\_kpi**
3. In the StringValue field, you can enter the list of key ids separated by a comma.
The "key ids" we referred to in (3) can be obtained by first finding the license key on the product page and then clicking on the green icon next to the license key ("see stats for the key). This will redirect you to a new page with the following url: [https://app.cryptolens.io/report/LicenseActivity?keyId=](https://app.cryptolens.io/report/LicenseActivity?keyId=)....
The key id is the value in the url after the equals sign. A better approach to extract it will be released later this year.
## Road map
We are actively working on improving them and incorporate user feedback we receive. Please email us at [help@devolens.com](mailto:help@devolens.com) if you have any feedback. Below is a list of a few items that are currently being worked upon. Thank you for your feedback!
* ~~Exclude test licenses~~ (implemented)
* See how many seats/machines are used on average for a given license (you may want to try this script in meantime: [https://github.com/Cryptolens/reporting-tools/tree/master/FloatingLicenseAudit](https://github.com/Cryptolens/reporting-tools/tree/master/FloatingLicenseAudit))
* Show units on hover.
* Including other minor fixes.
# License Templates
Source: https://help.cryptolens.io/feature/web-ui/license-templates
## Idea
When you create many licenses of the same type (eg. expiration date, set of features), it can be handy to be able to save these configurations and re-use them next time you create a new license.
## Usage
### Creating a new license template
The easiest way to create a license template is by clicking on **Save as Template** when creating a new license key. This will redirect you to a new page where you can specify the name of the template. The **parameters** field is the internal representation of the template, which we will cover a bit later.
### Using an existing license template
When you are on the page to create a new license, you can select the license template from the dropdown list and click on the create button next to it. This will ensure that the configuration from the license template is used.
## Advanced
### Format of the ‘parameters’ field
The **parameters** field is a JSON representation of what type of license to generate. There are two formats that are used. The first one (which is used in most cases) is a JSON dictionary of the paremters accepted by [Create Key](https://app.cryptolens.io/docs/api/v3/CreateKey) method, for example:
```
{"ProductId":3645,"Period":30,"F1":true,"F2":true,"F3":false,"F4":false,"F5":false,"F6":false,"F7":false,"F8":false,"Notes":null,"Block":false,"CustomerId":0,"TrialActivation":false,"MaxNoOfMachines":0,"AllowedMachines":null}
```
The second format will only be used if you create a license template with the custom defined features (more info [here](https://cryptolens.io/2019/05/support-for-any-number-of-features/)). The format allows you to specify what methods are to be called and in what order. It is also possible to re-use the result from the previous method in the next method, for example, `[key]` in [AddDataObjectToKey](https://app.cryptolens.io/docs/api/v3/AddDataObject) refers to the key returned by [Create Key](https://app.cryptolens.io/docs/api/v3/CreateKey). An example is shown below:
```
[{"APIMethodName":"CreateKey","Parameters":{"ProductId":3645,"Period":30,"F1":false,"F2":false,"F3":false,"F4":false,"F5":false,"F6":false,"F7":false,"F8":false,"Notes":null,"Block":false,"CustomerId":0,"TrialActivation":false,"MaxNoOfMachines":0,"AllowedMachines":null}},{"APIMethodName":"AddDataObjectToKey","Parameters":{"Name":"cryptolens_features","StringValue":"[\"ModuleA\",[\"ModuleB\",[\"Submodule 1\",\"Submodule 2\"]]]","IntValue":0,"CheckForDuplicates":false,"ProductId":3645,"Key":"[key]"}}]
```
Note, for the time being, only [Create Key](https://app.cryptolens.io/docs/api/v3/CreateKey) and [AddDataObjectToKey](https://app.cryptolens.io/docs/api/v3/AddDataObject) are supported.
# Searching for Licenses using Linq Queries
Source: https://help.cryptolens.io/feature/web-ui/linq-search-product
Explains the way you can use linq queries on the product page to search for keys that satisfy certain properties.
### Sorting by ID
If you happen to know the ID of a license key, it can easily be found. You can use relation operators too. These are just some of the examples:
* `id=2` - One key where id is equal to 2.\
* `id=2` or `id=3` - Two keys, one with id set to 2 and another with id set to 3.
* `id < 10` - Keys where id is less than 10. Here, we will get 9 keys. You can also change to `id <= 10` to get 10 keys (using less than or equal to operator).
### Sorting by Key
Let’s say that you want to look at a license (or several licenses) with a certain key string (in Key column). Below, some of the examples:
* `key="ITVBC-GXXNU-GSMTK-NIJBT"` - One license key (if exists).
* `key.contains("ITVBC")` - All keys that contain “ITVBC”.
**Update:** you can search for a specific license without the “key=” prefix. In other words, `ITVBC-GXXNU-GSMTK-NIJBT` and `key="ITVBC-GXXNU-GSMTK-NIJBT`” are equivalent.
### Sorting “Created” and “Expires”
Say you want to look at licenses that were created at a certain point in time or that will expire at a given date. Or, maybe you are interested in a certain interval, for instance keys created a yesterday or a month ago. Here are some examples:
* `created = today` - Keys created today only.
* `created >= yesterday` - Keys created today and yesterday. We could also type `created = today or created = yesterday`.
* `created >= DateTime(2015,01,01)` - Keys that were created in the beginning of 2015.
* `expires <= DateTime(2016,01,01)` - Keys that will expire no later than the beginning of 2016.
#### Variables
In addition, you can use variables such as `tomorrow`, `monthback`, `monthforward`>.
### Sorting by “Period”
If you choose to have a time limited license, such as those that are used in a subscription, the period becomes important. You can sort keys based on the period as follows:
* `period = 30` - Keys that have a period equal to 30.
### Sorting features “F1,…, F8”
Features can be sorted also. Note, although features are represented as 1’s and 0’s, these are actually referring to a Boolean type, i.e. True or False.
* `F1 = true` - Keys that have feature1 set to true (or 1 on the product page).
### Searching The Notes Field
Notes field can be sorted in a similar way as Key (see above). Here are some of the examples.
* `notes="Bob"` - Keys where Notes is equal to “Bob”
* `notes.contains("to Bob")` - Keys where Notes contains “to Bob”
### Sorting by Block
Block can be sorted similar to Features. “Yes” and “No” refer to the Boolean values “True” and “False”, respectively.
* `block=true` - Keys that are blocked (block=yes/true).
### Sorting based on Customer
A customer object has four fields that can be used when sorting licenses.
* **Id** - a number, similar to ID field sorting.
* **Name** - a string, similar to notes field sorting.
* **Email** - a string, similar to notes field sorting.
* **CompanyName** - a string, similar to notes field sorting.
* **Created** - a date, similar to Created field sorting.
* **EnableCustomerAssociation** - Whether it should be possible to customers to sign up for an account (a boolean).
* **IsPublic** - Whether resellers should be able to see this customer. Note, if a reseller is registered with this customer, they will still be able to see it even if this is set to false (a boolean).
* **ResellerId** - The UserId of the account that acts as a reseller (an integer).
* **MaxNoOfDevices** - The maximum number of devices that the customer can log in on if user account authentication is used (an integer).
* **Owner** - the user id of the owner of this object. This will be your user id (an integer).
Here are some sample queries:
* `customer.name="Bob"` - Keys where the Customer’s name is “Bob”
* `customer.id=3` - Keys where where Customer’s id is 3.
* `customer.created= today` - Keys where the Customer’s creation date is set to today.
### Sorting based on Reseller (only in dashboard)
You can use the reseller id of a reseller to find licenses that they have issued. For example:
* `resellerid=1234` - show only licenses created by reseller with id 1234.
* `resellerid=-1` - show licenses that were not created by any reseller.
### Sorting based on Activated Devices
The Activated Devices (aka Activated Machines) is stored as a list of elements that contain three fields:
* **Mid** - (machine code of the device)
* **IP** - (the IP address of the device during activation)
* **Time** - (the date and time of the activation)
There are several useful parameters that can be retrieved using a query:
#### Find license keys that have activated devices
* `ActivatedMachines.Count() > 0 `- Keys that have at least one activated device.
* `ActivatedMachines.Count() > 0 and ActivatedMachines.Count() < 10` - Keys that have at least one and at most 9 activated devices.
#### Find license keys that have a certain machine code
* `ActivatedMachines.Count(it.Mid="machine code") > 0` - Keys with at least one device that has the machine code “machine code”.
* `ActivatedMachines.Count(it.Time >= DateTime(2015,01,01)) > 0` - Keys that were activated after the 1st of January, 2015.
### Sorting based on Data Objects (additional variables)
Every license key can have a set of data objects (aka additional variables) associated with them. They have the following four fields:
* **Id** - (the unique identifier of the data object, eg. 35.)
* **Name** - (an optional name of the data object)
* **StringValue** - (the string value of the data object)
* **IntValue** - (the int value of the data object)
#### Find licenses keys that have at least one data object
* `dataobjects.count() > 0` - Keys with at least one data object
#### Find license keys that have a specific value attached to them
* `dataobjects.count(it.StringValue="test") > 0` - Keys where at least one data object has the string value of “test”.
* `dataobjects.count(it.name="usagecount") > 0` - Keys that have a usage counter (see [Set Usage ‘Quota’ for a Feature](https://help.cryptolens.io/web-api/dotnet/v401#custom-variables-aka-data-objects))
### Sorting with Advanced Parameters
Advanced parameters are those that are not displayed directly on the product page, but can be found when selecting individual keys. These are:
* **AutomaticActivation** - (a Boolean i.e. either *true* or *false*, depending on if it should be possible to perform an activation)
* **AllowedMachines** - (a string, separated by new lines, that contains a white list of devices that can be activated, no matter if the maximum number of machines limit has been achieved)
* **TrialActivation** - (a Boolean that sets the [Trial Activation](/feature/web-ui/trial-activation))
* **MaxNoOfMachines** - (an integer that specifies the number of devices that can use the same license key simultaneously)
#### Keys with Trial Activation property
* `trialactivation = true` - Keys with [Trial Activation](/feature/web-ui/trial-activation) enabled.
#### Keys with a certain white listed machine code
* `allowedmachines.contains("machine code")` - Keys that have an allowed machine code “machine code”.
### Notes
* Variable names, eg. *AllowedMachines*, are *case-insensitive*, that is, you can express it as "allowedmachines" or "AllowedMachines".
# Maximum number of machines
Source: https://help.cryptolens.io/feature/web-ui/maximum-number-of-machines
Please check out the [FAQ article ](https://help.cryptolens.io/faq#maximum-number-of-machines)as well.
The maximum number of machines allows you to lock a license key to specific number of devices (aka hardware lock / node-lock). This value is an upper bound. If it is set to zero, this feature will be deactivated, which means that unlimited number of activations can be performed. This will only affect [key verification](/examples/key-verification), i.e. you will still be able to retrieve key information using [GetKey method](https://app.cryptolens.io/docs/api/v3/GetKey).
* 0 – hardware lock disabled, i.e. unlimited number of activation.
* 1,2, … – sets the upper bound for the number of devices that can activate the license key.
When maximum number of machines is set to **0**, machine codes will not be added to the list of activated machines. This means that if your code calls `Helpers.IsOnRightMachine()`, that call will fail. Please review [this article](https://help.cryptolens.io/faq#maximum-number-of-machinesachines) for all the cases.
## Ways of restricting access
There are two ways you can restrict the number of machines that can use a license:
1. [Node-locked](/licensing-models/node-locked): Either you restrict a license to a certain number of machines indefinitely (i.e. until they are deactivated)
2. [Floating](/licensing-models/floating): or you allow a license to “float” between different machines, as long as at any one point this upper bound of machines is never exceeded
For trial keys, it’s better to use the first approach whereas for paid licenses, either approach will work fine, although floating licenses will be more convenient for your customers and remove the hassle of license deactivation.
## Example
Suppose you want to allow a team of 5 people to use a license key that you’ve created. In that case, you simply set `Machine Number of Machines` to 5.
# Add additional admins
Source: https://help.cryptolens.io/feature/web-ui/multiple-admins
To invite your team members to your account, you can use the [Authorized admins](https://app.cryptolens.io/User/AuthUsers) feature. This will give every team member that you invite admin access to your account. If you want to customize the access and have an audit log of every operation, we recommend to check out [restricted accounts](https://help.cryptolens.io/web-interface/restricted-accounts).
**Note:** You need a premium subscription to use this feature.
## Getting started
To invite a team member, please visit [Authorized admins](https://app.cryptolens.io/User/AuthUsers) page and enter the email address where you would like the invite to be sent.
Note: before your team member can accept the invite, they need to have a Cryptolens account. Their account does not have to have an active subscription.
Once they have an account, they can follow the link and they will automatically be added to your account.
To access your account, they can visit the [main page](https://app.cryptolens.io/) and click on your name in the top right corner. They will now have access to your account.
## FAQ
Below is a summary of frequently asked questions. If you would have any other questions, please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io)!
The account expiration emails can be safely disregarded if they have been sent to your team members. Only the production account (where you have all products and licenses) should have a subscription. Your team member only needs a subscription if they plan to create their own products.
To access all your products (on the main account), they would first need to go to the [main page](https://app.cryptolens.io/) and click on your username below **Login as**.
If you instead invite your employees using the [restricted account feature](/feature/web-ui/restricted-accounts), they will see all the products and customers they have access to on their own account.
# Customize which licenses are shown first
Source: https://help.cryptolens.io/feature/web-ui/order-by-product
By default, the product page will show the recently created licenses first. If you prefer to change the ordering property, this can be accomplished in two ways:
### Temporary
The last state of the **Order By** will be saved in a locally and will persist for that session.
### Permanently
If you would like to persist the value of **Order By** for all users, this can be accomplished by adding data object (aka. metadata field) for a specific product.
1. On the product page, click on **Data Objects**
2. Create a new data object with the name `cryptolens_orderby` and set the string value to one of the supported values. *Note, this value is case sensitive.*
### List of supported values
You can use one of the following values to change the how licenses are ordered:
| ID (ascending) |
| :-------------------- |
| ID (descending) |
| Created (ascending) |
| Created (descending) |
| Expires (ascending) |
| Expires (descending) |
| Customer (ascending) |
| Customer (descending) |
| F1 (ascending) |
| F1 (descending) |
| F2 (ascending) |
| F2 (descending) |
| F3 (ascending) |
| F3 (descending) |
| F4 (ascending) |
| F4 (descending) |
| F5 (ascending) |
| F5 (descending) |
| F6 (ascending) |
| F6 (descending) |
| F7 (ascending) |
| F7 (descending) |
| F8 (ascending) |
| F8 (descending) |
| Block (ascending) |
| Block (descending) |
| Modified (ascending) |
| Modified (descending) |
# Product configuration
Source: https://help.cryptolens.io/feature/web-ui/product-configuration
Describes custom configurations that can be enabled that will alter behaviour of certain operations.
## Introduction
It is possible to use data objects on the product level to change the default behaviour of certain methods as well as adjust how licenses are shown. On this page, we have listed the different data objects that can be created and the value that they accept.
## List of configurations
| Name | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cryptolens\_unblockextendedlicenses | If the string value is set to **true**, when a blocked license is extended (either through the dashboard or the API), it will automatically be unblocked. |
| cryptolens\_trialactivation | By default, [start countdown upon activation](https://help.cryptolens.io/web-interface/trial-activation) will extend the time-limit of the license each time a new device is registered. If you want to extend the license only for the first activation, set the string value to **first\_activation**. |
| cryptolens\_orderby | The string value can be used to specify how to order licenses on the product page. For example, setting the string value to **Created (descending)** will order all licenses based on the creation date in descending order. |
| cryptolens\_featuretemplate | The string value can be used to specify the [feature template](https://help.cryptolens.io/web-interface/feature-templates). |
| cryptolens\_dobjreset | The string value can be used to automatically reset int value of data objects inside the license keys of the product. The string value is the list of objects, where the object provides information on what type of data objects should be reset and how often. For example, **cryptolens\_dobjreset** can be set to `[{"Name":"credits", "Freq":1, "Default":5}]`,which will set the int value of all data objects (associated with a license key inside the product) to 5 every day whose name is "credits". The "Freq" value can be set to e.g. 2, which will mean every second day, 3 meaning every third day, etc. Since this is a list, you can reset data objects with a different names as well. For example, to reset "credit" every day and "credit2" every second day, you can set **cryptolens\_dobjreset** (on the product level) to `[{"Name":"credit", "Freq":1, "Default":5}, {"Name":"credit2", "Freq":2, "Default":5}]`. |
# Restricted accounts
Source: https://help.cryptolens.io/feature/web-ui/restricted-accounts
## Idea
Operator accounts allow you to grant your employees restricted access to your dashboard. This is useful if you want to prevent them to accidentally remove product or an access token, while still allow them to perform necessary changes to licenses, etc.
All operations performed by the user will be logged in the [Object log](https://app.cryptolens.io/docs/api/v3/GetObjectLog).
## Getting started
### Adding an operator
To invite an employee to sign up for an operator account, you can share the link on the [operators page](https://app.cryptolens.io/Operator). When they have signed up, they will not have any access by default, so you will need to add this afterwards.
### Modifying permissions
Once an employee has signed up, you will be able to grant them permission by clicking on **Modify** on [operators page](https://app.cryptolens.io/Operator).
In most cases, we would recommend to give them two permissions: **edit** permission for **products** and **owner** permission for **customers**. **Resource Id** should be set to 0 in most cases. You only need to set it if you want to restrict access to a specific product or customer only.
## Permissions
In this section we describe what your employees can do with different permission levels.
### Access Type
There are three different access types that you can assign to a user. Note, extra permission is given to users depending on the resource they have permission to, which is covered in *Resources*.
#### View
The user can only view the object.
#### Edit
The user can edit the object but not remove it.
#### Owner
The user can add new and remove existing objects.
### Resources
In this section we cover extra permission given to users depending on the resource.
#### Product
In addition to the permission granted based on the access types described earlier, users will get extra permission to other objects depending on the access type.
View
Users will get read-only access to see all license keys, machine code and data objects (associated with the product, a license key or machine code).
Edit & Owner
Users will be able to add and modifying the aforementioned objects. Moreover, they will also be able to create new customers when creating a new license key (although they won’t see existing customers if they don’t have a separate permission to view customers).
#### Customer
No special permission is granted beyond what is defined under *Access Type*.
#### Analytics
No special permission is granted beyond what is defined under *Access Type*. For now, the analytics portal only supports read-only mode, so **edit** or **owner** permissions don’t give any extra access.
#### Billing
Allows users to manage billing information, access invoices, etc. View permission allows only viewing what is stored whereas Edit allows to change the pricing tier, the attached card, etc.
### Resource Id
When this is set to zero, the user will have access to all products of that type. If you set it to be an id of an object (eg. product id), the user will only be granted the specified permission for that object.
# SKGL vs. SKM15
Source: https://help.cryptolens.io/feature/web-ui/skgl-vs-skm15
This article compares SKGL and SKM15 key generation algorithms and suggests possible use cases for each of them.
Cryptolens platform supports two key generation algorithms, SKGL and SKM15. Both offer different advantages, however it is recommended to use SKM15 instead of SKGL. Note, some methods in the Web API work with a certain type of key generation algorithm only. You can find out more here.
## SKGL
[SKGL](https://help.cryptolens.io/faq/what-is-skgl) – Serial Key Generating Library is an open source, information based key validation algorithm. It is a legacy algorithm to generate license keys. One of the features of SKGL is that it is decentralized, allowing you to validate keys without using Cryptolens. However, one of the advantages of using Cryptolens is the fact that you don’t need to expose the encryption password during key validation.
> Note, each product using SKGL can only store 99999 (10^5-1) keys. Furthermore, since information is stored inside the license key, the license key string will change when certain properties are updated.
## SKM15
SKM15 – is an algorithm that is specifically developed to take an advantage of the fact that most of the applications will directly or indirectly access the license server at some point in time. It’s an information based key generation algorithm that does not contain any useful information such as the creation date. Instead, it relies on the license server or a local license file accompanying the license key.
## SKGL versus SKM15?
Depending on your requirements, both SKGL and SKM15 can work equally well. However, if you are unsure, you should preferably choose SKM15 (once the Web API supports it completely), mainly because it has several features that will be great to most of the users.
## Reasons to choose SKGL
* If you want to be able to get information such as the creation date, expiration date, features, etc, from the license key string using the open-source library.
* If your application will only have access to the licence key (i.e. with no access to neither the license server nor a a signed license file).
## Reasons to choose SKM15
* Your application will either have full access to the license server or a signed license file.
* You want to be able to update the key (either using the control panel or \* when using trial activation) without changing the license key. In SKGL, by changing key information details, it will update the key since the algorithm is decentralized.
* You don’t want to remember a password for each project.
* You want your keys to contain as little amount of information as possible (such as creation date).
# Start countdown upon activation (aka trial activation)
Source: https://help.cryptolens.io/feature/web-ui/trial-activation
## Idea
Trial activation allows you to pre-generate time-limited licenses so that the time-limit starts from the first day of activation. This is especially useful when you don’t know in advance when the license key will be used for the first time.
## Getting started
By default, a newly generated license key does not have this option enabled. In order to enable it,
1. Go to [https://app.cryptolens.io/Product](https://app.cryptolens.io/Product)
2. Select the product you want to use.
3. Click Create new key
4. Tick **start countdown upon activation**
5. Set the Maximum number of machines to 1. (note, this value can be anything greater than zero for this feature to work)
6. Click Create.
If your set time was set to 30 (as an example), then if the user activates the license key in 2 months from now, they will still be able to use the license for 30 days.
## Remarks
Please read these through before you start using feature in production.
### Maximum number of machines greater than 1
Update from **2021-07-26**: It is now possible to configure it so that the license is extended only when the first device activates, independent of the value of *Maximum number of machines*. To do that, you need to create a data object (inside the product) with the name “cryptolens\_trialactivation” and string value “first\_activation”.
Trial activation feature ensures that each new machine code can use the license for a set number of days (specified in set time / period). However, since the “expiration date” is global for the license (shared among all machines), setting maximum number of machines to a value greater than 1 will allow the old machines to use the license until the last machine expires.
It is possible to change this behaviour at the product level by adding a data object named `cryptolens_trialactivation` and setting its string value to one of the following options, depending on the desired behaviour of the method.
1. **Default behaviour** = expiry date will be extended until the limit of the maximum number of machines is reached. For example, let’s say you set the maximum number of machines to 2 and the time to 30 (this value is referred to as the **period** on the product page). When the first machine activates the licence, the expiry date will be extended so that the licence can be used for 30 days. After those 30 days have passed, the second machine activates the licence, which will extend the expiry date by another 30 days. The original machine will still be able to use the licence.
2. **first\_activation** = This will extend the expiry date only for the first activation. Subsequent activations will not extend the date. Please note that if all machines are deactivated and the licence is activated again, the expiry date will be extended once more for the first activation.
3. **disable\_on\_first\_activation** = This behaves the same way as **first\_activation**, except that the feature which starts the countdown upon activation is disabled at the licence level after the first activation. In practice, this means that the expiry date will be extended only once.
4. **disable\_on\_first\_activation\_v2** = Similar to **disable\_on\_first\_activation**, the feature that starts the countdown upon activation will be disabled at the licence level. However, it will otherwise behave in the same way as the default behaviour for that request, meaning that if a new machine is activated, the expiry date will be extended.
### SKGL key generation algorithm
We recommend to always use SKM15, which is the default for all newly created products. Using SKGL will update the license key string, which is described [here](https://app.cryptolens.io/docs/api/v3/Activate).
### Hard coding licenses
Never hard code a trial key into your application. Although the trial will not be extended if only one user is using it, once we allow multiple users to activate the trial (for the same key), they will update the global trial count. If you need trial key functionality, please take a look at [verified trials](/examples/verified-trials).
# AutoCAD
Source: https://help.cryptolens.io/getting-started/autocad
Please refer to the following blog article for more information while we work on this article: [https://cryptolens.io/2019/01/autocad-plugin-software-licensing/](https://cryptolens.io/2019/01/autocad-plugin-software-licensing/)
# Build with AI
Source: https://help.cryptolens.io/getting-started/build-with-ai
Use coding agents, skills, and llms.txt files to integrate Devolens faster.
Use AI coding agents to integrate Devolens (formerly Cryptolens) faster.
Install the full Devolens skill bundle rather than a single skill. The language-specific skills depend on `cryptolens-sdk-common`.
Install from the Claude plugin marketplace:
```text theme={null}
/plugin marketplace add Cryptolens/skills
/plugin install devolens@devolens
```
This installs all twelve skills in the bundle. To pull new versions later, refresh the marketplace with `/plugin marketplace update devolens`.
If you need a manual fallback, copy the folders from the repo's `skills/` directory into `.claude/skills/` or `~/.claude/skills/`, then start a new Claude Code session.
Try:
```text theme={null}
Use the license-offline skill to help me add cached offline license verification to this project.
```
Install the Devolens Codex marketplace:
```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/Cryptolens/skills/main/install.sh | bash
```
Restart Codex, open Plugin Directory, then install `Devolens Licensing` (`devolens`).
The installer places a local marketplace at `$CODEX_HOME/marketplaces/devolens`, defaulting to `~/.codex/marketplaces/devolens` when `CODEX_HOME` is not set. It registers `Devolens` as a marketplace source in `$CODEX_HOME/config.toml`. If you prefer not to run the remote script directly, download [github.com/Cryptolens/skills](https://github.com/Cryptolens/skills) and run `scripts/install_codex_marketplace.sh` locally.
Try:
```text theme={null}
Use the license-key-verification skill to help me add license key verification with cached offline fallback to this project.
```
If your agent does not support skills directly, load the `llms.txt` files below.
If it supports MCP, you can also connect it to the Devolens MCP endpoint for tool access.
## Documentation for AI agents
Load these files to give your agent full context of Devolens (formerly Cryptolens) Web API, license models, use cases and best practises.
| Filename | Summary | When to use |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [llms.txt](https://help.cryptolens.io/llms.txt) (Documentation) | Structured index of the documentation page hosted at [help.cryptolens.io](https://help.cryptolens.io/). | Answering queries on how to get started with e.g. key verification in different languages, available licensing models and best practises. |
| [llms-full.txt](https://help.cryptolens.io/llms-full.txt) (Documentation) | Completed documentation of all pages hosted at [help.cryptolens.io](https://help.cryptolens.io/). | When you need the full context (see above). |
| [llms.txt](https://app.cryptolens.io/docs/api/v3/llms.txt) (Web API) | Structured index of the Web API documentation page. It's language agnostic but provides vital information about each API endpoint. | Answering queries that are not covered by our documentation. This is the source of truth of how the API endpoints work. It's recommended when you are implementing a license flow not covered by examples, or when you need to implement advanced features, such as "field masking" for privacy/compliance reason, understanding of how to configure access tokens for advanced use cases, and more. |
| [llms-full.txt](https://app.cryptolens.io/docs/api/v3/llms-full.txt) (Web API) | Completed documentation of the Web API (see above). | When you need the full context (see above). |
## Starter prompts
Use outcome-based prompts rather than asking for a generic Devolens overview.
| Goal | Prompt |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Add license checks | `Use the license-key-verification skill to add Devolens license key verification to this project.` |
| Add offline support | `Use the license-offline skill to add cached offline license verification to this project.` |
| Add trials | `Use the license-trials skill to add a verified trial license flow to this project.` |
| Gate features | `Use the license-feature-gates skill to add feature-gated licensing for these modules: ...` |
| Add subscriptions | `Use the license-subscriptions skill to add subscription expiry checks and renewal handling.` |
| Add floating seats | `Use the license-floating skill to add floating license support with safe overdraft defaults.` |
| Create keys from a backend | `Use the license-key-generation skill to create Devolens license keys from my backend after payment succeeds.` |
| Automate payments | `Use the license-payment-automation skill to integrate Devolens with Stripe, n8n, Zapier, or another external payment provider.` |
Before publishing, ask your agent to review the integration with the `license-key-verification` skill. See [Checklist before publishing](/getting-started/checklist-before-publishing).
# Checklist before publishing
Source: https://help.cryptolens.io/getting-started/checklist-before-publishing
This guide covers important aspects of the platform that we recommend to review before you publish your application.
### Expiry date is not enforced by default
The expiry date is not enforced by default and it will still be possible to activate a license even when the expiry date has passed. However, there are two ways to enforce it: either by setting up automatic blocking of expired licenses (if you will be using `Key.Activate` method) or by checking if the *Block* property inside the **License Key** object is True (if you are calling Key.GetKey or using offline licensing). You can find more information [here](/feature/web-ui/keys-that-dont-expire).
### Machines are not activated by default
If you plan to use [node-locking](/licensing-models/node-locked) or [floating licensing](/licensing-models/floating), where the goal is to track machines, it’s important that each license that you issue sets the *Maximum number of machines* parameter to a value greater than 0. For example, if you would like to limit the license to 2 machines, *Maximum number of machines* should be set to 2.
### Access token should have minimal scope of permissions
When you are ready to publish your application with Cryptolens, it’s important to ensure that access tokens that are used inside the application that is distributed to the client have as few permissions as possible. Normally, the access token used in the client application should only have Activate permission. Some data that is returned can also be masked if this is needed. Please review [this article](/security/data-policy).
### Verify AI-generated implementations
If a coding agent helped implement the integration, ask it to review the implementation before you ship. If you installed the Devolens skill bundle, use the `license-key-verification` skill and paste this into the same chat session:
```text theme={null}
Use the license-key-verification skill to review the Devolens/Cryptolens integration in this project against:
- https://help.cryptolens.io/llms-full.txt
- https://app.cryptolens.io/docs/api/v3/llms-full.txt
Check that:
- the RSA public key or signed response is verified where the SDK/API supports it;
- the product id, license key, and access token are not hardcoded unless clearly intended;
- the access token permissions match the API methods actually called;
- IsOnRightMachine or equivalent machine-binding checks are only required when the license has maximum number of machines greater than 0;
- offline fallback stores a signed activation response or license file, not the access token;
- API errors are surfaced or logged clearly enough to diagnose access-token, product-id, permission, and subscription issues.
Report any issues with file paths, severity, and the exact change needed.
```
# C/C++
Source: https://help.cryptolens.io/getting-started/cpp
Getting started guide for Cryptolens C and C++ SDK
## Introduction
Cryptolens offers two SDKs for projects using C/C++. When possible, we recommend using the C++ SDK, since the C SDK offers a subset of features available in the C++ SDK.
* C++ SDK: [https://github.com/Cryptolens/cryptolens-cpp](https://github.com/Cryptolens/cryptolens-cpp)
* C SDK: [https://github.com/Cryptolens/cryptolens-c](https://github.com/Cryptolens/cryptolens-c)
For time being, we recommend visiting the GitHub page for each of these projects for more information on how to get started.
# Excel Addins
Source: https://help.cryptolens.io/getting-started/excel
## Idea
In this post we have summarized the necessary steps to add software licensing into an Excel add in.
We recommend to read the **Considerations** section in the end of the tutorial that can help when troubleshooting common errors.
## Implementation
There are two steps to get add key verification into an Excel add in.
### Installing the SDK
Cryptolens offers a .NET SDK that we recommend installing into your projects, which makes it easier to perform license key verification and implement other licensing models (such as offline verifications).
The easiest way is to install [Cryptolens.Licensing through NuGet](https://www.nuget.org/packages/Cryptolens.Licensing). Pre-compiled binaries and the source code are also available on [our GitHub page](https://github.com/Cryptolens/cryptolens-dotnet/).
### Adding the key verification code
If your add in uses [VB.NET](http://VB.NET) or C#, you can find the complete code snippet on [this page](https://help.cryptolens.io/examples/key-verification).
## Considerations
When computing the machine code, we recommend using the latest version of the method, which is used in the key verification tutorial. However, in some versions of Excel, it might not be feasible to call Helpers.GetMachineCode on .NET Framework 4.6. The reason for this is the call we make to `System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform`. To fix this, we have added a boolean flag in `Helpers` class. Before calling `Helpers.GetMachineCode` or `Helpers.IsOnRightMachine`, please set `Helpers.WindowsOnly=True`.
```c# theme={null}
Helpers.WindowsOnly = true;
var machineCode = Helpers.GetMachineCode();
```
If the approach above does not work, please try the following call instead:
```c# theme={null}
var machineCode = SKGL.SKM.getMachineCode(SKGL.SKM.getSHA1);
```
# Getting started guide
Source: https://help.cryptolens.io/getting-started/getting-started-guide
Cryptolens is a powerful platform that accommodates many different use cases, application types, licensing models, and additional licensing features.
Here are all of the steps you need to take to implement a basic software licensing system:
After license [key verification](/examples/key-verification) is set up in your code, you can customize the licensing system to suit your unique use case using our detailed guides.
## Setting things up
You first need to create a product using our dashboard. Each product gets its own product ID which is displayed on the product page, and this is important for a later step. You can keep all of the default values when creating your first product.
After you have created a product, you can start to create license keys for that specific product. As you can see, a license key in Cryptolens consists of a 20-character-long identifier that you then can distribute out to customers. All default values can be kept when creating your first license key, but this is where you would later specify the characteristics of the license key, such as the expiry date.
Web API is a channel that can be used to talk to Cryptolens from external applications. It contains a wide range of methods that will respond to different information depending on input parameters. For example, these methods allow you to activate keys, generate new ones, get a list of activated machines, and so much more. It’s always possible to talk to the Web API directly, however, you will most likely use an existing SDK that simplifies that task.
You can find information about how to download our SDKs in your programming language [here](/libraries/index).
After you have installed our Client API, the next step is to paste a code snippet into your own product. This is the code that we will modify slightly in later steps. You can find code examples in your programming language [here](https://help.cryptolens.io/examples/key-verification). We also have YouTube videos featuring how to set up key verifications in different languages.
After pasting the [key verification code](/examples/key-verification) snippet into your own code, there are three parameters that you need to replace:
* Your RSA Public Key
* Your Access Token (called token or auth in code examples)
* Your Product ID
Navigate to the bottom of [this page](https://app.cryptolens.io/docs/api/v3/QuickStart) to find your RSA Public Key. If you are logged in, you can view it below:
Please log in to view your RSA Public key here. [Click here](https://app.cryptolens.io/user/serviceauth/?appId=help\&substr=getting-started/getting-started-guide) to view it.
You can now replace the RSA Public Key in the [key verification code](https://help.cryptolens.io/examples/key-verification) snippet with your own.
For testing purposes, you can use the Access Token that is listed at the bottom of [this page](https://app.cryptolens.io/docs/api/v3/QuickStart). If you are logged in, it will also show up below:
Please log in to view your access token here. [Click here](https://app.cryptolens.io/user/serviceauth/?appId=help\&substr=getting-started/getting-started-guide) to view it.
However, when you begin to sell your product, we recommend that you create your own Access Tokens with the correct permissions for your unique use case. You can create access tokens [here](https://app.cryptolens.io/User/AccessToken#/).
Regardless if you use the pre-made Access Token or create your own, you need to paste your Access Token into the [key verification code](https://help.cryptolens.io/examples/key-verification) snippet in your product.
One of the first steps in this guide was to create a product and license key in our dashboard. You can now copy the Product ID from the product page and paste it into the [key verification code](https://help.cryptolens.io/examples/key-verification) snippet.
## Testing
Now that you have specified your unique RSA Public Key, Access Token, and Product ID, you can start to test the licensing system.
In production, you need to have a way for the customer to provide their license key, and that value should be pasted in the license key parameter. For testing purposes, you can simply copy the license key string that you created in our dashboard earlier and paste it into the license key field in the [code snippet](https://help.cryptolens.io/examples/key-verification). The code will now check if that specific license key is valid.
> Many of our code examples include a call to **IsOnRightMachine** method. If you created a license key without changing any of the properties, please remove this check. For this check to work, the maximum number of machines property needs to be set to a value greater than 0.
After doing the changes we listed above, the [key verification code](/examples/key-verification) should now work if you run the code. The code should return a message saying that the license key is valid. Relevant information such as the status of certain feature flags can also be displayed, and you can now program your code to act the way you want it to based on the status of the license key.
## Possible customizations
You have now set up a very basic software licensing system! There are several ways to change the setup to accommodate your unique requirements. Here are guides you can use as the next step so that your licensing system works the way you want it to:
1. Licensing Models
1. Subscriptions
2. Pay per Use (usage-based licensing)
3. Node-Locking
4. Floating Licenses
5. SDK Licensing
6. Account-Based Licensing
2. Offline Licensing (License Server)
3. Setting up Payments
4. Automation with Zapier
5. Integrations
6. Additional Features
# Go
Source: https://help.cryptolens.io/getting-started/go
Getting started guide for the Go SDK.
## Introduction
The easiest way to set up licensing in your Go application is by using our Go SDK. It's available on the link below:
* [https://github.com/Cryptolens/cryptolens-golang/tree/go\_mod](https://github.com/Cryptolens/cryptolens-golang/tree/go_mod)
In addition to the SDK, we provide examples in the following repo:
* [https://github.com/Cryptolens/cryptolens-golang-examples](https://github.com/Cryptolens/cryptolens-golang-examples)
Before reviewing the examples, we recommend going through the [Getting Started tutorial](/getting-started/getting-started-guide).
## Examples
In the examples below, you would need to replace the token, publicKey, product id and the license key. How they are found is described in the [Getting Started tutorial](/getting-started/getting-started-guide).
### Normal activation
```go theme={null}
package main
import (
"fmt"
"github.com/Cryptolens/cryptolens-golang/cryptolens"
"time"
)
func main() {
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("License key activation failed!")
return
}
fmt.Printf("License key for product with id: %d\n", licenseKey.ProductId)
if time.Now().After(licenseKey.Expires) {
fmt.Println("License key has expired")
return
}
if licenseKey.F1 {
fmt.Println("Welcome! Pro version enabled!")
} else {
fmt.Println("Welcome!")
}
}
```
### Offline activation
```go theme={null}
package main
import (
"errors"
"fmt"
"github.com/Cryptolens/cryptolens-golang/cryptolens"
"io/ioutil"
"time"
)
func ActivateAndSaveLicenseKey() (string, error) {
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
return "", errors.New("Initial license key activation failed")
}
serialized, err := licenseKey.ToBytes()
if err != nil {
return "", err
}
f, err := ioutil.TempFile("", "cryptolens_example_offline_")
if err != nil {
return "", err
}
defer f.Close()
_, err = f.Write(serialized)
if err != nil {
return "", err
}
return f.Name(), nil
}
func main() {
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
filename, err := ActivateAndSaveLicenseKey()
if err != nil {
fmt.Println("Failed to activate or save license key")
return
}
fmt.Printf("License key saved to file %s\n\n", filename)
savedKeyBytes, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println("Failed to read saved license key")
return
}
licenseKey, err := cryptolens.KeyFromBytes(savedKeyBytes)
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("Error in saved license key")
return
}
fmt.Printf("License key sucessfully loaded from file!\n")
if time.Now().After(licenseKey.Expires) {
fmt.Println("License key has expired")
return
}
if licenseKey.F1 {
fmt.Println("Welcome! Pro version enabled!")
} else {
fmt.Println("Welcome!")
}
}
```
# Android and iOS
Source: https://help.cryptolens.io/getting-started/ios-android
## Idea
The way software licensing can be added to an application for a mobile OS such as Android or iOS depends on what framework your application is written in. There are multiple possibilities:
* **Default for that platform**: the application is written in the default language for that platform i.e. Java for Android and Objective-C for iOS.
* **Bridge**: the original application/library is written in C/C++ (eg. producing a .so file) with a wrapper in the default language for that platform (eg. Java for Android).
* **Other framework**: the application is written in a framework such as Unity or Xamarin.
We will cover each of these cases below:
## Implementation
### Default language
#### Android
Since Java is the default language of Android applications, you can use our [Java client](https://github.com/cryptolens/cryptolens-java). You need to use the jar file called `cryptolens-android.jar`. Pre-compiled jar files can be found [here](https://github.com/Cryptolens/cryptolens-java/releases).
> Please note that `Helpers.GetMachineCode()` is not supported and that when you call `Helpers.IsOnRightMachine()`, you need to use those that allow you to specify a custom machine code. You can use [InstanceId](https://developers.google.com/instance-id/guides/android-implementation) instead or other approaches described later in the tutorial.
Adding the library
You can add the library by adding `api files('libs/cryptolens-android.jar')` in `build.gradle` (Module: app), as shown below. Please note that `cryptolens-android.jar` needs to be in the `libs` folder of your project.
```
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
api files('libs/cryptolens-android.jar')
}
```
**Verifying a license**
Since license key verification (eg. Key.Activate) uses networking, we need to call it asynchronously (i.e. not on the main thread). Moreover, we need to ensure that internet permission is set.
To call a method asynchronously, we can wrap it as shown below. For example, we can paste the code snippet from the [key verification tutorial](https://help.cryptolens.io/examples/key-verification) into the `run` method below:
```java theme={null}
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// call eg. Key.Activate.
}
});
thread.start();
```
Internet permission requires a small change in the manifest file:
```
...
here](https://help.cryptolens.io/getting-started/ios-android).
### **Key verification**
The following example is similar to what is covered in the [key verification tutorial](/examples/key-verification).
Assuming you have referenced the `cryptolens.jar` file, the code below should generate successful result. A working project with the code below can be found in the [example-app](https://github.com/Cryptolens/cryptolens-java/tree/master/example-app) folder.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public class Main {
public static void main(String[] args) {
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = "WyIyNjA5IiwiaWE5b0VFT3Q2eDlNR2FvbHBHK2VOYUZ4bzNjT3h5UkNrMCtiYnhPRSJd";
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL", Helpers.GetMachineCode(2)));
if (license == null || !Helpers.IsOnRightMachine(license, 2)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
}
}
```
In order to adjust this code to your products, several parameters needs to be changed, which we outline below:
* `RSAPubKey` - the RSA Public key, which can be found on [this](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys) page.
* `auth` - the access token (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
* `product_id` - the id of the product can be found on the product page (in the example above, it's 3646).
* `key` - the license key to be verified (above it's MPDWY-PQAOW-FKSCH-SGAAU).
* `machine_code` - the unique id of the device, which may require root access. Note, this value is not the same as the one generated by the .NET client.
**Note**: The code above assumes that node-locking is enabled. By default, license keys created with Maximum Number of Machines set to zero, which deactivates node-locking. As a result, machines will not be registered and the call to Helpers.IsOnRightMachine(license) will return False. You can read more about this behaviour [here](https://help.cryptolens.io/faq/index#maximum-number-of-machines). For testing purposes, please feel free to remove Helpers.IsOnRightMachine(license) from the if statement.
### **Offline activation (saving/loading licenses)**
Assuming the license key verification was successful, we can save the result in a file so that we can use it instead of contacting Cryptolens.
```java theme={null}
String licenseString = license.SaveAsString();
```
When loading it back, we can use the code below:
```java theme={null}
LicenseKey newLicense = LicenseKey.LoadFromString(RSAPubKey, licenseString);
```
If you want to make sure that the license file is not too old, you can specify the maximum number of days as shown below (after 30 days, this method will return null).
```java theme={null}
LicenseKey newLicense = LicenseKey.LoadFromString(RSAPubKey, licenseString, 30);
```
**Note:** `LicenseKey.LoadFromString` does not check the *ProductId*. In case you have multiple products, we recommend that you check that the *ProductId* corresponds to the product where the user tries to use the license file.
### **Floating licenses**
[Floating licenses](https://help.cryptolens.io/licensing-models/floating) can be enabled by passing a floatingTimeInterval to the `ActivateModel`. Optionally, you can also allow customers to exceed the bound by specifying the maxOverdraft.
The code below has a floatingTimeInterval of 300 seconds and maxOverdraft set to 1. To support floating licenses with overdraft, the call to `Helpers.IsOnRightMachine(license, true, true)` needs two boolean flags to be set to true.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = "WyIyNjA5IiwiaWE5b0VFT3Q2eDlNR2FvbHBHK2VOYUZ4bzNjT3h5UkNrMCtiYnhPRSJd";
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2), 300, 1));
if (license == null || !Helpers.IsOnRightMachine(license, 2, true, true)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
}
```
### **Deactivation**
In order to deactivate a license, we can call the `Key.Deactivate` method, as shown below. Note, we need an access token with Deactivate permission, so the ones used in earlier examples cannot be used (unless they have this permission).
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String auth = "";
boolean result = Key.Deactivate(auth, new DeactivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2)));
if (result == true) {
System.out.println("Deactivation successful.");
} else {
System.out.println("Deactivation failed.");
}
}
```
The call above is useful when [node-locking](https://help.cryptolens.io/licensing-models/node-locked) is used. If it's a floating license, deactivation is not necessary since licenses will be deactivated automatically after a certain period of time. However, to force a deactivation earlier, you can use similar code as above with addition of a boolean flag.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String auth = "";
boolean result = Key.Deactivate(auth, new DeactivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2), true));
if (result == true) {
System.out.println("Deactivation successful.");
} else {
System.out.println("Deactivation failed.");
}
}
```
### **Calling through the license server**
If you would like to re-route the requests through our [license-server](https://github.com/cryptolens/license-server) that is installed on the client site, you can specify its url using `LicenseServerUrl` parameter. All API models expose this parameter.
For example, let's suppose that your client runs the license server on `http://10.1.1.6:8080` and you want to call `Key.GetKey()`. In this case, we first define all parameters for the request and then modify the license server url:
```java theme={null}
GetKeyModel model = new GetKeyModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL");
model.LicenseServerUrl = "http://10.1.1.6:8080";
```
We do this because there is currently no overload method that accepts `LicenseServerUrl` parameter.
The entire code is shown below:
```java theme={null}
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTn+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = APIKey.get("getkeyactivate");
APIError error = new APIError();
GetKeyModel model = new GetKeyModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL");
model.LicenseServerUrl = "http://10.1.1.6:8080";
LicenseKey license = Key.GetKey(auth, RSAPubKey, model , error);
if (license == null) {
System.out.println("The license does not work.");
System.out.println("Error: " + error.message);
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
# .NET desktop application
Source: https://help.cryptolens.io/getting-started/net-intro
An older tutorial on how to integrate Cryptolens licensing into a desktop application in .NET
This is an old tutorial that we have kept that shows how you can implement basic license verification in a .NET application. We recommend to review our [Getting Started Guide](/getting-started/getting-started-guide) and refer to our [key verification](/examples/key-verification) tutorial for production ready examples.
Our aim of this tutorial is to guide you through, step by step, the process of implementing Cryptolens into your application. We’ll assume that you’ve developed a .NET application and use [Visual Studio 2013](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx). If you are using a different language, we recommend to check out [this page](https://help.cryptolens.io/web-api/skm-client-api).
> If you want to jump straight into the code, please check out the [key verification](/examples/key-verification) tutorial.
> Our application will have three features (voice, audio and converter), and the ability to license these as a ‘subscription’ (time-limited) or ‘perpetual’ (unlimited time). We will check the license key when we have an Internet connection. If we’re offline, we will use the saved license file. The user will be able to use the software 90 days offline at most.
## Cryptolens .NET Client
Now we can start using all of the power of the Cryptolens Client API.
The easiest way to get Cryptolens Client API is through NuGet in Visual Studio.
1. Go to the solution explorer and right click on the project (not the solution).
2. Click on `Manage NuGet Packages...`
Now, type `Cryptolens.Licensing` into the Browse tab, select `SKM Client API` and install it.
## Adding Code
We almost have all the pieces in place to get a working licensing solution. We will cover the code in this section and look at how to retrieve **access tokens** and **public key (RSA)** later.
Our application will have two places where we will need to insert the code:
* During license key registration - the first time the user inserts a license key
* During application start - to check for existing license on launch
You can download the source code [here](https://github.com/Cryptolens/Examples/tree/master/Digital%20Tools%20Collection).
> Note, you can find more detailed examples of .NET [here](https://help.cryptolens.io/examples/) and other environments [here](https://help.cryptolens.io/web-api/skm-client-api). Please don’t hesitate to contact us should you have any questions.
### License Key Registration
```
' The code below is based on https://help.cryptolens.io/examples/key-verification.
Dim token = "{access token with permission to access the activate method}"
Dim RSAPubKey = "{Your RSA Public key}"
Dim keyStr = TextBox1.Text.Replace(" ", "")
Dim result = Key.Activate(token:=token, parameters:=New ActivateModel() With {
.Key = keyStr,
.ProductId = 3349,
.Sign = True,
.MachineCode = Helpers.GetMachineCode()
})
If result Is Nothing OrElse result.Result = ResultType.[Error] OrElse
Not result.LicenseKey.HasValidSignature(RSAPubKey).IsValid Then
' an error occurred or the key is invalid or it cannot be activated
' (eg. the limit of activated devices was achieved)
MsgBox("Unable to access the license server or the key is wrong.")
Else
' everything went fine if we are here!
Dim license = result.LicenseKey
Form1.Button1.Enabled = license.HasFeature(1).IsValid() ' either we have feature1 or not.
Form1.Button2.Enabled = license.HasFeature(2).IsValid() ' either we have feature2 or not.
Form1.Button4.Enabled = license.HasFeature(3).IsValid() ' either we have feature3 or not.
Form1.Text = "Digital Tools"
If license.HasFeature(4).HasNotExpired().IsValid() Then
' feature 1 is a time limited, so we check that it has not expired.
Form1.Text = "Digital Tools - " + license.DaysLeft().ToString() + " day(s) left"
ElseIf license.HasNotFeature(4).IsValid() Then
Else
MsgBox("Your license has expired and cannot be used.")
nolicense()
End If
license.SaveToFile()
End If
```
This code will retrieve all the license data (using `Refresh`) and enable buttons depending on the features in the license. Features 3-5 indicate a certain functionality whereas Feature 1 is used to indicate a time-limit (in case it’s a subscription). Once we have a valid license, we will save it locally (it will be signed by SKM, because **sign** is set to true in `Refresh`).
### Application Start
```
NoLicense()
' The code below is based on https://help.cryptolens.io/examples/key-verification.
Dim license = New LicenseKey().LoadFromFile()
Dim publicKey = "{Your RSA Public key}"
Dim token = "{access token with permission to access the activate method}"
If license IsNot Nothing Then
Dim result = Key.Activate(token:=token, parameters:=New ActivateModel() With {
.Key = license.Key,
.ProductId = 3349,
.Sign = True,
.MachineCode = Helpers.GetMachineCode()
})
' either we get a fresh copy of the license or we use the existing one (given it is no more than 90 days old)
If (result.Result <> ResultType.Error And result.LicenseKey.HasValidSignature(publicKey).IsValid) Or license.HasValidSignature(publicKey, 90).IsValid() Then
Button1.Enabled = license.HasFeature(1).IsValid() ' either we have feature1 or not.
Button2.Enabled = license.HasFeature(2).IsValid() ' either we have feature2 or not.
Button4.Enabled = license.HasFeature(3).IsValid() ' either we have feature3 or not.
Text = "Digital Tools"
If license.HasFeature(4).HasNotExpired().IsValid() Then
' feature 1 is a time limited, so we check that it has not expired.
Text = "Digital Tools - " + license.DaysLeft().ToString() + " day(s) left"
ElseIf license.HasNotFeature(4).IsValid() Then
' not time limited.
Else
MsgBox("Your license has expired and cannot be used.")
NoLicense()
End If
license.SaveToFile()
Else
MsgBox("Your license has expired and cannot be used.")
End If
Else
' no license found. you could tell the user to provide a license key.
End If
```
When your application launches, we will try to get a new version of the license key, in case it has been modified. If we don’t have internet access, we will use the local copy as long as 90 days have not passed since it was retrieved. Everything else is the same as when the user registers the license for the first time.
## Access Tokens
In all of the code examples, you will see variables such as `token` or `auth`. They refer to an [access token](https://app.cryptolens.io/docs/api/v3/auth).
The idea behind access tokens is to allow you to:
* identify yourself with Cryptolens (authentication) - let Cryptolens know that you are you
* make sure only desired permission is given to something (authorization) - eg. method scope, product, etc.
Using an access token, you can specify the methods you want to be able to call, the product you want to use, the license key, and optionally feature you want to change. You can read more about them [here](https://app.cryptolens.io/docs/api/v3/auth).
### Creating a new Access Token
In order to create an access token with the permission required by the previous example:
1. Go to ‘Your account name’ (right corner) > ‘Access Token’.
2. Click on ‘Create new Access Token’. You will now be on [this page](https://app.cryptolens.io/User/AccessToken#/newtoken).
3. Enter a name, such as “ActivateToken”.
4. Check the ‘Activate’ box (under LicenseKey)
5. Select the ‘Product Lock’ to be the name of your application.
6. Press ‘Create an Access Token’.
7. Copy the access token and replace our token with yours.
### Example
## Public Key
In many cases, you will store license key data locally on your customers’ device, partly to allow your customers to use the application offline. At the same time, you don’t want them to be able to modify the license key data (for example, the number of features they are entitled to and expiration date).
Therefore, Cryptolens will sign the license key (if you explicitly tell it to do so) with your **private key**. The **public key** will allow your application to check that the license key file hasn’t been modified since it was by Cryptolens.
The **public key** won’t allow them to re-sign the data, only to validate it.
### Finding your Public Key
In order to find your unique public key:
1. In the menu in the right corner (with your name), select ‘Security Settings’.
2. Copy the entire public key and replace it with yours (in the `publicKey` variable).
## Create a Key
We’re now ready to test the code using your account.
1. Select the product from the [list](https://app.cryptolens.io/Product).
2. Click on ‘Create a new Key’.
3. Check features 3,4,5.
4. Optionally, if you want to have a time limit, check feature 1 too.
In case you kept the code as it is (without replacing the token and pubkey with yours), you can test the following:
* `IWXAZ-FVMOD-KBZQU-DKUFW` - all features in one years since this tutorial was written
* `IYVOO-JCWXQ-LCRBI-ZAIQZ` - all features but the recorder with no time constraint.
### Done!
You have successfully completed this tutorial! If you would have any questions, please get in touch us with us using chatbox available on each page.
Good luck!
## Next steps
You’ve now implemented Cryptolens into your application. Here’s what’s next:
1. [Get started with payment forms](/payments/payment-forms)
2. [Learn more about possible licensing models](/licensing-models/index)
3. [Understand GDPR and its implications](/security/gdpr)
# Node Js
Source: https://help.cryptolens.io/getting-started/node
## Introduction
The easiest way to set up licensing in your Node JS application is by using our Node SDK. It's available on the link below:
* [https://github.com/Cryptolens/cryptolens-nodejs](https://github.com/Cryptolens/cryptolens-nodejs)
## Getting started
### Installation
```bash theme={null}
npm add cryptolens
```
### Key Verification
To verify a license key, you can use the code below. The RSAPublicKey, token and the product id can be found on [this page](/examples/key-verification).
```javascript theme={null}
const key = require('cryptolens').Key;
const Helpers = require('cryptolens').Helpers;
var RSAPubKey = "Your RSA Public key, which can be found here: https://app.cryptolens.io/User/Security";
var result = key.Activate(token="Access token with with Activate permission", RSAPubKey, ProductId=3349, Key="GEBNC-WZZJD-VJIHG-GCMVD", MachineCode=Helpers.GetMachineCode());
result.then(function(license) {
// success
// Please see https://app.cryptolens.io/docs/api/v3/model/LicenseKey for a complete list of parameters.
console.log(license.Created);
}).catch(function(error) {
// in case of an error, an Error object is returned.
console.log(error.message);
});
```
### Offline activation (saving/loading licenses)
Assuming the license key verification was successful, we can save the result in a file so that we can use it instead of contacting Cryptolens.
First, we need to add the reference to the helper methods:
```javascript theme={null}
const Helpers = require('cryptolens').Helpers;
```
We can now proceed and save it as a string.
```javascript theme={null}
var licenseString = Helpers.SaveAsString(license);
```
When loading it back, we can use the code below:
```javascript theme={null}
var license = Helpers.LoadFromString(RSAPubKey, licenseString);
```
If you want to make sure that the license file is not too old, you can specify the maximum number of days as shown below (after 30 days, this method will return null).
```javascript theme={null}
var license = Helpers.LoadFromString(RSAPubKey, licenseString, 30);
```
# Platforms
Source: https://help.cryptolens.io/getting-started/platform-overview
## Quick start
If you are new to Cryptolens, we recommend to go through the basics, such as [creating new account](https://help.cryptolens.io/getting-started/create-account) and [new product](https://help.cryptolens.io/getting-started/new-product).
## Supported platforms
More examples can be found on our [GitHub](https://github.com/Cryptolens) page. Please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io) if you have any questions.
# Python/Autodesk Maya
Source: https://help.cryptolens.io/getting-started/python-autodesk-maya
In newer versions of Autodesk Maya, where Python 3.\* is used, you can use our standard Python SDK.
The SDK is available at [https://github.com/Cryptolens/cryptolens-python](https://github.com/Cryptolens/cryptolens-python)
# Rhino 3D / Grashopper
Source: https://help.cryptolens.io/getting-started/rhino-ceros-3d-grashopper
## Idea
In this article we will cover steps necessary to set up key verifications in your Rhinoceros or Grashopper plugin using Cryptolens.
## Implementation
### Client library
By adding the Cryptolens client library, you get access to methods such as `Key.Activate` that make it easier to verify licenses, and perform other licensing related operations.
The easiest way is to install `Cryptolens.Licensing` using NuGet. However, based on our tests, Rhino 8 (and potentially other versions of Rhino) do not seem to support this approach. As a workaround, we suggest the steps below that have been tested in Rhino 8.
1. Download `Cryptolens.Licensing.dll` [from this link](https://github.com/Cryptolens/cryptolens-dotnet/releases).
2. Extract the folder.
3. In Solution Explorer in Visual Studio, right click on **Dependencies** under the name of your project.
4. Using **Browse**, select the path to Cryptolens.Licensing.dll located in the **netstandard2.0**.
5. Using NuGet, install Newtonsoft.Json vs 13.
### Key verification code
Adding key verification code works the same way as described in the [key verification tutorial](/examples/key-verification).
If you would encounter any issues or have any other questions, please reach out to our support.
# Troubleshooting guide for licensing
Source: https://help.cryptolens.io/getting-started/troubleshooting-guide
When implementing Cryptolens into your software, you may get different of errors depending if you call the API directly or through one of the client libraries. This section covers the most common errors, why they occur and how to troubleshoot them.
## Basics
### API errors
The majority of errors (99%) are caused by a wrong **product id**, **access token** or **RSA Public key**. In some cases, it can also be because an active subscription is missing.
Our client libraries might not always show the real error message and instead display a generic error. To find out the real error message from the Web API, you can call the method through curl or in the browser. For example, to check the access token, you can call curl as shown below:
```bash theme={null}
curl https://api.cryptolens.io/api/key/Activate? \
-d token= \
-d ProductId= \
-d Key=
```
You can also check this with the browser. The call above would translate to:
```
https://api.cryptolens.io/api/key/Activate?token=&ProductId=&Key=
```
The most common errors and their cause is summarized below:
| Error message | Description |
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Unable to authenticate` | The access token is wrong. It is also shown if the subscription has expired. API access requires an active subscription. |
| `Something went wrong. Please contact support@cryptolens.io and we will try to fix it.` | A temporary server error. Try again with a new request. |
| `No active subscription found. Please contact support.` | No active subscription is associated with the account and thus the API call failed. The error can be fixed by updating your billing information and add a valid payment method on the [billing page](https://app.cryptolens.io/billing). |
| `Access denied` | The access token is correct but it does not have sufficient permission. Make sure that if you, for example, want to call GetKey that the access token has “GetKey” permission. |
| `Not enough permission and/or key not found.` | If you have checked that the product contains the license key and the access token has the right permission to call the method, this error can be fixed by setting Key Lock to -1 when creating the access token (assuming that you are working with data object related methods). |
### SDK errors
SDKs and the code implementation can also contribute to errors. A common error is when `IsOnRightMachine` is included in the key verification code but the license is not set up to track machines. If your code relies on this check in such case, the code will fail.
For example, let’s look at the Python code to verify licenses:
```python theme={null}
if result[0] == None or not Helpers.IsOnRightMachine(result[0], v=2):
# an error occurred or the key is invalid or it cannot be activated
# (eg. the limit of activated devices was achieved)
print("The license does not work: {0}".format(result[1]))
else:
# everything went fine if we are here!
print("The license is valid!")
license_key = result[0]
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
If the license uses the default value of the *maximum number of machines*, the code above will fail with the error message that the license does not work. To fix this, you can either remove `Helpers.IsOnRightMachine` call or set *maximum number of machines* to a value greater than 0.
### SDK errors during implementation
We recommend to review the troubleshooting section of the [Key verification tutorial](https://help.cryptolens.io/examples/key-verification#troubleshooting) to see common errors and how to fix them.
## Other tools
Another useful tool to determine the cause of an error is the [Log Explorer](https://app.cryptolens.io/extensions/WebAPILogs). It tracks many of the customer-facing methods (such as Activate, Deactivate, and data object related methods).
# Unity 3D / Mono
Source: https://help.cryptolens.io/getting-started/unity
## Idea
In this post we have summarized the necessary steps to add software licensing into a Unity game. Unity uses Mono runtime, which means that a special version of our .NET client library can be used.
We recommend to read the **Considerations** section in the end of the tutorial that can help when troubleshooting common errors.
## Implementation
### Client library
To implement license key verification a Unity game, we can use a special build of the [client library for .NET](https://github.com/cryptolens/cryptolens-dotnet) that is platform independent. You can download it [here](https://github.com/Cryptolens/cryptolens-dotnet/releases). Make sure to reference the binary with the name `Cryptolens.Licensing.CrossPlatform.dll`. You can also install it through [NuGet](https://www.nuget.org/packages/Cryptolens.Licensing.CrossPlatform/).
#### A note about Newtonsoft.Json
`Cryptolens.Licensing.CrossPlatform` depends on the `Newtonsoft.Json` library. In earlier versions of Unity, the `Newtonsoft.Json.dll` had to be included alongside the `Cryptolens.Licensing.CrossPlatform.dll` file (it is provided in the same [zip file as our library](https://github.com/Cryptolens/cryptolens-dotnet/releases/tag/v4.0.36)).
In later versions (from our tests, since 2020.3.16f1), Unity added Newtonsoft.Json as an internal package, meaning that it no longer needs to be included in the Asset folder. However, depending on the Newtonsoft.Json version used by Unity, you may have to recompile our library to target that version. In sum, there are three cases:
If Unity does not have an internal Newtonsoft.Json package
This is the case for older versions of Unity (from our tests, for versions earlier than 2020.3.16f1). In this case, you need to include both `Cryptolens.Licensing.CrossPlatform.dll` and `Newtonsoft.Json.dll`.
If Unity uses Newtonsoft.Json v13
In this case, we recommend using the .NET Framework 4.8 binary (in the net48 folder) without including the `Newtonsoft.Json.dll` file. It should be possible to use the .NET Standard assembly too, and this has been tested in 2022.3.39f1. However, if you are using an older version of Unity, you might need to change a setting to enable .NET Standard. We have outlined this in the [Considerations](https://help.cryptolens.io/getting-started/unity#considerations) section at the bottom of this page.
> If you are using the latest LTS version of Unity, it should already by targeting Newtonsoft.Json v13.
If Unity uses a different version of Newtonsoft.Json
In this case, our library needs to be recompiled with a few changes. The steps are as follows:
* Step 1: [Install the .NET SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0).
* Step 2: [Clone the repository](https://github.com/Cryptolens/cryptolens-dotnet) or [download the zip file](https://github.com/Cryptolens/cryptolens-dotnet/archive/refs/heads/master.zip).
* Step 3: In the Cryptolens.Licensing folder, open `Cryptolens.Licensing.CrossPlatform.csproj`. Remove the following lines:
```
...
true
false
certifikat.pfx
...
```
* Step 4: Change Newtonsoft.Json version from `13.0.1` to `12.0.3`(or any other version you would like to use) in the section below:
```
```
* Step 5: Go back to the main folder (with `sln` files) and run the following command: `dotnet build Cryptolens.Licensing.CrossPlatform.sln`.
* Step 6: Navigate to `Cryptolens.Licensing\bin\Debug\net48` (or `Cryptolens.Licensing\bin\Release\net48`) and copy the `Cryptolens.Licensing.CrossPlatform.dll` into the assets folder.
If you have any questions, please reach out to us at [support@cryptolens.io](mailto:support@cryptolens.io).
### License key verification
Once you have the binaries, we can get license key verification up and running quite quickly. We assume that already have [an account](https://help.cryptolens.io/getting-started/create-account) and [a product](https://help.cryptolens.io/getting-started/new-product). There are just two things left:
1. Add the Cryptolens.Licensing.dll and in some cases Newtonsoft.Json.dll into the **Assets folder** (please check out the section earlier when Newtonsoft.Json needs to be included).
2. Include the [license key verification logic](https://help.cryptolens.io/examples/key-verification).
**Note:** we recommend to avoid using [Key.Activate](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html?#SKM_V3_Methods_Key_Activate_System_String_SKM_V3_Models_ActivateModel_) that takes in an [ActivateModel](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Models.ActivateModel.html) since signature verification will not work on some platforms. Instead, we recommend to use a special version of [Key.Activate](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html#SKM_V3_Methods_Key_Activate_System_String_System_Int32_System_String_System_String_System_Boolean_System_Int32_System_Int32_) as shown below:
```c# theme={null}
// call to activate
var result = Key.Activate(token: auth, productId: 3349, key: "GEBNC-WZZJD-VJIHG-GCMVD", machineCode: "foo");
// obtaining the license key (and verifying the signature automatically).
var license = LicenseKey.FromResponse("RSAPubKey", result);
```
Also, when implementing offline verification, please call `LoadFromFile` and `LoadFromString` with the RSAPubKey. The format of the license file should be set to **Other languages** (see more [here](https://help.cryptolens.io/faq/index#protocols)) when downloading it in the dashboard or using Activation Forms.
You can download a sample project [here](https://github.com/Cryptolens/Examples/tree/master/unity).
### Other licensing models
Since Unity uses C# as the scripting interface, you can use all of the .NET examples provided in this wiki. As a next step, please take a look at the available [licensing models](https://help.cryptolens.io/licensing-models/licensetypes).
Please keep in mind if you use the version of [Key.Activate](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html#SKM_V3_Methods_Key_Activate_System_String_System_Int32_System_String_System_String_System_Boolean_System_Int32_System_Int32_) that was suggested earlier, you would need to create a license key object from the response such as `LicenseKey.FromResponse(result).SaveToFile()` to be able to use the extension methods, eg. `result.LicenseKey.SaveToFile()` will not work.
### Considerations
The `GetMachineCode()` and `IsOnRightMachine()` methods require root access on Linux. If you are using Unity with IL2CCP, `Helpers.GetMacAddress()` can be a better alternative, since other methods may throw an error.
Some users who build and then run the unity project may get a null response from `Key.Activate`, which will be treated as if the license key is invalid. We recommend to set **API Compatibility level** to .NET 4.x in Edit > Project Settings > Player > Other Settings > API Compatibility Level. In this case, the the binaries for .NET 4.\* need to be used, i.e. not the .NET Standard version.
# Introduction
Source: https://help.cryptolens.io/index
Welcome to Cryptolens documentation.
We are moving to a new documentation page. If there is something you cannot find on this page, please reach out to us through the chat box or by emailing us at [support@cryptolens.io](mailto:support@cryptolens.io).
## Setting things up - licensing
Our getting started guide covers all the steps from creating product to adding our key verification code snippet in your code.
Find the best licensing model for your use case. E.g. node-locked/floating, usage based, license vs user account authnetication, and more.
Review all examples of the most common use cases.
Review all supported platforms out of the box and specifics related to each of them.
When setting things up, we recommend to review our [troubleshooting guide](/getting-started/troubleshooting-guide). Once you are ready to publish the integration to production, please review [this checklist](/getting-started/checklist-before-publishing).
## Payments and integrations
The first step to world-class documentation is setting up your editing environments.
Check out how you can process payments with Stripe, PayPal, FastSpring and other providers.
Our Zapier app allows you to automate workflows e.g. payments and CRM integrations.
## Other features
Allows your customers to manage their licenses and purchase new ones.
Allows you to manage updates and send out notifications to some or all users.
Allows you to invite resellers, who can issue licenses and manage customers on their own.
Allows you to support enterprise users who cannot connect to the internet.
# C
Source: https://help.cryptolens.io/libraries/c
C client SDK reference
We recommend to review the repository of the C client SDK for more examples: [https://github.com/Cryptolens/cryptolens-c](https://github.com/Cryptolens/cryptolens-c)
Below are build instructions. We recommend to use our [C++ SDK](/libraries/cpp) when possible, since the it is has support for more methods.
## Build instructions
### Visual Studio
Build the project in `vsprojects/Cryptolens` with some platform and configuration (e.g. `Win32` and `Debug`). This will create the `Cryptolens.lib` file in `vsprojects/Cryptolens/Cryptolens/$(Configuration)` or `vsprojects/Cryptolens/Cryptolens/x64/$(Configuration)` for platforms `Win32` and `x64`, respectively.
The `Cryptolens.lib` file can then be used in another project, together with the include files in the `include/` directory. The `examples/CryptolensExamples` project is setup to build the example file `examples/example_activate_external.c` in this way.
Thus, to build the example project, first build the library in `vsprojects/Cryptolens` and then build the example project in `examples/CryptolensExamples`.
### CMake
There is a `CMakeLists.txt` in the root of the repository which can be used to build the library. The examples can be built using the `CMakeLists.txt` in `examples/`.
The following commands, when ran from the root of the repository, builds the examples:
```
mkdir build
cd build
cmake ../examples
make
```
### **GCC/Clang**
The example file can also be build manually with GCC or Clang using the following command (run from the root of the repository):
```
gcc -Iinclude/ -Ithird_party/cJSON/ src/cryptolens.c src/data_object.c src/decode_base64.c src/error.c src/machine_code_computer_static.c src/request_handler_curl.c src/response_parser_cJSON.c src/signature_verifier_openssl.c third_party/cJSON/cJSON.c third_party/openbsd/base64.c third_party/openbsd/strlcpy.c -lcrypto -lssl -lcurl examples/example_activate.c
```
# C++
Source: https://help.cryptolens.io/libraries/cpp
Overview of the Cryptolens Client API for C++
On this page, we have outlined several examples of how to get started with the Cryptolens Client API for C++.
**Note**, Cryptolens Client API for C++ currently supports a subset of the full Cryptolens Web API. Please contact us if you need something in particular.
You can find the API documentation here: [https://help.cryptolens.io/api/cpp/](https://help.cryptolens.io/api/cpp/).
If you are already familiar with the .NET version of the library, we have summarized key differences in an [announcement](https://cryptolens.io/2017/08/new-client-api-for-c/) on our blog.
# Table of contents
* [Example projects](#example-projects)
* [CMake](#cmake) (for Linux)
* [Visual Studio](#visual-studio) (for Windows)
* [Library overview](#library-overview)
* [Error handling](#error-handling)
* [Offline activation](#offline-activation)
* [HTTPS requests outside the library](#https-requests-outside-the-library)
# Example projects
[This repository](https://github.com/Cryptolens/cryptolens-cpp) contains some example projects using the library in the `examples/` directory. The directory contains examples for Unix and Windows. The Unix example projects use CMake as the build system and depend on OpenSSL and libcurl. The Windows example projects are built using Visual Studio and depend on WinHTTP and CryptoAPI. The rest of this section contains instructions for how to build the example projects.
## CMake
First we need to install libcurl and OpenSSL, including the header files, unless that has
already been done.
```shellscript theme={null}
Debian/Ubuntu:
$ apt-get install libcurl4-openssl-dev libssl-dev
Fedora/Red Hat:
$ yum install libcurl-devel openssl-devel
```
Next, clone the repository and build the examples
```shellscript theme={null}
$ git clone https://github.com/Cryptolens/cryptolens-cpp.git
$ cd cryptolens-cpp/examples/unix/cmake
$ mkdir build
$ cd build
$ cmake ..
$ make -j8
$ ./example_activate
```
## Visual Studio
Getting started with the example project for Visual Studio requires two steps. First we
build the library file the example project will statically link against, then we build
the example project.
The following steps build the library:
* Open the solution file *vsprojects/Cryptolens.sln* in Visual Studio.
* Set platform and configuration as appropriate, e.g. *x64* and *Debug*
* Build the project in Visual Studio
* Now the folder *vsprojects/Output/Platform/Configuration/* contains the *Cryptolens.lib* file. With platform set to "x64" and configuration set to "Debug", the file is *vsprojects/Output/x64/Debug/Cryptolens.lib*
Now we can build the example project:
* Open *examples/VisualStudio/VisualStudio.sln*
* Set configuration and platform in the same way as when building the library
* Build and run the project.
Instructions for how to add the library to your own Visual Studio project can be found [here](/tutorials/add-to-project-windows.md).
# Library overview
This section contains an overview of the standard way to implement the library in an
application. The first step is to include the appropriate headers:
```cpp theme={null}
#include
#include
#include
#include
namespace cryptolens = ::cryptolens_io::v20190401;
```
Besides including headers the above code sets up a namespace alias for the api version of the C++
library we are using.
The `Configuration` class allows for using different libraries for parsing JSON, making HTTPS
requests, performing cryptographic operations as well as minor changes in the behaviour of the
library. We currently support the following `Configurations` and `MachineCodeComputers`:
| Configuration | Description |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Configuration_Unix_IgnoreExpires` | Suggested default configuration for Unix-like systems. Use s ArduinoJson 5, libcurl and OpenSSL. Does not check if the license key has expired against the system time. |
| `Configuration_Unix_CheckExpires` | Same as `Configuration_Unix`, but additionally checks if the license key has expired against the system time. |
| `Configuration_Windows_IgnoreExpires` | Suggested default configuration for Windows based systems. Uses ArduinoJson 5, WinHTTP and CryptoAPI. Does not check if the license key has expired against the system time. |
| `Configuration_Windows_CheckExpires` | Same as `Configuration_Windows`, but additionally checks if the license key has expired against the system time. |
| MachineCodeComputer | Description |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `MachineCodeComputer_COM` | Works on Windows and computes a machine code using functionallity provided through COM. |
| `MachineCodeComputer_static` | Does not automatically compute a machine code, instead the machine code is set by calling `set_machine_code()`. |
| `MachineCodeComputer_SystemdDBusInodes_SHA256` | Works on Linux systems and computes a machine code based on information provided by Systemd, DBus and the filesystem. |
The next step is to create and set up a handle class responsible for making requests
to the Cryptolens Web API.
```cpp theme={null}
using Cryptolens = cryptolens::basic_Cryptolens
>;
cryptolens::Error e;
Cryptolens cryptolens_handle(e);
cryptolens_handle.signature_verifier.set_modulus_base64(e, "ABCDEFGHI1234");
cryptolens_handle.signature_verifier.set_exponent_base64(e, "ABCD");
// This line is only for MachineCodeComputer_static and sets the machine code to a static value
cryptolens_handle.machine_code_computer.set_machine_code(e, "289jf2afs3");
```
Here the strings `"ABCDEFGHI1234"` and `"ABCD"` needs to be replaced by your public keys. These
can be found when logged in on Cryptolens.io from the menu in the top-right corner
("Hello username!") and then *Security Settings*. The example above corresponds to
the following value on the website
```
ABCDEFGHI1234ABCD
```
In this example we set the machine code used to `"289jf2afs3"`.
Now that the handle class has been set up, we can attempt to activate a license key
```cpp theme={null}
cryptolens::optional license_key =
cryptolens_handle.activate
( e, // Object used for reporting if an error occured
"WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=", // Cryptolens Access Token
3646, // Product id
"MPDWY-PQAOW-FKSCH-SGAAU" // License Key
);
if (e) {
// An error occured. Handle it.
handle_error(e);
return 1;
}
```
The `activate` method takes several arguments:
1. The first argument is used to indicate if an error occured, and if so can provide additional
information. For more details see the section *Error handling* below.
2. We need an access token with the `Activate` scope. Access tokens can be created at
[https://app.cryptolens.io/User/AccessToken/](https://app.cryptolens.io/User/AccessToken/).
3. The third argument is the product id, this can be found at the page for the corresponding product at
[https://app.cryptolens.io/Product](https://app.cryptolens.io/Product).
4. The fourth argument is a string containing the license key string, in most cases this will be
input by the user of the application in some application dependent fashion.
After the `activate` call we check if an error occurred by converting `e` to bool. If an error
occured this returns true. If an error occurs, the optional containing the `LicenseKey` object
will be empty, and dereferencing it will result in undefined behaviour.
If no error occurs we can inspect the `LicenseKey` object for information as follows
```cpp theme={null}
if (license_key->check()->has_expired(1234567)) {
// Above, the value 1234567 represents the current time as a unix timestamp in seconds
std::cout << "Your subscription has expired." << std::endl;
return 1;
}
if (license_key->check()->has_feature(1)) { std::cout << "Welcome! Pro version enabled!" << std::endl; }
else { std::cout << "Welcome!" << std::endl; }
```
# Error Handling
This section explains how the Cryptolens C++ library handles errors. The library adopts an exceptionless design, using return values with optionals to handle cases where a value might be absent. Many functions accept a reference to a `cryptolens::basic_Error` object as their first argument, which is used to report errors and provide detailed error information.
## Error Classes
The library defines two key classes for error handling:
* `cryptolens::basic_Error`: An abstract base class that defines the interface for error handling. It specifies the required methods for error reporting.
* `cryptolens::Error`: A concrete implementation of the `cryptolens::basic_Error` interface. This is the error class used in our examples and by most users of the library.
This design allows users to create custom error classes by subclassing `cryptolens::basic_Error` instead of using the provided `cryptolens::Error`. Details on subclassing `cryptolens::basic_Error` are provided in the section [Custom error class](#custom-error-class) below.
## Interface Overview
The `cryptolens::basic_Error` interface provides detailed information about errors through several methods. Errors are categorized by subsystem (e.g., API, networking, or cryptographic operations), allowing precise identification of the error’s origin.
The error information consists of three components:
* **Subsystem**: Identifies the subsystem (e.g., API, networking, cryptography) where the error occurred, accessible via the `get_subsystem()` method.
* **Reason**: Describes the specific cause of the error within the subsystem, accessible via the `get_reason()` method.
* **Extra**: Provides additional details, such as error codes from underlying libraries (e.g., OpenSSL, WinHTTP), accessible via the `get_extra()` method.
> Note: Dividing errors into subsystems ensures that error codes from different libraries (e.g., HTTP or cryptographic libraries) do not conflict, enabling clear and unambiguous error reporting.
To check if an error has occured, the error class allows an implicit conversion to a boolean. This allows for checking if an error has occured using a standard if-statement. For example:
```cpp theme={null}
cryptolens::Error e;
f(e);
if (e) {
// Error has occured. Handle it!
}
```
Additionally, the `get_call()` method indicates which method call triggered the error. How this method can be used is described in more detail below in section [Behaviour When Error Has Occured](#behaviour-when-error-has-occured).
The following table summarizes the key methods of the `cryptolens::basic_Error` interface:
| **Method** | **Description** |
| ----------------- | ----------------------------------------------------------------- |
| `get_subsystem()` | Returns the subsystem where the error occurred. |
| `get_reason()` | Returns the specific reason for the error. |
| `get_extra()` | Returns additional details, such as library-specific error codes. |
| `get_call()` | Returns the method call that triggered the error. |
## Behavior When Error Has Occured
If an error occurs during a method call, the `cryptolens::basic_Error` object is updated to reflect the error. In this case, subsequent calls to methods that accept a reference to a `cryptolens::basic_Error` object then become no-ops and return default values. This design simplifies error handling by allowing users to defer error checks until after multiple method calls, rather than checking after each method call.
For example:
```cpp theme={null}
cryptolens::Error e;
f1(e);
f2(e);
if (e) {
// Handle error from f1 or f2
}
```
To identify which method caused the error, the `get_call()` method can be used.
## Error codes
### Subsystem overview
Errors from the library are divided into multiple subsystems to allow identifying the origin of an error. As described above, this information is available through the `get_subsystem()` method on the error class.
Constants indicating in which subsystem an error occured are available in the namespace `::cryptolens_io::v20190401::errors::Subsystem` and are included by the file `cryptolens/core.hpp`.
The possible values are as follows:
| **Subsystem** | **Numeric value** | **Description** |
| ------------------------------ | ----------------- | ---------------------------------------------------------------------------------------------- |
| `Subsystem::Ok` | 0 | Indicates that no error has occured. |
| `Subsystem::Main` | 1 | Indicates that a request was made to the server and the server returned an error. |
| `Subsystem::Json` | 2 | Indicates that an error occured when dealing with JSON. |
| `Subssytem::Base64` | 3 | Indicates that an error occured when dealing with Base64 encoded data. |
| `Subsystem:: RequestHandler` | 4 | Indicates that an error occured when connecting to the server. |
| `Subsystem::SignatureVerifier` | 5 | Indicates that an error occured when checking cryptographic signatures in the server response. |
### Subsystems
**Main subssystem**
The main subsystem consists of errors where the request was made successfully to the server and the server returned an error.
The main subsystem uses the reason field for communicating more information about what went wrong, but does not use the extra field.
Constants for the possible values for the reason field are available in the namespace `::cryptolens_io::v20190401::errors::Main` and are included by the file `cryptolens/core.hpp`.
The possible values for the reason field are as follows:
| **Reason** | **Numeric value** | **Description** |
| ------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Main::UNKNOWN_SERVER_REPLY` | 1 | General error that indicates that the reply from the server was not in format expected by the library. This error should not happen in general but can occur if there is e.g. a proxy that alters the response from the server. |
| `Main::INVALID_ACCESS_TOKEN` | 2 | Indicates that the access token used is not a valid access token for this user. |
| `Main::ACCESS_DENIED` | 3 | Indicates that the the access token used does not have permission to perform the requested operation. |
| `Main::INCORRECT_INPUT_PARAMETER` | 4 | Indicates that a parameter was incorrect. |
| `Main::PRODUCT_NOT_FOUND` | 5 | Indicates that the product parameter was incorrect. |
| `Main::KEY_NOT_FOUND` | 6 | Indicates that the key could not be found. |
| `Main::KEY_BLOCKED` | 7 | Indicates that the operation could not be performed because the key was blocked. |
| `Main::DEVICE_LIMIT_REACHED` | 8 | Indicates that the operation could not be performed because the maximum number of activated machines was reached. |
| `Main::KEY_EXPIRED` | 9 | Indicates that the operation could not be performed because the key has expired. |
| `Main::MACHINE_CODE_NOT_ACTIVATED_OR_NO_KEY_ACTIVATION` | 10 | Indicates that XXX |
**Json subsystem**
Indicates that an error occured when processing a Json value. This subsystem does currently not provide more information about the exact reason for why the error occured.
**Base64 subsystem**
Indicates that an error occured when decodring a Base64 value. This subsystem does currently not provide more information about the exact reason for why the error occured.
**RequestHandler subsystem**
Indicates that en error occured when making a network connection to the server.
This subsystem uses the reason value to provide a more detailed location in the code where the error occured, to aid debugging, and uses the extra field to propagate the error from the underlying library used for making network connections.
*Curl*
The value of the extra field is a CURLcode error code. The possible values, what causes them and what can be done to resolve the problem is available in [Curls documentation](https://curl.se/libcurl/c/libcurl-errors.html) and also mirrored in [our documentation](/libraries/cpp/libcurl-errors).
*WinHTTP*
The value of the extra field is a debug system error code, and the full list of possible values can be found in Microsoft's documentation about [Debug system error codes](https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes).
In most cases the code will be from WinHTTP itself, and those values are described in Microsoft's documentation about [Error Messages (Winhttp.h)](https://learn.microsoft.com/en-us/windows/win32/winhttp/error-messages).
**SignatureVerifier subsystem**
This subsystem uses the reason value to indicate where the error occured. Sometimes additional information is provided in the extra field.
Note we recommend using these values only for debugging purpuses, and do not recommend giving access to them in an automated way where e.g. another computer program can read the value. The values may reveal information about precisely where a cryptographic operation failed, which may be useful for circumventing the cryptographic protections.
# Offline activation
One way to support activation while offline is to initially make one activation request
while connected to the internet and then saving this response. By then reading the saved
response and performing the cryptographic checks it is not necessary to make another
request to the Web API in the future. Thus we can proceed as during online activation
but save the response as a string:
```cpp theme={null}
cryptolens::Error e;
Cryptolens cryptolens_handle(e);
cryptolens_handle.signature_verifier.set_modulus_base64(e, "ABCDEFGHI1234");
cryptolens_handle.signature_verifier.set_exponent_base64(e, "ABCD");
cryptolens::optional license_key =
cryptolens_handle.activate
( e, // Object used for reporting if an error occured
"WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=", // Cryptolens Access Token
3646, // Product id
"MPDWY-PQAOW-FKSCH-SGAAU" // License Key
);
if (e) { handle_error(e); return 1; }
std::string s = license_key->to_string();
```
The string `s` can now be saved to a file or similar, in an application dependent manner. In order
to check the license later when offline, load the string `s` and recover the license key as follows:
```cpp theme={null}
cryptolens::Error e;
Cryptolens cryptolens_handle(e);
cryptolens_handle.signature_verifier.set_modulus_base64(e, "ABCDEFGHI1234");
cryptolens_handle.signature_verifier.set_exponent_base64(e, "ABCD");
cryptolens::optional license_key =
cryptolens_handle.make_license_key(e, s);
if (e) { handle_error(e); return 1; }
```
A full working version of the code above can be found as *example\_offline.cpp* among the examples.
# HTTPS requests outside the library
In some cases it may be needlessly complex to have the Cryptolens library be responsible
for initiating the HTTPS request to the Web API, instead it might be easier to have some
other part of the application that does the HTTPS request, and then only use this library
for making sure that the cryptographic signatures are valid. This can be accomplished
by using the `handle_activate()` function as follows
```cpp theme={null}
std::string web_api_response; // Some other part of the code creates and populates this object
cryptolens::Error e;
Cryptolens cryptolens_handle(e);
cryptolens_handle.signature_verifier.set_modulus_base64(e, "ABCDEFGHI1234");
cryptolens_handle.signature_verifier.set_exponent_base64(e, "ABCD");
cryptolens::optional license_key =
cryptolens_handle.make_license_key(e, web_api_response);
if (e) { handle_error(e); return 1; }
```
This code is also applicable to the case where one wants a completly air-gapped offline activation.
In this case the `web_api_response` string would be prepared and delivered one for example an USB
thumb drive, and the application then reads this response from the device and stores it in
the `web_api_response` string.
# .NET
Source: https://help.cryptolens.io/libraries/dotnet
The .NET SDK can be used both for applications targeting .NET Framework and newer versions of .NET (.NET Core, .NET Standard and more recent version). Most of the examples on our documentation page include both C# and VB.NET, as well as ways you can install the library. If you would like to access the source code, you can view it on the following link: [https://github.com/Cryptolens/cryptolens-dotnet](https://github.com/Cryptolens/cryptolens-dotnet)
We have included general guides below that are also available on the link above.
Documentation is available on this page: [https://help.cryptolens.io/api/dotnet/api/index.html](https://help.cryptolens.io/api/dotnet/api/index.html)
We recently moved to a new documentation system, and this article is still draft. If you have any questions, please reach out to support.
## Getting started
### Install Nuget package
In Visual Studio package manager
```powershell theme={null}
PM> Install-Package Cryptolens.Licensing
```
Using dotnet CLI
```shellscript theme={null}
dotnet add package Cryptolens.Licensing
```
**If you are targeting Mac, Linux or Unity/Mono, we recommend to use the cross platform version of that package.**
In Visual Studio package manager
```shellscript theme={null}
PM> Install-Package Cryptolens.Licensing.CrossPlatform
```
Using dotnet CLI
```shellscript theme={null}
dotnet add package Cryptolens.Licensing.CrossPlatform
```
**Note:** If you plan to use the client with Mono (eg. in Unity) or on Linux/Mac, you need to use the cross platform version of the library, [Cryptolens.Licensing.CrossPlatform](https://www.nuget.org/packages/Cryptolens.Licensing.CrossPlatform/), which is available on NuGet. You can also download the pre-compiled binaries [here](https://github.com/Cryptolens/cryptolens-dotnet/releases).
# Go
Source: https://help.cryptolens.io/libraries/go
The documentation of the Go client SDK
**Note**, currently only the **activate** method from the Web API is supported.
In order to get started with the library, start by
```shellscript theme={null}
go get github.com/Cryptolens/cryptolens-golang/cryptolens
```
Now we can use the library in our code. A working example of the following can be found in the `examples/example_activate` directory (the repo link: [https://github.com/Cryptolens/cryptolens-golang/tree/go\_mod](https://github.com/Cryptolens/cryptolens-golang/tree/go_mod))
We start by importing the library with:
```go theme={null}
import "github.com/Cryptolens/cryptolens-golang/cryptolens"
```
Now we can activate a license key as follows:
```go theme={null}
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("License key activation failed!")
return
}
```
In order to use the code above with your account on [cryptolens.io](http://cryptolens.io) you need to change the constants as follows:
1. The `token` need to be changed to a valid access token for your account. Access tokens can be created at [https://app.cryptolens.io/User/AccessToken/](https://app.cryptolens.io/User/AccessToken/). In order to be able to use the `KeyActivate()` function the token needs to have the `Activate` scope.
2. The correct value for `publicKey` for your account can be found when logged in on [Cryptolens.io](http://Cryptolens.io) from the menu in the top-right corner ("Hello !") and then *Security Settings*. Copy paste the value from the *Public key* field.
3. The `ProductId` can be found at the page for the corresponding product at [https://app.cryptolens.io/Product](https://app.cryptolens.io/Product).
4. The `Key` is the license key string, and would in most cases be entered by the user of the application in some application dependent manner.
5. The `MachineCode` is an optional argument allowing you to provide an identifier for which device the application is running on, or something with a similar purpuse.
Finally, additional properties of the license key can be checked if desired:
```go theme={null}
fmt.Printf("License key for product with id: %d\n", licenseKey.ProductId)
if time.Now().After(licenseKey.Expires) {
fmt.Println("License key has expired")
return
}
if licenseKey.F1 {
fmt.Println("Welcome! Pro version enabled!")
} else {
fmt.Println("Welcome!")
}
```
## Examples
```go Key verification theme={null}
package main
import (
"fmt"
"github.com/Cryptolens/cryptolens-golang/cryptolens"
"time"
)
func main() {
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("License key activation failed!")
return
}
fmt.Printf("License key for product with id: %d\n", licenseKey.ProductId)
if time.Now().After(licenseKey.Expires) {
fmt.Println("License key has expired")
return
}
if licenseKey.F1 {
fmt.Println("Welcome! Pro version enabled!")
} else {
fmt.Println("Welcome!")
}
}
```
```go Offline verification theme={null}
package main
import (
"errors"
"fmt"
"github.com/Cryptolens/cryptolens-golang/cryptolens"
"io/ioutil"
"time"
)
func ActivateAndSaveLicenseKey() (string, error) {
token := "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0="
publicKey := "khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"
licenseKey, err := cryptolens.KeyActivate(token, cryptolens.KeyActivateArguments{
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
})
if err != nil || !licenseKey.HasValidSignature(publicKey) {
return "", errors.New("Initial license key activation failed")
}
serialized, err := licenseKey.ToBytes()
if err != nil {
return "", err
}
f, err := ioutil.TempFile("", "cryptolens_example_offline_")
if err != nil {
return "", err
}
defer f.Close()
_, err = f.Write(serialized)
if err != nil {
return "", err
}
return f.Name(), nil
}
func main() {
filename, err := ActivateAndSaveLicenseKey()
if err != nil {
fmt.Println("Failed to activate or save license key")
return
}
fmt.Printf("License key saved to file %s\n\n", filename)
savedKeyBytes, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println("Failed to read saved license key")
return
}
licenseKey, err := cryptolens.KeyFromBytes(savedKeyBytes)
if err != nil || !licenseKey.HasValidSignature(publicKey) {
fmt.Println("Error in saved license key")
return
}
fmt.Printf("License key sucessfully loaded from file!\n")
if time.Now().After(licenseKey.Expires) {
fmt.Println("License key has expired")
return
}
if licenseKey.F1 {
fmt.Println("Welcome! Pro version enabled!")
} else {
fmt.Println("Welcome!")
}
}
```
# Cryptolens Licensing Client SDK Reference
Source: https://help.cryptolens.io/libraries/index
A reference of client SDKs available in multiple languages.
## Introduction
Cryptolens offers client SDKs for most programming languages and frameworks. All of the SDKs are freely available on our [GitHub page](https://github.com/Cryptolens/), distributed as open-source libraries.
A client SDK is **not** necessary to use Cryptolens. You can always call the Web API on your own. However, since our client SDKs offer an interface to the Web API methods, it's usually more convinient to use a client SDK instead of calling the Web API on your own.
For example, all SDKs offer a method to activate a license and many offer a way to generate a machine code.
Note: The use of most of the methods requires an active subscription.
There are currently five versions of the API. Our aim is to ensure that they work on all platforms, both on desktops on any OS as well as on embedded systems.
* [.NET version](/libraries/dotnet)
* [C++ version](/libraries/cpp)
* [Java version](/libraries/java)
* [Python version](/libraries/python)
* [PHP version](/libraries/php)
* [Golang version](/libraries/go)
* [NodeJS version](/libraries/nodejs)
* [C version](https://help.cryptolens.io/libraries/c)
# Java
Source: https://help.cryptolens.io/libraries/java
Examples of how to use Cryptolens Java SDK
The source code is available at [https://github.com/Cryptolens/cryptolens-java](https://github.com/Cryptolens/cryptolens-java) and Javadoc can be found here: [https://help.cryptolens.io/api/java/](https://help.cryptolens.io/api/java/)
## Adding the library
This library can be installed using Maven Central, for more information, please refer to [this page](https://central.sonatype.com/artifact/io.cryptolens/cryptolens/). If you do not want to use Maven, you can use the jar file attached to the latest [release](https://github.com/Cryptolens/cryptolens-java/releases). If you use the jar file, you need to install gson manually.
If you plan to run the library on other platforms, it is possible to use the library without`oshi-core`. If you choose to skip the `oshi-core` dependency, `GetMachineCode` and `IsOnRightMachine` need to be called with the version parameter set to 3. For example, `Helpers.GetMachineCode(3)` or `Helpers.IsOnRightMachine(license, 3)`. If your application will run on an Android device, we recommend to use a different way to obtain the machine code, which is described [here](https://help.cryptolens.io/getting-started/ios-android).
## Example
### Key verification
The following example is similar to what is covered in the [key verification tutorial](https://help.cryptolens.io/examples/key-verification).
Assuming you have referenced the `cryptolens.jar` file, the code below should generate successful result. A working project with the code below can be found in the [example-app](https://github.com/Cryptolens/cryptolens-java/tree/master/example-app) folder.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public class Main {
public static void main(String[] args) {
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = "WyIyNjA5IiwiaWE5b0VFT3Q2eDlNR2FvbHBHK2VOYUZ4bzNjT3h5UkNrMCtiYnhPRSJd";
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL", Helpers.GetMachineCode(2)));
if (license == null || !Helpers.IsOnRightMachine(license, 2)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
}
}
```
In order to adjust this code to your products, several parameters needs to be changed, which we outline below:
* `RSAPubKey` - the RSA Public key, which can be found on [this](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys) page.
* `auth` - the access token (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
* `product_id` - the id of the product can be found on the product page (in the example above, it's 3646).
* `key` - the license key to be verified (above it's MPDWY-PQAOW-FKSCH-SGAAU).
* `machine_code` - the unique id of the device, which may require root access. Note, this value is not the same as the one generated by the .NET client.
**Note**: The code above assumes that node-locking is enabled. By default, license keys created with Maximum Number of Machines set to zero, which deactivates node-locking. As a result, machines will not be registered and the call to Helpers.IsOnRightMachine(license) will return False. You can read more about this behaviour [here](https://help.cryptolens.io/faq#maximum-number-of-machines). For testing purposes, please feel free to remove Helpers.IsOnRightMachine(license) from the if statement.
### Offline activation (saving/loading licenses)
Assuming the license key verification was successful, we can save the result in a file so that we can use it instead of contacting Cryptolens.
```java theme={null}
String licenseString = license.SaveAsString();
```
When loading it back, we can use the code below:
```java theme={null}
LicenseKey newLicense = LicenseKey.LoadFromString(RSAPubKey, licenseString);
```
If you want to make sure that the license file is not too old, you can specify the maximum number of days as shown below (after 30 days, this method will return null).
```java theme={null}
LicenseKey newLicense = LicenseKey.LoadFromString(RSAPubKey, licenseString, 30);
```
**Note:** `LicenseKey.LoadFromString` does not check the *ProductId*. In case you have multiple products, we recommend that you check that the *ProductId* corresponds to the product where the user tries to use the license file.
### Floating licenses
[Floating licenses](https://help.cryptolens.io/licensing-models/floating) can be enabled by passing a floatingTimeInterval to the `ActivateModel`. Optionally, you can also allow customers to exceed the bound by specifying the maxOverdraft.
The code below has a floatingTimeInterval of 300 seconds and maxOverdraft set to 1. To support floating licenses with overdraft, the call to `Helpers.IsOnRightMachine(license, true, true)` needs two boolean flags to be set to true.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = "WyIyNjA5IiwiaWE5b0VFT3Q2eDlNR2FvbHBHK2VOYUZ4bzNjT3h5UkNrMCtiYnhPRSJd";
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2), 300, 1));
if (license == null || !Helpers.IsOnRightMachine(license, 2, true, true)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
}
```
### Deactivation
In order to deactivate a license, we can call the `Key.Deactivate` method, as shown below. Note, we need an access token with Deactivate permission, so the ones used in earlier examples cannot be used (unless they have this permission).
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String auth = "";
boolean result = Key.Deactivate(auth, new DeactivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2)));
if (result == true) {
System.out.println("Deactivation successful.");
} else {
System.out.println("Deactivation failed.");
}
}
```
The call above is useful when [node-locking](https://help.cryptolens.io/licensing-models/node-locked) is used. If it's a floating license, deactivation is not necessary since licenses will be deactivated automatically after a certain period of time. However, to force a deactivation earlier, you can use similar code as above with addition of a boolean flag.
```java theme={null}
import io.cryptolens.methods.*;
import io.cryptolens.models.*;
public static void main(String args[]) {
String auth = "";
boolean result = Key.Deactivate(auth, new DeactivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(2), true));
if (result == true) {
System.out.println("Deactivation successful.");
} else {
System.out.println("Deactivation failed.");
}
}
```
### Calling through the license server
If you would like to re-route the requests through our [license-server](https://github.com/cryptolens/license-server) that is installed on the client site, you can specify its url using `LicenseServerUrl` parameter. All API models expose this parameter.
For example, let's suppose that your client runs the license server on `http://10.1.1.6:8080` and you want to call `Key.GetKey()`. In this case, we first define all parameters for the request and then modify the license server url:
```java theme={null}
GetKeyModel model = new GetKeyModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL");
model.LicenseServerUrl = "http://10.1.1.6:8080";
```
We do this because there is currently no overload method that accepts `LicenseServerUrl` parameter.
The entire code is shown below:
```java theme={null}
String RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTn+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB";
String auth = APIKey.get("getkeyactivate");
APIError error = new APIError();
GetKeyModel model = new GetKeyModel(3349, "ICVLD-VVSZR-ZTICT-YKGXL");
model.LicenseServerUrl = "http://10.1.1.6:8080";
LicenseKey license = Key.GetKey(auth, RSAPubKey, model , error);
if (license == null) {
System.out.println("The license does not work.");
System.out.println("Error: " + error.message);
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
# Node JS
Source: https://help.cryptolens.io/libraries/nodejs
This library contains helper methods to verify licenses in NodeJS. The repository is available at [https://github.com/Cryptolens/cryptolens-nodejs](https://github.com/Cryptolens/cryptolens-nodejs)
## Installation
```shellscript theme={null}
npm add cryptolens
```
## Example
### Key Verification
To verify a license key, you can use the code below. The RSAPublicKey, token and the product id can be found on [this page](https://help.cryptolens.io/examples/key-verification).
```javascript theme={null}
const key = require('cryptolens').Key;
const Helpers = require('cryptolens').Helpers;
var RSAPubKey = "Your RSA Public key, which can be found here: https://app.cryptolens.io/User/Security";
var result = key.Activate(token="Access token with with Activate permission", RSAPubKey, ProductId=3349, Key="GEBNC-WZZJD-VJIHG-GCMVD", MachineCode=Helpers.GetMachineCode());
result.then(function(license) {
// success
// Please see https://app.cryptolens.io/docs/api/v3/model/LicenseKey for a complete list of parameters.
console.log(license.Created);
}).catch(function(error) {
// in case of an error, an Error object is returned.
console.log(error.message);
});
```
### Offline activation (saving/loading licenses)
Assuming the license key verification was successful, we can save the result in a file so that we can use it instead of contacting Cryptolens.
First, we need to add the reference to the helper methods:
```javascript theme={null}
const Helpers = require('cryptolens').Helpers;
```
We can now proceed and save it as a string.
```javascript theme={null}
var licenseString = Helpers.SaveAsString(license);
```
When loading it back, we can use the code below:
```javascript theme={null}
var license = Helpers.LoadFromString(RSAPubKey, licenseString);
```
If you want to make sure that the license file is not too old, you can specify the maximum number of days as shown below (after 30 days, this method will return null).
```javascript theme={null}
var license = Helpers.LoadFromString(RSAPubKey, licenseString, 30);
```
# PHP
Source: https://help.cryptolens.io/libraries/php
PHP library for license key verification
The repository with all the code is available at [https://github.com/Cryptolens/cryptolens-php](https://github.com/Cryptolens/cryptolens-php)
## Code example
```php theme={null}
```
The code above uses our testing access token, product id, license key and machine code. In a real values for you can be obtained as follows:
* Access tokens can be created at [https://app.cryptolens.io/User/AccessToken](https://app.cryptolens.io/User/AccessToken) (remember to check 'Activate' and keep everything else unchanged)
* The product id is found by selecting the relevant product from the list of products ([https://app.cryptolens.io/Product](https://app.cryptolens.io/Product)), and then the product id is found above the list of keys.
* The license key would be obtained from the user in an application dependant way.
* Currently the PHP library does not compute the machine code, either the machine code can be computed by the application, or a dummy value can be used. In a future release the library itself will compute the machine code.
```php Cryptolens.php theme={null}
$token
, 'ProductId' => $product_id
, 'Key' => $key
, 'Sign' => 'True'
, 'MachineCode' => $machine_code
, 'SignMethod' => 1
, 'v' => 1
);
$postfields = '';
$first = TRUE;
foreach ($params as $i => $x) {
if ($first) { $first = FALSE; } else { $postfields .= '&'; }
$postfields .= urlencode($i);
$postfields .= '=';
$postfields .= urlencode($x);
}
unset($i, $x);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1
, CURLOPT_URL => 'https://app.cryptolens.io/api/key/Activate'
, CURLOPT_POST => 1
, CURLOPT_POSTFIELDS => $postfields
));
$result = curl_exec($curl);
curl_close($curl);
$resp = json_decode($result);
if ( is_null($resp)
|| !property_exists($resp, 'message')
|| $resp->{'message'} !== ''
|| !property_exists($resp, 'licenseKey')
|| !property_exists($resp, 'signature')
)
{
return FALSE;
}
$license_key_string = base64_decode($resp->{'licenseKey'});
if (!$license_key_string) { return FALSE; }
$license_key = json_decode($license_key_string);
if ( is_null($license_key)
|| !property_exists($license_key, 'ProductId')
|| !is_int($license_key->{'ProductId'})
|| !property_exists($license_key, 'Key')
|| !is_string($license_key->{'Key'})
|| !property_exists($license_key, 'Expires')
|| !is_int($license_key->{'Expires'})
|| !property_exists($license_key, 'ActivatedMachines')
//|| !is_iterable($license_key->{'ActivatedMachines'})
)
{
return FALSE;
}
if ( $license_key->{'ProductId'} !== $product_id
|| $license_key->{'Key'} !== $key
)
{
return FALSE;
}
$machine_found = FALSE;
foreach ($license_key->{'ActivatedMachines'} as $machine) {
if (!property_exists($machine, 'Mid'))
{
return FALSE;
}
if ($machine->{'Mid'} == $machine_code) { $machine_found = TRUE; }
}
unset($machine);
if (!$machine_found) { return FALSE; }
$time = time();
if ($license_key->{'Expires'} < $time) { return FALSE; }
return TRUE;
}
?>
```
# Python
Source: https://help.cryptolens.io/libraries/python
This library contains helper methods to verify license keys in Python.
Python docs can be found here: [https://help.cryptolens.io/api/python/](https://help.cryptolens.io/api/python/)
**Autodesk Maya**: The Python2 version needs to be used, as described [here](https://cryptolens.io/2019/07/autodesk-maya-plugin-software-licensing/).
**Autodesk Revit / Iron Python 2.7.3**: The Python2 version needs to be used with `HelperMethods.ironpython2730_legacy = True`.
> Please check out our guide about common errors and how to solve them: [https://help.cryptolens.io/faq/index#troubleshooting-api-errors](https://help.cryptolens.io/faq/index#troubleshooting-api-errors). For Python specific errors, please review [this section](#possible-errors).
## Installation
### Python 3
```shellscript theme={null}
pip install licensing
```
### Python 2
Please copy `cryptolens_python2.py` file into your project folder. The entire library is contained in that file.
> In the examples below, please disregard the imports and use only the following one:
```python theme={null}
from cryptolens_python2 import *
```
If you create a plugin for Autodesk Revit or use IronPython 2.7.3 or earlier, please also add the line below right after the import:
```python theme={null}
HelperMethods.ironpython2730_legacy = True
```
## Example
### Key verification
The code below will work exactly as the one explained in the [key verification tutorial](https://help.cryptolens.io/examples/key-verification).
First, we need to add the namespaces:
In Python 3:
```python theme={null}
from licensing.models import *
from licensing.methods import Key, Helpers
```
In Python 2:
```python theme={null}
from cryptolens_python2 import *
```
Now we can perform the actual key verification:
```python theme={null}
RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB"
auth = "WyIyNTU1IiwiRjdZZTB4RmtuTVcrQlNqcSszbmFMMHB3aWFJTlBsWW1Mbm9raVFyRyJd=="
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code=Helpers.GetMachineCode(v=2))
if result[0] == None or not Helpers.IsOnRightMachine(result[0], v=2):
# an error occurred or the key is invalid or it cannot be activated
# (eg. the limit of activated devices was achieved)
print("The license does not work: {0}".format(result[1]))
else:
# everything went fine if we are here!
print("The license is valid!")
license_key = result[0]
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
* `RSAPubKey` - the RSA public key (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
* `token` - the access token (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
* `product_id` - the id of the product can be found on the product page.
* `key` - the license key to be verified
* `machine_code` - the unique id of the device.
**Note:** The code above assumes that node-locking is enabled. By default, license keys are created with *Maximum Number of Machines* set to zero, which deactivates node-locking. As a result, machines will not be registered and the call to `Helpers.IsOnRightMachine(result[0])` will return `False`. You can read more about this behaviour [here](https://help.cryptolens.io/faq/index#maximum-number-of-machines).
### Offline activation (saving/loading licenses)
Assuming the license key verification was successful, we can save the result in a file so that we can use it instead of contacting Cryptolens.
```python theme={null}
# res is obtained from the code above
if result[0] != None:
# saving license file to disk
with open('licensefile.skm', 'w') as f:
f.write(result[0].save_as_string())
```
When loading it back, we can use the code below:
```python theme={null}
# read license file from file
with open('licensefile.skm', 'r') as f:
license_key = LicenseKey.load_from_string(pubKey, f.read())
if license_key == None or not Helpers.IsOnRightMachine(license_key, v=2):
print("NOTE: This license file does not belong to this machine.")
else:
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
If you want to make sure that the license file is not too old, you can specify the maximum number of days as shown below (after 30 days, this method will return NoneType).
```python theme={null}
# read license file from file
with open('licensefile.skm', 'r') as f:
license_key = LicenseKey.load_from_string(pubKey, f.read(), 30)
if license_key == None or not Helpers.IsOnRightMachine(license_key, v=2):
print("NOTE: This license file does not belong to this machine.")
else:
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
### Floating licenses
[Floating licenses](https://help.cryptolens.io/licensing-models/floating) can be enabled by setting the floatingTimeInterval. Optionally, you can also allow customers to exceed the bound by specifying the maxOverdraft.
The code below has a floatingTimeInterval of 300 seconds and maxOverdraft set to 1. To support floating licenses with overdraft, the call to `Helpers.IsOnRightMachine(license, true, true)` needs two boolean flags to be set to true.
```python theme={null}
from licensing.models import *
from licensing.methods import Key, Helpers
RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB"
auth = "WyIyNTU1IiwiRjdZZTB4RmtuTVcrQlNqcSszbmFMMHB3aWFJTlBsWW1Mbm9raVFyRyJd=="
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code=Helpers.GetMachineCode(v=2),\
floating_time_interval=300,\
max_overdraft=1)
if result[0] == None or not Helpers.IsOnRightMachine(result[0], is_floating_license=True, allow_overdraft=True, v=2):
print("An error occurred: {0}".format(result[1]))
else:
print("Success")
license_key = result[0]
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
### Create Trial Key (verified trial)
#### Idea
A [trial key](https://help.cryptolens.io/examples/verified-trials) allows your users to evaluate some or all parts of your software for a limited period of time. The goal of trial keys is to set it up in such a way that you don’t need to manually create them, while still keeping everything secure.
In Cryptolens, all trial keys are bound to the device that requested them, which helps to prevent users from using the trial after reinstalling their device.
You can define which features should count as trial by [editing feature definitions](https://help.cryptolens.io/web-interface/feature-definitions) on the product page.
#### Implementation
The code below shows how to create trial key. If the trial key is successful, `trial_key[0]` will contain the license key string. We then need to call `Key.Activate` (as shown in the earlier examples) with the obtained license key to verify the license.
```python theme={null}
from licensing.models import *
from licensing.methods import Key, Helpers
trial_key = Key.create_trial_key("WyIzODQ0IiwiempTRWs4SnBKTTArYUh3WkwyZ0VwQkVyeTlUVkRWK2ZTOS8wcTBmaCJd", 3941, Helpers.GetMachineCode(v=2))
if trial_key[0] == None:
print("An error occurred: {0}".format(trial_key[1]))
RSAPubKey = "sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==AQAB"
auth = "WyIyNTU1IiwiRjdZZTB4RmtuTVcrQlNqcSszbmFMMHB3aWFJTlBsWW1Mbm9raVFyRyJd=="
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key=trial_key[0],\
machine_code=Helpers.GetMachineCode(v=2))
if result[0] == None or not Helpers.IsOnRightMachine(result[0], v=2):
print("An error occurred: {0}".format(result[1]))
else:
print("Success")
license_key = result[0]
print("Feature 1: " + str(license_key.f1))
print("License expires: " + str(license_key.expires))
```
### License server or custom endpoint
To forward requests to a local license server or a different API, you can set it using the `server_address` in HelperMethods, i.e.,
```python theme={null}
HelperMethods.server_address = "http://localhost:8080/api/";
```
It is important to include one */* in the end of the address as well as to attach the "api" suffix.
### Other settings
#### Proxy
If you have customers who want to use a proxy server, we recommend enabling the following setting before calling any other API method, such as Key.Activate.
```python theme={null}
HelperMethods.proxy_experimental = True
```
This will ensure that the underlying HTTP library (urllib) used by Cryptolens Python SDK will use the proxy configured on the OS level. The goal is to make this default behaviour in future versions of the library, once enough feedback is collected.
#### SSL verification
SSL verification can temporarily be disabled by adding the line below before any call to Key.Activate.
```python theme={null}
HelperMethods.verify_SSL = False
```
The Cryptolens Python SDK will verify that the license information has not changed since it left the server using your RSA Public Key. However, we recommend to keep this value unchanged.
### Possible errors
#### The expiration date cannot be converted to a datetime object. Please try setting the period to a lower value.
This error occurs when the timestamp for the expiration date received from the server exceeds the limit in Python. This typically occurs when the **Period** is set to a excessively large value, often to prevent the license from expiring.
While Cryptolens requires a period (defaulted to 30) during the creation of a new license, this does not mark the license as time-limited. You can learn more about it [here](https://help.cryptolens.io/web-interface/keys-that-dont-expire). In essence, a license is treated as time-limited by either enforcing this in the Python code (e.g. if F1=true, the license is time-limited and so we check the expiration date against the current date, to see that it is still valid) or on the server side. On the server side, you can, for example, set up a feature that will automatically block expired licenses. You can read more about it [here](https://help.cryptolens.io/faq/index#blocking-expired-licenses).
In sum, to solve this issue, you can either follow one of the methods described above or set the period to a smaller value.
#### Could not contact the server. "Error message: \"
This error is thrown when the urllib library (a built in library in Python that we use to send HTTP requests) is unable to locate the CA files on the client machine. From our experience, this error occurs exclusively on Macs where the Python environment is incorrectly installed.
To solve this temporarily for **testing purposes**, you could temporary disable SSL verifications as described in [here](#ssl-verification), however, we do not recommend this in a production scenario. Instead, a better solution is to fix the underlying issue preventing the Python environment from finding the CA files.
This can be accomplished in at least two ways:
**Using certifi**
Before calling any of the API methods (e.g. Key.activate), you can add the following code:
```python theme={null}
import certifi
os.environ['SSL_CERT_FILE'] = certifi.where()
```
Please note that this requires `certifi` package to be installed.
**Running a script in the Python environment**
An alternative is to run script in their environment that should fix the issue. You can read more about it in this thread: [https://github.com/Cryptolens/cryptolens-python/issues/65](https://github.com/Cryptolens/cryptolens-python/issues/65)
**Summary**
The key takeaway is that it is better to address the issue with missing CA on the user side, since this issue will typically be user-specific. If that is not possible, you can use the code above to manually set the path to CA files. Although we have mentioned turning off SSL verification temporarily, it should not be used in production. `Key.activate` takes care of signature verification internally, but some other methods do not.
# Rust
Source: https://help.cryptolens.io/libraries/rust
Rust client SDK and examples
The Rust client SDK is currently in development, and its repository is available at [https://github.com/Cryptolens/cryptolens-rust](https://github.com/Cryptolens/cryptolens-rust). The source code of the library is available a the bottom of this page in the [lib.rs](http://lib.rs) file.
Below are some examples. They are available in the following repository as well [https://github.com/Cryptolens/cryptolens-rust-examples](https://github.com/Cryptolens/cryptolens-rust-examples).
```rust Activation example theme={null}
use cryptolens;
fn main() {
let license_key = cryptolens::KeyActivate(
"WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=",
cryptolens::KeyActivateArguments {
ProductId: 3646,
Key: "MPDWY-PQAOW-FKSCH-SGAAU",
MachineCode: "289jf2afs3",
.. Default::default()
}
).unwrap();
let public_key = r#"khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"#;
match license_key.has_valid_signature(public_key) {
Ok(true) => { }
_ => { println!("Signature check failed. Aborting!"); return; }
}
println!("Successfully activated license key: {}", license_key.Key.unwrap());
}
```
```rust Loading license from file (offline) theme={null}
use cryptolens;
fn main() {
let license_key = cryptolens::LicenseKey::from_str(
r#"{"licenseKey":"eyJQcm9kdWN0SWQiOjM2NDYsIklEIjo0LCJLZXkiOiJNUERXWS1QUUFPVy1GS1NDSC1TR0FBVSIsIkNyZWF0ZWQiOjE0OTAzMTM2MDAsIkV4cGlyZXMiOjE0OTI5MDU2MDAsIlBlcmlvZCI6MzAsIkYxIjpmYWxzZSwiRjIiOmZhbHNlLCJGMyI6ZmFsc2UsIkY0IjpmYWxzZSwiRjUiOmZhbHNlLCJGNiI6ZmFsc2UsIkY3IjpmYWxzZSwiRjgiOmZhbHNlLCJOb3RlcyI6bnVsbCwiQmxvY2siOmZhbHNlLCJHbG9iYWxJZCI6MzE4NzYsIkN1c3RvbWVyIjpudWxsLCJBY3RpdmF0ZWRNYWNoaW5lcyI6W3siTWlkIjoiIiwiSVAiOiIxNTUuNC4xMzQuMjciLCJUaW1lIjoxNDkxODk4OTE4fSx7Ik1pZCI6ImxvbCIsIklQIjoiMTU1LjQuMTM0LjI3IiwiVGltZSI6MTQ5MTg5ODk5NX0seyJNaWQiOiIyODlqZjJhZnNmIiwiSVAiOiIxNTUuNC4xMzQuMjciLCJUaW1lIjoxNDkxOTAwNTQ2fSx7Ik1pZCI6IjI4OWpmMmFmczMiLCJJUCI6IjE1NS40LjEzNC4yNyIsIlRpbWUiOjE0OTE5MDA2MzZ9XSwiVHJpYWxBY3RpdmF0aW9uIjpmYWxzZSwiTWF4Tm9PZk1hY2hpbmVzIjoxMCwiQWxsb3dlZE1hY2hpbmVzIjoiIiwiRGF0YU9iamVjdHMiOltdLCJTaWduRGF0ZSI6MTQ5NTAxOTc2Nn0=","signature":"SqPm8dtTdVBrXrmJzXer7qq6dvdQfctJxP8mar+RO9p8QABsgWWaX+uH7aOGMBd42eg+2Omorv7Ks6V7itRhXPeeq5qWoKuefd+pTsFagvqiu2N/E2Np8fpt51aqmiygdHLECo42nJwVD8JzlN67hnvJTgY7iyDWhG7qFK9Slk+kEJjjK/0J1pJYI6nOi+7sgBV7ZRca+7DmiP6OmOjNfySps6PdiB7QbiSis5f24Xmc5OYyRe3fzZmAueqF3eymBK19XhYFroWXeT4tcNsBNJsv+YfItovGbJysLx+K4ppltd2GNwEFQgtE3ILGOUj7EVbeQmQXg9m2c5MTPyk8iA==","result":0,"message":""}"#
).unwrap();
let public_key = r#"khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==AQAB"#;
match license_key.has_valid_signature(public_key) {
Ok(true) => { }
_ => { println!("Signature check failed. Aborting!"); return; }
}
println!("Successfully activated license key: {}", license_key.Key.unwrap());
}
```
```rust lib.rs expandable theme={null}
use std::result::Result;
use serde::{Deserialize, Serialize};
//use serde_json::Result;
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct ActivateResponse {
result: i64,
message: Option,
licenseKey: Option,
signature: Option,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct SerdeLicenseKey {
ProductId: u64,
Id: Option,
Key: Option,
Created: u64,
Expires: u64,
Period: u64,
F1: bool,
F2: bool,
F3: bool,
F4: bool,
F5: bool,
F6: bool,
F7: bool,
F8: bool,
Notes: Option,
Block: bool,
GlobalId: Option,
Customer: Option,
ActivatedMachines: Vec,
TrialActivation: bool,
MaxNoOfMachines: Option,
AllowedMachines: String,
DataObjects: Vec,
SignDate: u64,
}
#[allow(non_snake_case)]
pub struct LicenseKey {
pub ProductId: u64,
pub Id: Option,
pub Key: Option,
pub Created: u64,
pub Expires: u64,
pub Period: u64,
pub F1: bool,
pub F2: bool,
pub F3: bool,
pub F4: bool,
pub F5: bool,
pub F6: bool,
pub F7: bool,
pub F8: bool,
pub Notes: Option,
pub Block: bool,
pub GlobalId: Option,
pub Customer: Option,
pub ActivatedMachines: Vec,
pub TrialActivation: bool,
pub MaxNoOfMachines: Option,
pub AllowedMachines: Vec,
pub DataObjects: Vec,
pub SignDate: u64,
license_key_bytes: Vec,
signature_bytes: Vec,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Customer {
pub Id: u64,
pub Name: String,
pub Email: String,
pub CompanyName: String,
pub Created: u64,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct ActivationData {
pub Mid: String,
pub IP: String,
pub Time: u64,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct DataObject {
pub Id: u64,
pub Name: String,
pub StringValue: String,
pub IntValue: u64,
}
#[allow(non_snake_case)]
pub struct KeyActivateArguments<'a, 'b> {
pub ProductId: u64,
pub Key: &'a str,
pub MachineCode: &'b str,
pub FieldsToReturn: u64,
pub FloatingTimeInterval: u64,
pub MaxOverdraft: u64,
}
impl Default for KeyActivateArguments<'static, 'static> {
fn default() -> Self {
KeyActivateArguments {
ProductId: 0,
Key: "",
MachineCode: "",
FieldsToReturn: 0,
FloatingTimeInterval: 0,
MaxOverdraft: 0,
}
}
}
impl LicenseKey {
pub fn from_str(s: &str) -> Result {
let activate_response: ActivateResponse = serde_json::from_str(&s).map_err(|_| ())?;
// TODO: Check result and parse message in case there is an error
let license_key: &str = activate_response.licenseKey.as_ref().ok_or(())?;
let signature: &str = activate_response.signature.as_ref().ok_or(())?;
let license_key_bytes = base64::decode(license_key).map_err(|_| ())?;
let license_key_string = String::from_utf8(license_key_bytes.clone()).map_err(|_| ())?;
let signature_bytes = base64::decode(signature).map_err(|_| ())?;
let serde_lk: SerdeLicenseKey = serde_json::from_str(&license_key_string).map_err(|_| ())?;
Ok(LicenseKey {
ProductId: serde_lk.ProductId,
Id: serde_lk.Id,
Key: serde_lk.Key,
Created: serde_lk.Created,
Expires: serde_lk.Expires,
Period: serde_lk.Period,
F1: serde_lk.F1,
F2: serde_lk.F2,
F3: serde_lk.F3,
F4: serde_lk.F4,
F5: serde_lk.F5,
F6: serde_lk.F6,
F7: serde_lk.F7,
F8: serde_lk.F8,
Notes: serde_lk.Notes,
Block: serde_lk.Block,
GlobalId: serde_lk.GlobalId,
Customer: serde_lk.Customer,
ActivatedMachines: serde_lk.ActivatedMachines,
TrialActivation: serde_lk.TrialActivation,
MaxNoOfMachines: serde_lk.MaxNoOfMachines,
AllowedMachines: serde_lk.AllowedMachines.split('\n').map(|x| x.to_string()).collect(),
DataObjects: serde_lk.DataObjects,
SignDate: serde_lk.SignDate,
license_key_bytes: license_key_bytes,
signature_bytes: signature_bytes,
})
}
}
#[allow(non_snake_case)]
pub fn KeyActivate(token: &str, args: KeyActivateArguments<'_, '_>) -> Result {
let product_id = args.ProductId.to_string();
let fields_to_return = args.FieldsToReturn.to_string();
let floating_time_interval = args.FloatingTimeInterval.to_string();
let max_overdraft = args.MaxOverdraft.to_string();
let params = [
("token", token),
("ProductId", &product_id),
("Key", args.Key),
("MachineCode", args.MachineCode),
("FieldsToReturn", &fields_to_return),
("FloatingTimeInterval", &floating_time_interval),
("MaxOverdraft", &max_overdraft),
("Sign", "true"),
("SignMethod", "1"),
("v", "1"),
];
let client = reqwest::Client::new();
let mut res = client.post("https://app.cryptolens.io/api/key/Activate")
.form(¶ms)
.send()
.map_err(|_| ())?;
let s = res.text().map_err(|_| ())?;
LicenseKey::from_str(&s)
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct RSAKeyValue {
Modulus: String,
Exponent: String,
}
impl LicenseKey {
pub fn has_valid_signature(&self, public_key: &str) -> Result {
let public_key: RSAKeyValue = serde_xml_rs::from_str(public_key).map_err(|_| ())?;
let modulus_bytes = base64::decode(&public_key.Modulus).map_err(|_| ())?;
let exponent_bytes = base64::decode(&public_key.Exponent).map_err(|_| ())?;
let modulus = openssl::bn::BigNum::from_slice(&modulus_bytes).map_err(|_| ())?;
let exponent = openssl::bn::BigNum::from_slice(&exponent_bytes).map_err(|_| ())?;
let keypair = openssl::rsa::Rsa::from_public_components(modulus, exponent).map_err(|_| ())?;
let keypair = openssl::pkey::PKey::from_rsa(keypair).map_err(|_| ())?;
let mut verifier = openssl::sign::Verifier::new(openssl::hash::MessageDigest::sha256(), &keypair).map_err(|_| ())?;
verifier.update(&self.license_key_bytes).map_err(|_| ())?;
verifier.verify(&self.signature_bytes).map_err(|_| ())
}
}
```
# Introduction to Web API
Source: https://help.cryptolens.io/libraries/web-api
Introduction to Cryptolens Web API
## What is Web API?
We are working on a new version of this article.
Web API can be thought of as a channel that can be used to talk to Cryptolens from external applications. It contains a wide range of methods that will respond different information depending on input parameters. For example, these methods allow you to activate keys, generate new ones, get list of activated machines, and so much more. It’s always possible to talk to the Web API directly, however, you will most likely use an existing API that simplifies that task. If you use an environment that is not supported at the moment, it is always possible to use the Web API right away, which makes Cryptolens a platform independent system.
## Next
* [Web API Reference](https://app.cryptolens.io/docs/api/v3)
* [Cryptolens Client APIs](https://help.cryptolens.io/web-api/skm-client-api)
# Containerized environments
Source: https://help.cryptolens.io/licensing-models/containers
Explains how to license applications running in containerized environments (for example, Docker).
## Idea
Most of the licensing models can be applied to applications running in a containerized environments such as Docker. If you would like to enforce a restriction on the number of instances that can run the application, we recommend to choose the [floating model](https://help.cryptolens.io/licensing-models/floating) and set the **MachineCode** to a random value (computed whenever the application starts) when calling Key.Activate. The random value should be stored temporarily inside the application and never saved to disk.
This is done since containers can easily be copied and moved across different machines, making it difficult to identify individual containers.
**Note:** When clients do not have internet access, our [license server](https://github.com/Cryptolens/license-server#floating-licenses-offline) can be used instead.
## Implementation
An quick way to obtain a random identifier is to use the GUID. A method to obtain it is available in most languages. We have summarized several examples below:
```c# C# theme={null}
var machineCode = Guid.NewGuid();
```
```python Python theme={null}
import uuid
machine_code = uuid.uuid4().hex
```
# Floating licenses
Source: https://help.cryptolens.io/licensing-models/floating
An overview of how floating licenses can be implemented in Cryptolens
## Idea
Using [node-locking](https://help.cryptolens.io/licensing-models/node-locked), we saw a way to “lock” a license to a limited number of machines. A problem with this set up is if your customers plans to change computers often, in which case it can turn out to be inconvenient to keep activate and deactivate machines.
Instead, we can use the notion of floating licenses, where we ensure that a license can be used on a certain number of machines at a time.
Here are some examples:
* you have developed a desktop app that your customers can install unlimited number of times, with the condition that they use it on 1 device at a time.
* a company has 100 employees, but you only want to ensure that 20 of those can use your software simultaneously.
* a company has 10 devices that have the software installed, but only 2 of them will use it concurrently. Sometimes, all of the 10 devices have to be used simultaneously and you want to permit that (aka overdraft license).
* your application will run in a container that will created and removed frequently, and you want to ensure that your clients can only run 10 containers simultaneously.
**Note:** This tutorial focuses on how floating licenses can be implemented when there is a connection to the internet. If some of your clients will be offline, they can install our license server instead. More information is available [here](https://github.com/Cryptolens/license-server#floating-licenses-offline).
## Implementation
### In the dashboard
As with [node-locked](/licensing-models/node-locked) licenses, floating licenses are enforced by setting **maximum number of machines** to a value greater than 0.
### In your application
We can reuse almost the entire code snippet from the [key verification](/examples/key-verification) tutorial, with the only difference in the last parameter, which we have to add. The `IsOnRightMachine` check is similar to the one in the [node-locking](/licensing-models/node-locked) example, only differing in `isFloatingLicense`, which is set to true.
#### Floating without overdraft
The code bellow allows at most the **maximum number of machines** to use the software concurrently.
```c# C# theme={null}
var auth = "Access token with permission to access the activate method";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
Key = licenseKey,
ProductId = 3349,
Sign = true,
MachineCode = Helpers.GetMachineCode(),
FloatingTimeInterval = 100 // <- we have added this parameter.
});
// from node-locking example
if(Helpers.IsOnRightMachine(result.LicenseKey, isFloatingLicense: true)))
{
// everything is ok
}
else
{
// an error occurred
}
```
```VB.NET VB.NET theme={null}
Dim auth = "Access token with permission to access the activate method"
Dim result = Key.Activate(token:=auth, parameters:=New ActivateModel() With {
.Key = licenseKey,
.ProductId = 3349,
.Sign = True,
.MachineCode = Helpers.GetMachineCode(),
.FloatingTimeInterval = 100 ' <- we have added this parameter.
})
' from node-locking example
If Helpers.IsOnRightMachine(result.LicenseKey, isFloatingLicense:= true) Then
' everything is ok
Else
' an error occurred
End If
```
```java Java theme={null}
String RSAPubKey = "{Your RSA Public key}";
String auth = "Access token with permission to access the activate method";
// note that the new parameter set to 100.
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(), 100));
// from node-locking example
if (license == null || !Helpers.IsOnRightMachine(license, true)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
```python Python theme={null}
RSAPubKey = "{enter the RSA Public key here}"
auth = "{access token with permission to access the activate method}"
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code=Helpers.GetMachineCode(),\
floating_time_interval=100)
if result[0] == None or not Helpers.IsOnRightMachine(result[0], is_floating_license= True):
# an error occurred or the key is invalid or it cannot be activated
# (eg. the limit of activated devices was achieved)
print("The license does not work: {0}".format(result[1]))
else:
# everything went fine if we are here!
print("The license is valid!")
```
#### Floating with overdraft
The code bellow allows at most the **maximum number of machines** + 1 to use the software concurrently.
```c# C# theme={null}
var auth = "Access token with permission to access the activate method";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
Key = licenseKey,
ProductId = 3349,
Sign = true,
MachineCode = Helpers.GetMachineCode(),
FloatingTimeInterval = 100, // <- we have added this parameter.
MaxOverdraft = 1 // <- we can exceed the max number of machines by one.
});
// from node-locking example
if(Helpers.IsOnRightMachine(result.LicenseKey, isFloatingLicense: true, allowOverdraft: true)))
{
// everything is ok
}
else
{
// an error occurred
}
```
```VB.NET VB.NET theme={null}
Dim auth = "Access token with permission to access the activate method"
Dim result = Key.Activate(token:=auth, parameters:=New ActivateModel() With {
.Key = licenseKey,
.ProductId = 3349,
.Sign = True,
.MachineCode = Helpers.GetMachineCode()
.FloatingTimeInterval = 100, ' <- we have added this parameter.
.MaxOverdraft = 1 ' <- we can exceed the max number of machines by one.
})
' from node-locking example
If Helpers.IsOnRightMachine(result.LicenseKey, isFloatingLicense:= true, allowOverdraft:= true) Then
' everything is ok
Else
' an error occurred
End If
```
```java Java theme={null}
String RSAPubKey = "{Your RSA Public key}";
String auth = "Access token with permission to access the activate method";
// note the two new parameter, one for floating time interval (i.e. 100) and one telling how much we can exceed the max number of machines (i.e. by 1).
LicenseKey license = Key.Activate(auth, RSAPubKey, new ActivateModel(3349, "MTMPW-VZERP-JZVNZ-SCPZM", Helpers.GetMachineCode(), 100, 1));
// from node-locking example (NB: we need to provide an additional true flag in Helpers.IsOnRightMachine to support overdraft)
if (license == null || !Helpers.IsOnRightMachine(license, true, true)) {
System.out.println("The license does not work.");
} else {
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
```python Python theme={null}
RSAPubKey = "{enter the RSA Public key here}"
auth = "{access token with permission to access the activate method}"
result = Key.activate(token=auth,\
rsa_pub_key=RSAPubKey,\
product_id=3349, \
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code=Helpers.GetMachineCode(),\
floating_time_interval=100,\
max_overdraft = 1)
if result[0] == None or not Helpers.IsOnRightMachine(result[0], is_floating_license= True, allow_overdraft=True):
# an error occurred or the key is invalid or it cannot be activated
# (eg. the limit of activated devices was achieved)
print("The license does not work: {0}".format(result[1]))
else:
# everything went fine if we are here!
print("The license is valid!")
```
#### Notes about the code
> **FloatingTimeInterval** specifies how long back in history of activations we should go, in order to decide whether a machine is still using the software or not (specified in seconds). **MaxOverdraft** specifies how many more devices than specified in **maximum number of machines** can use the software concurrently.
**Note**, in comparison to node-locking, where key verification for most applications can occur once during startup, floating licensing requires continuous key verifications. The smaller FloatingTimeInterval, the more key verifications have to occur.
> As a rule of thumb, if you plan to verify a license every 10 minutes, the **FloatingTimeInterval** should be no less than 600 (600s = 10min) if you want to ensure that your customers never exceed the **maximum number of machines**. However, if you can allow the customers to exceed this boundary in eg. 5 minutes, you could reduce **FloatingTimeInterval** to 300 (300s = 5min).
### Tips
#### Releasing a floating license
Normally, floating licenses will automatically be released in a certain period of time (specified by `FloatingTimeInterval`). However, you can manually release a floating license by using [Key.Deactivate](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html?#SKM_V3_Methods_Key_Deactivate_System_String_SKM_V3_Models_DeactivateModel_) with `Floating=True`, as shown below:
```c# C# theme={null}
var auth = "Access token with permission to access the deactivate method";
Key.Deactivate(activateToken, new DeactivateModel {
Key = "GEBNC-WZZJD-VJIHG-GCMVD",
ProductId = 3349,
MachineCode = Helpers.GetMachineCode(),
Floating = true // <- add this
});
```
```python Python theme={null}
result = Key.deactivate(token="Access token with permission to access the deactivate method",\
product_id=3349,\
key="ICVLD-VVSZR-ZTICT-YKGXL",\
machine_code = Helpers.GetMachineCode(),\
floating=True)
```
#### Number of used and free licenses
To get the number of floating licenses left (among other things), you can simply add `Metadata=true`. A helper method, [GetFloatingLicenseInformation](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Helpers.html#SKM_V3_Methods_Helpers_GetFloatingLicenseInformation_SKM_V3_Models_ActivateModel_SKM_V3_Models_KeyInfoResult_) can be used to extract this information.
```c# theme={null}
// we have just separated the initialization of the input parameters for activate
// into a separate variable, since we will need it later on.
var activateModel = new ActivateModel()
{
Key = licenseKey,
ProductId = 3349,
Sign = true,
MachineCode = Helpers.GetMachineCode(),
FloatingTimeInterval = 100, // <- we have added this parameter.
MaxOverdraft = 1, // <- we can exceed the max number of machines by one.
Metadata = true
}
var auth = "Access token with permission to access the activate method";
var result = Key.Activate(token: auth, parameters: activateModel);
// some code here
if (result != null && result.Result == ResultType.Success)
{
var info = Helpers.GetFloatingLicenseInformation(activateModel, result);
Console.WriteLine(info.AvailableDevices);
Console.WriteLine(info.UsedDevices);
Console.WriteLine(info.OverdraftDevices);
}
```
# Introduction
Source: https://help.cryptolens.io/licensing-models/index
List of possible licensing models supported by Cryptolens.
| Licensing Model | Description | Implementation |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Try and Buy (perpetual) | This model enables you to securely distribute trial versions of your product, which your customer can easily upgrade. A trial license can either be time-limited (eg. 30 days) or not. When it's not time-limited, it can have a reduced set features (eg. lite version). Once the time limit is reached, it can either disable the entire application or just certain features. The user can later upgrade the same license key to be able to get a full-featured version of the application. | [Perpetual license tutorial](https://help.cryptolens.io/licensing-models/perpetual) |
| Subscription | A subscription is when you give access to your product for a limited period of time. Instead of offering your application as a 'product', you can offer it it as a 'service' (for example, your customers may get additional support together with the product during the subscription period). This model is good because it provides you with a recurring revenue stream. | [Subscription model tutorial](https://help.cryptolens.io/licensing-models/subscription) |
| Pay per use | > By supporting usage-based licenses, we can monetize a group of users that would otherwise not have purchased the product (eg. because it is too expensive). Usage-based licensing is when you charge for usage of specific features. For example, if you have an accounting software, you can charge per created yearly report. If you have a movie editing software, you can charge per created movie (or for each conversion to a different movie format). The point is to allow a larger group of people to be able to use your software. For example, let's return to the movie editing software. There can be two groups of users: those that will use the software a lot (eg. professional use), in which case they will prefer a subscription or a perpetual license. Another group can have movie editing as a hobby, in which case they may create very few movies, so a subscription may be to expensive. | [Usage based licensing tutorial](https://help.cryptolens.io/licensing-models/usage-based) |
| Floating / Concurrent | Floating licenses restrict the number of users that can use the app at the same time. For example, imagine you want your customers to be able to use your application on 10 devices at once. That is, they may have the software installed on 100 devices, but they can only use it on 10 of them at the same time. | [Floating licenses tutorial](https://help.cryptolens.io/licensing-models/floating). |
| SDK licensing | Software Development Kit (SDK) licensing is when you distribute a component, usually a library, that is later going to be integrated as a part of another commercial solution. | [SDK licensing tutorial](https://help.cryptolens.io/licensing-models/sdk-licensing). |
| User Account licensing | User account based licensing replaces license keys with username/password authentication (and optionally with 2FA). A secure protocol, open-source, protocol is used to authenticatate users in the app, with them having to expose their username/password to the app. You can read more about the protocol [here](https://cryptolens.io/2018/10/secure-user-account-authentication-inside-apps-for-software-licensing/). | [User account authentication tutorial](https://help.cryptolens.io/licensing-models/user-login-intro). |
# Node locked licenses
Source: https://help.cryptolens.io/licensing-models/node-locked
An overview of node locked licenses
## Idea
In order to ensure that a license key is not used on more machines than the user is entitled to, **machine code locking** (aka node-locked licenses) can be used.
Here are some example cases:
* your customer bought 1 license that should only work on two computers, their work and home computer
* all employees of your customer should have access to your software, as long as they are working on-site.
Once the machines are registered, the license will be locked to them. The customer will have to de-activate machines to be able to use them on a new device (if this is permitted). If you instead want your customers to seamlessly switch machines (as long as the quota is not reached), floating licenses may be better suited for this case.
## Implementation
### In the dashboard
Node-locking is enforced by setting [maximum number of machines](https://help.cryptolens.io/web-interface/maximum-number-of-machines) to a value greater than 0. This can be done when creating a new or by modifying an existing license. Once set, each time a [key verification](https://help.cryptolens.io/examples/key-verification) request is performed, Cryptolens will ensure that the number of machines that have activated the license does not exceed the value that you have specified.
### Code
In order to get node-locking to work, you can use the same code that was shown in the [key verification](https://help.cryptolens.io/examples/key-verification) tutorial. For additional security, we should add a check as shown below:
**In C#**
```c# C# theme={null}
if(Helpers.IsOnRightMachine(result.LicenseKey))
{
// everything is ok
}
else
{
// an error occurred
}
```
```VB.NET theme={null}
If Helpers.IsOnRightMachine(result.LicenseKey) Then
' everything is ok
Else
' an error occurred
End If
```
This code will work for the first example (remember to set **maximum number of machines** to **2** in your dashboard).
For the second example, we will need to modify the key verification code slightly. Instead of using `MachineCode = Helpers.GetMachineCode()`, we need another way of identifying unique machine codes. In this case, we need to ensure every device in the on-site generates the same machine code. This could, for example, be the network name, IP address, etc.
## Related articles
* [User based activations](https://help.cryptolens.io/licensing-models/user-based-activations) - activating on a per user user rather than per machine basis.
# Perpetual licensing (try/buy)
Source: https://help.cryptolens.io/licensing-models/perpetual
Explains how the simple one-time purchase model (perpetual) can be implemented in Cryptolens.
## Idea
This model enables you to securely distribute trial versions of your product, which your customer can easily upgrade.
A trial license can either be time-limited (eg. 30 days) or not. When it’s not time-limited, it can have a reduced set features (eg. lite version). Once the time limit is reached, it can either disable the entire application or just certain features.
The user can later upgrade the same license key to be able to get a full-featured version of the application.
## Implementation
To implement this model, there are just two concepts that are required: key generation (for trial- and full-featured licenses) and key verification (inside your application).
### Key generation
#### Trial keys
In order to allow your customers to try your software, trial keys can be used. Trial keys can be generated inside your application using [Key.CreateTrialKey](https://help.cryptolens.io/api/dotnet/api/SKM.V3.Methods.Key.html?q=create%20trial#SKM_V3_Methods_Key_CreateTrialKey_System_String_SKM_V3_Models_CreateTrialKeyModel_) method.
These keys will be locked to the machine that requested them, which ensures that the trial cannot be requested again. Once the trial has elapsed, users can keep the same key (they only need to upgrade it). We describe this below.
#### Upgrading trial
If your users liked the product, the next step is to **upgrade the license**\* key with the features they need (or extending the duration of the key if you have a SaaS model). You can **create an entirely new license** key also. We cover both of the options below:
Upgrading existing key
The simplest way of doing this is to take their existing license key and upgrade it. In this case, changes will reach the software instantly.
There are two ways to upgrade a license key. The fastest way is to use [Payment Forms](https://help.cryptolens.io/payment-form/index) built-in into Cryptolens. You can also use any other third party service or perform this operation manually using our dashboard. If you use Payment Forms, we can take advantage of the [custom field](https://help.cryptolens.io/payment-form/request), when passing a license key to the form. In this way, you can create a link inside your app which allows your users to upgrade a license.
When upgrading a license key, [key related methods](https://app.cryptolens.io/docs/api/v3/Key) in the Web API will be of great help, for example, `Add Feature` or `Extend License`.
Creating a new key
If you do not use the built-in trial keys, there is an option to create an entierely new license key (upon a successful payment). As we described in the previous sections, you can either use [Payment Forms](https://help.cryptolens.io/payment-form/index) built-in into Cryptolens or any other third party service.
[Create Key](https://app.cryptolens.io/docs/api/v3/CreateKey) method can be used to create new license keys using the Web API. Please take a look at the tutorial below:
* [Key generation tutorial](/examples/key-generation)
Note, to generate a trial key, a similar procedure can be performed (if you don’t want to create trial keys inside your application).
### Key validation
Assuming your customer has received a license key (either by requesting a trial inside the app or by any other means), we now want to verify it in the application. This is described in the tutorial below:
* [Key verification tutorial](/examples/key-verification)
The only change that has to be added is the distinction between trial- and full-featured licenses. In the `else` statement, you can add something similar to:
Note, we have assumed that **feature 1** stands means it’s a trial license. This is quite flexible and you can choose any configuration of features to your specific needs.
In C#:
```c# C# theme={null}
// ...
// everything went fine if we are here!
if (result.license.HasFeature(1).HasNotExpired().IsValid())
{
// feature 1 is trial, so we check if it's enabled, we check
// the expiration date
}
else if (result.license.HasNotFeature(1).IsValid())
{
// if feature 1 is not enabled, it is a full featured license.
}
else
{
// the license has expired.
}
```
```VB.NET VB.NET theme={null}
' ...
' everything went fine if we are here!
If result.license.HasFeature(1).HasNotExpired().IsValid() Then
' feature 1 is trial, so we check if it's enabled, we check
' the expiration date
Else If result.license.HasNotFeature(1).IsValid() Then
' if feature 1 is not enabled, it is a full featured license.
Else
' the license has expired.
End If
```
```java Java theme={null}
//...
//everything went fine if we are here!
if (license.HasFeature(1) && license.HasNotExpired())
{
// feature 1 is trial, so we check if it's enabled, we check
// the expiration date
}
else if (license.HasNotFeature(1))
{
// if feature 1 is not enabled, it is a full featured license.
}
else
{
// the license has expired.
}
```
# Pre-determined activations
Source: https://help.cryptolens.io/licensing-models/pre-determined-activations
Explains how to allow a pre-determined list of machines to use a license key.
## Idea
A common use case when licensing a software to a larger organization is to require pre-approval of the machines that will use a license key. This tutorial shows how to limit activations to a pre-determined list of machines.
## Implementation
The first step is to ask customers to send the list of all machines they want to activate to you. We provide CMD and PowerShell scripts that they can run using e.g. Microsoft SCCM: [https://github.com/Cryptolens/admin-tools/tree/master/manual-activation#powershell-script](https://github.com/Cryptolens/admin-tools/tree/master/manual-activation#powershell-script).
When you have received the list, you can use the following tool to activate them at the same time: [https://app.cryptolens.io/extensions/ActivateDevices](https://app.cryptolens.io/extensions/ActivateDevices).
To verify a license key, the idea is to use `Key.GetKey` instead of `Key.Activate`. When you call, `Key.GetKey`, you will receive the license key object that contains the list of all permitted machines. You can then use this list to see if the machine the client uses is included in that list and return an error message if it’s not in the list.
# SDK licensing
Source: https://help.cryptolens.io/licensing-models/sdk-licensing
Summary of how SDKs can be protected using Cryptolens Licensing.
## Idea
Software Development Kit (SDK) licensing is when you distribute a component, usually a library, that is later going to be integrated as a part of another commercial solution.
In many ways, licensing an SDK or a desktop software is quite similar. In both cases, you will have to send a license key to your customers in order for the software to work. Many of the licensing models for desktop apps can be used in SDKs, although there are some that will be more suitable for SDKs. However, we need to keep in mind that **end users** are almost always different individuals, and the information that is stored in a license key will be shared with all end users (see the [Privacy](https://help.cryptolens.io/licensing-models/sdk-licensing#privacy) section) unless configured otherwise.
## Implementation
There are two ways of protecting SDKs. Either we keep track of each new device that uses the SDK (which requires internet access) or we allow developers to have unlimited number of end users, as long as it is their own application that uses the SDK (does not require access to the internet).
The choice depends on both the platform and the licensing model you would like to support:
* **Track all devices** - this approach is better suited for SDKs where you would like to charge your customers for usage, eg. per install or unique device. This will work on all platforms. Each instance using the SDK will need to contact Cryptolens at least once.
* **Offline mode** - if you do not want to charge customers per device/install, this approach would suit better. There is no need to contact Cryptolens at any point. This approach works if there is a way to identify the application that calls the SDK. In this tutorial, we will cover how it can be done if your SDKs are targeting .NET (including Mono/Unity) or if it the SDK will work on mobile devices.
### Track all devices (all platforms)
If your SDK will have access to the internet, you can protect it in two ways: by registering every end user of the SDK or by counting each time it is installed.
As with desktop apps, we need to perform a [key verification](https://help.cryptolens.io/examples/key-verification) before the user of the SDK gains access to the methods. A license key field can be a part of the constructor, a method or a config file stored alongside the SDK. If you only want to contact Cryptolens only once, you can save the response from `Key.Activate` and use it instead of contacting Cryptolens. Please read more [here](https://help.cryptolens.io/examples/offline-verification).
#### Charge per end user
A useful way to ensure that you retain control of all end user instances is to enable [node locking](https://help.cryptolens.io/licensing-models/node-locked).
For example, you can have different pricing tiers for the number of end users your customers can have (eg. 1 end user for trials, 10,000 for standard and 1,000,000 for enterprise), or you can charge for each new 1000nd activation. In the first case, you can simply enter the upper bound in [maximum number of machines](https://help.cryptolens.io/web-interface/maximum-number-of-machines) field. In the second case (where you charge for each 1000nd activation), you can set some upper bound (eg. 10,000) as the [maximum number of machines](https://help.cryptolens.io/web-interface/maximum-number-of-machines), which you later increase as they start to reach this limit. For billing purposes, a script can be used to count the number of new activations since last time checked.
#### Charge per install
Instead of keeping track of each end user instance, we can instead count the number of installs and then charge for them. This can be done quite easily with data objects, which you [increment](https://app.cryptolens.io/docs/api/v3/IncrementIntValue) each time a new install occurs (and recording this on the end user machine to not count the same machine twice).
### Offline mode (track applications using the SDK)
If your SDK won’t have access to the internet, we can protect it by restricting which applications can call its methods, ensuring that only the applications that were developed by your customers can use the SDK. This is accomplished by storing the identifier of the application inside the license file, either inside the “Machine code” field or as a data object.
In this model, you can charge **per developer** or **per application**.
#### In .NET/Mono/Unity
When you use the offline mode approach to protect your SDK, end users are not required to contact Cryptolens. However, to get a signed certificate, developers using the SDK need to contact Cryptolens. The signing utility will send the machine code of the device that requests a certificate, which allows you to enforce [node-locking](https://help.cryptolens.io/licensing-models/node-locked). For example, you can limit how many devices can be used for signing that use the same license key.
* [Signing utility and docs](https://github.com/Cryptolens/sdk-licensing).
#### Other environments (mobile SDKs)
Depending on the platform, there will be different ways to retrieve the identifier of the application. If you have developed a mobile SDK, you can use the application/bundle id as a way of identifying the application. The distribution process will be similar to the one below:
1. To allow an application to use the SDK, you need to obtain the application id and add it as an “activated device” inside the license (for example, by calling Key.Activate or in the dashboard).
2. Next, send the license file to the customer. They need to place it so that your SDK will be able to read it upon initialization.
3. Whenever the SDK is called, it will verify that the license contains the application id of the calling application.
## Privacy
When implementing SDK licensing, you have to assume that information in the license key (eg. notes field, activated devices, customer info) will be shared amongst all end users. In most cases, they will not see this, but we always have to assume they can, as this is technically possible.
A [key verification](https://help.cryptolens.io/examples/key-verification) call translates to a call to [Activate](https://app.cryptolens.io/docs/api/v3/Activate). This method supports data masking using the **feature lock** field, available when creating a new **access token**. If you scroll down to [License Key](https://app.cryptolens.io/docs/api/v3/Activate#LicenseKey), you will see the fields that can be hidden. If possible, please hide all information unless there is field that you really need.
**Note**, all fields in a license key can be seen by all end users, unless data masking is used.
## Code example
We will illustrate how SDKs can be protected on a class that contains helper methods for various mathematical operations, `MathHelpers`. This class contains three methods that require different features to be true:
* `Abs` - requires F5 to be set to true
* `Factorial` - requires F6 to be set to true
* `Fibonacci` - requires F7 to be set to true
> With this setup, you can easily customize the permitted methods in the dashboard for each license key.
### Initialization
From the customer perspective, SDK usage will depend on if we use the **track all devices** approach or **offline mode**.
#### Track all devices approach
The customer (developer using the SDK in their approach) will need to supply a license key to unlock functionality.
```c# theme={null}
var math = new MathMethods("FULXY-NADQW-ZAMPX-PQHUT");
Console.WriteLine(math.Abs(5));
Console.WriteLine(math.Fibonacci(5));
```
#### Offline mode approach
Since the customer has already provided the license key when signing the assembly, it will not be required when initializing the class.
```c# theme={null}
var math = new MathMethods();
Console.WriteLine(math.Abs(5));
Console.WriteLine(math.Fibonacci(5));
```
Depending on the license key, some methods may or may not work.
### Under the hood
As can be seen in the previous section, the license check occurs in the constructor when the customer provides us with the license key. In the **track all devices** approach with end user tracking, the code for this is essentially the same as the one in the [key verification](https://help.cryptolens.io/examples/key-verification) tutorial. The only difference is that we have added some code to take into account the expiration date and the features.
The `permittedMethods` variable is a `Dictionary` and we use it to track what methods the customer has access to.
```c# theme={null}
// ...
// in case the license key verification was successful.
// this is a time-limited license, so check expiration date.
if (result.LicenseKey.F1 && !result.LicenseKey.HasNotExpired().IsValid())
return false;
// everything went fine if we are here!
permittedMethods = new Dictionary();
if (result.LicenseKey.F5)
{
permittedMethods.Add("Abs", true);
}
if (result.LicenseKey.F6)
{
permittedMethods.Add("Factorial", true);
}
if (result.LicenseKey.F7)
{
permittedMethods.Add("Fibonacci", true);
}
return true;
```
If the license key verification was successful, we just need to add an if-statement that checks this before the method is executed.
```c# theme={null}
///
/// Compute the absolute value of a number
///
public int Abs(int num)
{
if (!permittedMethods.ContainsKey("Abs"))
throw new ArgumentException("The use of this method is not permitted.");
if (num > 0)
return num;
return -num;
}
```
### Source code
#### Track all changes approach
```c# theme={null}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SKM.V3;
using SKM.V3.Methods;
using SKM.V3.Models;
namespace SDKExample
{
public class MathMethods
{
private Dictionary permittedMethods = new Dictionary();
public MathMethods(string licenseKey)
{
if (!licenseCheck(licenseKey))
{
// error occurred when verifying the license.
throw new ArgumentException("License verification failed.");
}
}
private bool licenseCheck(string licenseKey)
{
// From: https://help.cryptolens.io/examples/key-verification
var RSAPubKey = "{Your RSA Public key}";
var auth = "{access token with permission to access the activate method}";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
Key = licenseKey,
ProductId = 3941,
Sign = true,
MachineCode = Helpers.GetMachineCode()
});
if (result == null || result.Result == ResultType.Error ||
!result.LicenseKey.HasValidSignature(RSAPubKey).IsValid())
{
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
return false;
}
else
{
// this is a time-limited license, so check expiration date.
if (result.LicenseKey.F1 && !result.LicenseKey.HasNotExpired().IsValid())
return false;
// everything went fine if we are here!
permittedMethods = new Dictionary();
if (result.LicenseKey.F5)
{
permittedMethods.Add("Abs", true);
}
if (result.LicenseKey.F6)
{
permittedMethods.Add("Factorial", true);
}
if (result.LicenseKey.F7)
{
permittedMethods.Add("Fibonacci", true);
}
return true;
}
}
///
/// Compute the absolute value of a number
///
public int Abs(int num)
{
if (!permittedMethods.ContainsKey("Abs"))
throw new ArgumentException("The use of this method is not permitted.");
if (num > 0)
return num;
return -num;
}
///
/// Compute the factorial, eg. num!.
///
public int Factorial(int num)
{
if (!permittedMethods.ContainsKey("Factorial"))
throw new ArgumentException("The use of this method is not permitted.");
if (num == 0)
return 1;
return num * Factorial(num - 1);
}
///
/// Compute the nth fibonacci number.
///
public int Fibonacci(int num)
{
if (!permittedMethods.ContainsKey("Fibonacci"))
throw new ArgumentException("The use of this method is not permitted.");
return fibonacciHelper(num);
}
// we use a helper methods to avoid checking the permission on each iteration.
private int fibonacciHelper(int num)
{
if (num == 0 || num == 1)
return 1;
return fibonacciHelper(num - 1) + fibonacciHelper(num - 2);
}
}
}
```
#### Offline mode
```c# theme={null}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SDKExample
{
public class MathMethods
{
private Dictionary permittedMethods = new Dictionary();
public MathMethods()
{
if (!licenseCheck())
{
// error occurred when verifying the license.
throw new ArgumentException("License verification failed.");
}
}
private bool licenseCheck()
{
string RSA = "{enter the RSA Public key here}";
var license = SKM.V3.Methods.Helpers.VerifySDKLicenseCertificate(RSA);
if(license == null)
{
return false;
}
// everything went fine if we are here!
permittedMethods = new Dictionary();
if (license.F5)
{
permittedMethods.Add("Abs", true);
}
if (license.F6)
{
permittedMethods.Add("Factorial", true);
}
if (license.F7)
{
permittedMethods.Add("Fibonacci", true);
}
return true;
}
///
/// Compute the absolute value of a number
///
public int Abs(int num)
{
if (!permittedMethods.ContainsKey("Abs"))
throw new ArgumentException("The use of this method is not permitted.");
if (num > 0)
return num;
return -num;
}
///
/// Compute the factorial, eg. num!.
///
public int Factorial(int num)
{
if (!permittedMethods.ContainsKey("Factorial"))
throw new ArgumentException("The use of this method is not permitted.");
if (num == 0)
return 1;
return num * Factorial(num - 1);
}
///
/// Compute the nth fibonacci number.
///
public int Fibonacci(int num)
{
if (!permittedMethods.ContainsKey("Fibonacci"))
throw new ArgumentException("The use of this method is not permitted.");
return fibonacciHelper(num);
}
// we use a helper methods to avoid checking the permission on each iteration.
private int fibonacciHelper(int num)
{
if (num == 0 || num == 1)
return 1;
return fibonacciHelper(num - 1) + fibonacciHelper(num - 2);
}
}
}
```
# Site license
Source: https://help.cryptolens.io/licensing-models/site-license
Explains how to allow your clients to use a license on any number of machines within a physical location
## Idea
A site license is a way to license your application that does not involve tracking individual devices that are using a license key. Instead, it limits usage to a physical location or a domain, allowing any number of users to use the license within that location or domain.
This can be an appealing option for customers that do not want to manage individual devices and is particularly suited for larger companies.
A site license reduces the administrative overhead of managing employees who should have access and deactivation of old devices when employees leave.
## Implementation
If you have already implemented [node-locking](/licensing-models/node-locked) or [floating licenses](/licensing-models/floating) using our defaults, the license will be limited to a specific number of machines. You can verify this by looking at the value of `MachineCode`. If it’s set to Helpers.GetMachineCode() or similar, it means that the license will track machines, allowing you to enforce a limit on concurrent use.
To change to a site license, we need to change the value of `MachineCode` so that it identifies the entire location or domain where the customer will use your application.
If we can assume that all users are domain authenticated, the domain name could be passed on as the `MachineCode` during activation, as an example.
```c# theme={null}
string upn = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string domain = upn.Split('@')[1];
```
### Tracking machines for analytics purposes
With site licenses, a limit on individual machines/devices is no longer enforced and instead any number of machines within a network/site can use a license key. If you would still like to keep track of the machines that use the license, you could use the the `FriendlyName` field, which is a part of the `Key.Activate` call. This will allow you to monitor usage on a machine level from the API Logs.
### Limiting maximum number of machines
If you would still like to enforce a limit on the maximum number of concurrent machines (either using [node-locking](/licensing-models/node-locked) or [floating licensing](/licensing-models/floating)) as well as limit usage to a specific location/site, we can store the domain (e.g. Active directory or a similar way of identifying a site) in a data object associated with the license key. This way, only machines that run in a domain that was added to a license may proceed with activation.
License key verification can proceed as follows:
1. Call `Key.GetKey` to obtain the license key object.
2. Look up the data object that you have defined that contains the approved domain/site identifier. Compare it to the one that is computed on the client machine.
3. If the client machine uses an approved domain, proceed with activation by calling Key.Activate.
**Tip** To add or edit a data object associated with a license key, click on the license key on the product page and scroll down to *data objects* section. You will then see a link to *add/edit data objects*.
# Subscription licensing (SaaS)
Source: https://help.cryptolens.io/licensing-models/subscription
Explains how you can set up recurring payments for your application, aka SaaS model. This can also be used to keep track of potential support/maintenance subscriptions.
## Idea
Instead of selling your software as a [one-time purchase](/licensing-models/perpetual), you can instead collect monthly or yearly payments from your customers.
There are at least three ways to set up a licensing model that supports some form of recurring payments. You can require an active subscription to:
* Use the product (or some of its features)
* Get updates, but allow access to older versions of the product after the expiration date (similar to [one-time purchase model](/licensing-models/perpetual))
* Receive support, but still allow access to the product. You can combine this with the “updates constraint” above.
Both of these models depend on the `expiration date` property that each license key has. We will cover the details of how this is implemented in the next section.
## Implementation
### In the dashboard
The expiration date of a license key can be controlled by clicking on the desired license on the product page. A box will popup, similar to the one below.
By adding a certain amount of days to a license key, Cryptolens will automatically determine the expiration date, in order to ensure that the license key can be used for at least the number of days that was specified or longer (if the user has some days left before expiration).
**Note**, if the textbox in the picture above does not show up, a quick solution is to click on **Edit Feature Names** on the product page and tick **Treat all licenses as time-limited**. You can read more about feature definitions [here](https://help.cryptolens.io/web-interface/feature-definitions).
### In your application
In the introduction, we mentioned three ideas to subscription based licensing model can be used: by constraining access to the entire product, newer versions of it and/or support.
#### Require subscription to access the product
This is quite simple to restrict access to entire product if the license key has expired. In the code below, we assume that you have read the [key verification tutorial](/examples/key-verification). The only change is to add `HasNotExpired()` in the if-statement below:
```c# C# theme={null}
// notice that we have added ".HasNotExpired()".
if (result == null || result.Result == ResultType.Error ||
!result.LicenseKey.HasValidSignature(RSAPubKey)
.HasNotExpired().IsValid())
{
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
Console.WriteLine("The license does not work.");
}
else
{
// everything went fine if we are here!
Console.WriteLine("The license is valid!");
}
```
```VB.NET theme={null}
' notice that we have added ".HasNotExpired()".
If result Is Nothing OrElse result.Result = ResultType.[Error] OrElse
Not result.LicenseKey.HasValidSignature(RSAPubKey).HasNotExpired().IsValid Then
' an error occurred or the key is invalid or it cannot be activated
' (eg. the limit of activated devices was achieved)
Console.WriteLine("The license does not work.")
Else
' everything went fine if we are here!
Console.WriteLine("The license is valid!")
End If
```
```java Java theme={null}
// notice that we have added "HasExpired()".
if (license == null || !Helpers.IsOnRightMachine(license) || license.HasExpired()) {
// an error occurred or the key is invalid or it cannot be activated
// (eg. the limit of activated devices was achieved)
System.out.println("The license does not work.");
} else {
// everything went fine if we are here!
System.out.println("The license is valid!");
System.out.println("It will expire: " + license.Expires);
}
```
If you use a specific feature to denote time-limited licenses, you can similarly add `HasFeature()` in the if-statement.
#### Require subscription to access updates
In this scenario, instead of restricting access to all versions of the product, we allow users to keep using the latest major version before they stopped paying for updates.
In the image above, we assume major releases (eg. v1.0 and v2.0) are released three times a year. Minor releases (eg. v1.1 and v1.2) are released more frequently. Let us further assume that users pay on a monthly basis.
The idea is as follows: if the user stops extending their subscription before the end of May, they will only be able to use **v1.\***. If they stop in June, they will be able to use **v2.\***, and so for.
We can express this in the code below. It assumes you use the code-snippet from the [key verification tutorial](/examples/key-verification). The code below is put inside the else-statement.
```c# theme={null}
// ..
// everything went fine if we are here!
Console.WriteLine("The license is valid!");
// we need to add this extra code inside the else-statement
string currentApplicationVersion = "1";
if (license.HasNotExpired().IsValid())
{
// the SLA is still valid so we proceed as usual
}
else if (license.HasExpired().IsValid())
{
// if the SLA has expired, we want to check the notes field
// to see which version they bought the last time
if (license.notes >= currentApplicationVersion)
{
// proceed as usual
}
else
{
// close the application
}
}
```
Remember, in addition to extending the duration of the license key, we need update the notes field with the last major release number.
#### Require subscription to receive support
If you plan to only use subscriptions to keep track of who is eligible for support, no additional code is required besides what is presented in the [key verification tutorial](/examples/key-verification).
### Integration with other services
The key to get subscriptions up and running is the [ExtendLicense](https://app.cryptolens.io/docs/api/v3/ExtendLicense) method in the Web API. It allows you to extend the expiration date of a license key by a certain number of days. For example, let’s assume the users pays for the subscription on a monthly basis. Once a payment is successful, we can extend the duration of the license by sending a GET request to as shown below (the access token can be created on [this page](https://app.cryptolens.io/User/AccessToken#/)):
```
https://app.cryptolens.io/api/key/ExtendLicense?ProductId=123&Key=FRQHQ-FSOSD-BWOPU-KJOWF&NoOfDays=30&token=
```
Although we extending the duration of the license key by **30** days (for a monthly subscription), it is better to add a few extra days in case there are issues with the payment, in order to avoid disruptions.
#### Tips when integrating with Stripe
If you use subscriptions in Stripe, there are two useful events that you can use to monitor subscriptions (when using Webhooks):
* `invoice.payment_succeeded` - when this event is received, [ExtendLicense](https://app.cryptolens.io/docs/api/v3/ExtendLicense) method should be called for the license that the subscription belongs to.
* `customer.subscription.deleted` - when this event is received, [BlockKey](https://app.cryptolens.io/docs/api/v3/BlockKey) should be called to make sure the license key can no longer be used.
> An implementation that supports recurring payments will be released in the coming weeks. In meantime, please reach out to us should you have any questions!
# Subscription vs usage-based pricing
Source: https://help.cryptolens.io/licensing-models/subscription-vs-usage-based
This article focuses on when to choose subscriptions over usage-based licensing and vice versa.
## Introduction
We have covered both [subscription licensing](https://help.cryptolens.io/licensing-models/subscription) (*customers paying a flat fee on e.g. a monthly basis.*) and [usage-based licensing](https://help.cryptolens.io/licensing-models/usage-based) (*customers paying for the actual usage*) in the previous articles, and one question that may arise is which one to choose. We will share some of our insights on which model tends to be better depending on how you sell your application.
### When to choose Usage based pricing
As a rule of thumb it can be said that for usage-based pricing, there needs to be a strong correlation between the value that is created for a customer and the “unit” you charge for. For example, if your software helps photographers to edit photos, this is a clear example of where you could apply the usage-based model.
Another reason to consider implementing usage-based pricing is if you develop a tool where your customer is not the direct end user. SDK is one such example. In such cases, it’s easier to motivate usage-based pricing since each end user your customer sells to is presumably going to create value for them.
### When to choose Subscription pricing
If your customer is the direct end user of your software, then a flat subscription fee may be better. For example, if you offer a SaaS application, charging for each new teammate that your customer adds to their account might not be a good idea. That’s because it could encourage your client to keep the number of teammates down, since each time they plan to add a new teammate is a new “buy” decision for them. Smaller firms could potentially share login details to the same account, which is bad from a security standpoint. Moreover, it tends to be better to increase product adoption within the company.
### Summary
If the “unit” you want to charge extra for creates “direct” value to your client and if your client is not the final end-user of your product, then usage-based licensing may work better. In other cases, subscription-based licensing would be better.
It might also be a good idea to select a hybrid approach, where pricing includes a flat monthly fee and some usage is charged on top of that.
# Usage-based licensing (pay per use)
Source: https://help.cryptolens.io/licensing-models/usage-based
Explains how you can set up implement usage-based licensing / pay per use / pay per click
## Idea
Usage-based licensing is when you charge for usage of specific features. For example, if you have an accounting software, you can charge per created yearly report. If you have a movie editing software, you can charge per created movie (or for each conversion to a different movie format).
The point is to allow a larger group of people to be able to use your software. For example, let’s return to the movie editing software. There can be two groups of users: those that will use the software a lot (eg. professional use), in which case they will prefer a subscription or a perpetual license. Another group can have movie editing as a hobby, in which case they may create very few movies, so a subscription may be too expensive.
> By supporting usage-based licenses, we can monetize a group of users that would otherwise not have purchased the product (eg. because it is too expensive).
Example project can be found [here](https://github.com/Cryptolens/Examples/tree/master/usage-based/java).
**Note:** If some of your clients will be offline, the usage information can still be tracked using our license server, as described [here](https://github.com/Cryptolens/license-server#usage-based-licensing-offline).
## Implementation
We can implement usage based licensing using [data objects (aka custom variables)](https://app.cryptolens.io/docs/api/v3/Data). An advantage of using them is that they allow us to increment and decrement them atomically, which means that the counter will always reflect the actual usage of a specific feature.
We can implement this in two ways; the choice of which depends on if we want to bill users upfront (in advance) or in the end of the billing cycle (based on the actual usage).
* [**Paying based on actual usage**](https://help.cryptolens.io/licensing-models/usage-based#paying-based-on-actual-usage): When charging based on actual usage, one way is to set the data object to zero and keep incrementing it as a feature is being used. In the end of the month, we record the usage and either reset it or keep the value (if we keep it, we need to know it next month so that we don’t charge customers twice).
* [**Paying upfront**](https://help.cryptolens.io/licensing-models/usage-based#paying-upfront): If you want to bill users upfront, one strategy is to treat the value of a data object as a credit, i.e. users can buy a certain amount of credits, eg. 1000, which will allow them to use a specific feature 1000 times before they have to refill. That is, we will decrement this value each time the feature is used.
* [**Recurring credits as a part of a subscription**](https://help.cryptolens.io/licensing-models/usage-based#recurring-credits-as-a-part-of-a-subscription): If you already charge customers a subscription fee (e.g. monthly) and want to offer usage-credits as a part of the subscription that are re-filled periodically.
### Creating a data object
In order to associate a data object with a license key, you can use the code below (in C#):
```c# C# theme={null}
// note, if we ran Key Verification earlier, we can can set Key=result.LicenseKey.KeyString
var parameters = new AddDataObjectToKeyModel()
{
Name="usagecount" ProductId = 3941,
Key = "FRQHQ-FSOSD-BWOPU-KJOWF",
IntValue=0
};
var result = Data.AddDataObject("access token with AddDataObject permission (and keylock set to '-1' for improved security)", parameters);
```
```Java Java theme={null}
String auth = "access token with AddDataObject permission (and keylock set to '-1' for improved security)";
BasicResult addResult = Data.AddDataObject(auth,
new AddDataObjectToKeyModel(
3941,
"FRQHQ-FSOSD-BWOPU-KJOWF",
"usagecount",
0,
""));
if (!Helpers.IsSuccessful(addResult)) {
System.out.println("Could not add a new data object. Maybe the limit was reached?");
}
```
```bash Curl theme={null}
curl https://app.cryptolens.io/api/data/AddDataObjectToKey? \
-d token=access token with AddDataObject permission \
-d Name=usagecount \
-d ProductId=3941 \
-d Key=FRQHQ-FSOSD-BWOPU-KJOWF \
-d IntValue=0
```
If you are using [Payment Forms](https://help.cryptolens.io/payment-form/index), you can create a data object by sending a GET request our Web API (shown below). More information about the parameters can be found [here](https://app.cryptolens.io/docs/api/v3/AddDataObject).
```
https://app.cryptolens.io/api/data/AddDataObjectToKey?token=accesstoken&name=usagecount&ProductId=3941&Key=FRQHQ-FSOSD-BWOPU-KJOWF&IntValue=0
```
#### IntValue
If you plan to charge people upfront, you can set the `IntValue` to eg. 1000 and if you charge in the end of the billing cycle based on actual usage, you can set it to 0.
### Updating the usage counter
There are three ways to update the counter stored in `IntValue`: using `IncrementIntValue`, `DecrementIntValue` or `SetIntValue`. In your software, you should either use `IncrementIntValue` or `DecrementIntValue` (remember that you need to create a specific access token that permits use of one of the methods). In other words, you only want the client software to update the counter in one direction only. `SetIntValue` allows you to assign an arbitrary value, so it should only be used on the server side (where you have control).
Note that `IncrementIntValue` or `DecrementIntValue` methods allow you to specify an upper or lower bound, which is especially useful if you have a **pay upfront** model. For example, if you give your users a certain credit, eg. 1000, you can then specify a lower bound to be 0, so that once it is reached, they won’t be able to use that particular feature.
#### Paying based on actual usage
If we simply want to keep track of the number of times a feature was used (and bill our customers in the end of the month), we can use the following code:
```c# C# theme={null}
var auth = "Access token with AddDataObject, ListDataObject and IncrementIntValue permission. Please also set KeyLock value to '-1'";
var licenseKey = "LZKZU-MPJEW-TARNP-UHDBQ";
var result = Data.ListDataObjects(auth, new ListDataObjectsToKeyModel
{
Contains = "usagecount",
Key = licenseKey,
ProductId = 3349
});
var obj = result.DataObjects.Get("usagecount");
if (obj == null)
{
// make sure to create it in case it does not exist.
Data.AddDataObject(auth, new AddDataObjectToKeyModel { Key = licenseKey, ProductId = 3349, Name = "usagecount", IntValue = 1 });
if(res == null || res.Result == ResultType.Error)
{
Console.WriteLine("Could not create new data object. Terminate." + res.Message);
}
}
else
{
var res = obj.IncrementIntValue(auth, 1, licenseKey: new LicenseKey { Key = licenseKey, ProductId = 3349 });
if (res == false)
{
Console.WriteLine("We could not update the data object. Terminate.");
}
}
```
```java Java theme={null}
// note, if we ran Key Verification earlier, we can can set Key=license.KeyString
String auth = "Access token with AddDataObject, ListDataObject and IncrementIntValue permission. Please also set KeyLock value to '-1'";
ListOfDataObjectsResult listResult = Data.ListDataObjects(auth, new ListDataObjectsToKeyModel(3941, "FRQHQ-FSOSD-BWOPU-KJOWF", "usagecount"));
if (!Helpers.IsSuccessful(listResult)) {
System.out.println("Could not list the data objects.");
}
if (listResult.DataObjects.size() == 0) {
BasicResult addResult = Data.AddDataObject(auth,
new AddDataObjectToKeyModel(
3941,
"FRQHQ-FSOSD-BWOPU-KJOWF",
"usagecount",
0,
""));
if (!Helpers.IsSuccessful(addResult)) {
System.out.println("Could not add a new data object. Maybe the limit was reached?");
}
} else {
// if you set enableBound=true and bound=50 (as an example)
// it won't be possible to increase to a value greater than 50.
BasicResult incrementResult = Data.IncrementIntValue(auth,
new IncrementIntValueToKeyModel(
3941,
"FRQHQ-FSOSD-BWOPU-KJOWF",
listResult.DataObjects.get(0).Id,
1,
false,
0));
if(!Helpers.IsSuccessful(incrementResult)) {
System.out.println("Could not increment the data object");
}
}
```
The idea is to either create a new data object (if such does not exist) or increment and existing one.
#### Paying upfront
If you instead want your customers to pay upfront, eg. buy the “usage credits” in advance, we can use the code below. We assume that a data object with name **usagecount** already exists.
If the value gets below zero, we will not permit the customers to continue using that feature.
```c# C# theme={null}
var auth = "Access token with AddDataObject, ListDataObject and IncrementIntValue permission. Please also set KeyLock value to '-1'";
var licenseKey = "LZKZU-MPJEW-TARNP-UHDBQ";
var result = Data.ListDataObjects(auth, new ListDataObjectsToKeyModel { Contains = "usagecount", Key = licenseKey, ProductId = 3349 });
var obj = result.DataObjects.Get("usagecount");
var res = obj.DecrementIntValue(auth, decrementValue: 1, enableBound:true, lowerBound: 0, licenseKey: new LicenseKey { Key = licenseKey, ProductId = 3349 });
if (!res)
{
Console.WriteLine("Could not decrement the data object. The limit was reached.");
}
```
```java Java theme={null}
String auth = "Access token with AddDataObject, ListDataObject and IncrementIntValue permission. Please also set KeyLock value to '-1'";
ListOfDataObjectsResult listResult = Data.ListDataObjects(auth, new ListDataObjectsToKeyModel(3941, "FRQHQ-FSOSD-BWOPU-KJOWF", "usagecount"));
if (!Helpers.IsSuccessful(listResult)) {
System.out.println("Could not list the data objects.");
}
BasicResult decrementResult = Data.DecrementIntValue(auth,
new DecrementIntValueToKeyModel(
3941,
"FRQHQ-FSOSD-BWOPU-KJOWF",
listResult.DataObjects.get(0).Id,
1,
true,
0));
if(!Helpers.IsSuccessful(decrementResult)) {
System.out.println("Could not decrement the data object");
}
```
#### Recurring credits as a part of a subscription
Let’s suppose your subscription offers usage-credits that are refilled on a monthly basis. If we assume that each license key has the data object with the name “credits” associated with it, we can follow the following steps to automatically reset the int value of all data objects with the name “credits” (that are associated to a license key).
1. Select the product.
2. Click on **Data Objects** at the top of the page.
3. Create a data object with the name **cryptolens\_dobjreset** and set the string value to `[{"Name":"credits", "Freq":30, "Default":10}]`. This will set the int value of all data objects associated with a license key that have the name “credits” to 10 every 30 days.
You can read more about this feature on [this page](https://help.cryptolens.io/web-interface/product-configurations).
You can reset data objects with a different names as well. For example, to reset “credit” every day and “credit2” every second day, you can set **cryptolens\_dobjreset** (on the product level) to `[{“Name”:”credit”, “Freq”:1, “Default”:5}, {“Name”:”credit2”, “Freq”:2, “Default”:5}]`.
### Security note
You may have noticed that we recommend to set **KeyLock to ‘-1’** when creating access tokens that will be used to work with data objects. The reason for that is to ensure that a license key and a product id are required to perform operations on data objects. If we don’t set the key lock value, it is possible to use the same access token to modify data objects of other customers.
# User based activations
Source: https://help.cryptolens.io/licensing-models/user-based-activations
Per user licensing approach
## Idea
Normally, node-locking (aka machine code locking) works on a per device basis. When end users switch devices often, node-locked licenses might not be ideal since the old devices would need to be deactivated to allow new ones to activate. One solution to this is to use [floating licensing](https://help.cryptolens.io/licensing-models/floating), where deactivations occur automatically. Another solution is to activate users instead of their devices, i.e. license on a **per user** rather than **per device** basis.
## Implementation
There are two ways you can license on a per user basis:
* **Passwordless (no user input)**: If you can identify a user using a system similar to Azure AD, then there is no need to ask them for a username and password. The identifier from such system can be used directly (or in hashed form) as the machine code. The user email would be stored in the machine code field.
* **Username and password (user input)**: In other cases, especially when there is no reliable way to identify a specific user, it’s better to ask users to provide the password since they have control over the username (with Azure AD, they would not, which is why a password is not needed). The idea is the same as case 1, except that machine code would contain a hash of the password, and the friendly name would be the username.
### Passwordless user authentication
One way to identify a user is by using their user principle name, which can be obtained with the command below:
```bash theme={null}
whoami.exe /UPN
```
Activation and verification of a user can occur using the same method, e.g. Activate, as shown below:
```c# theme={null}
var res = Key.Activate(AccessToken.AccessToken.Activate, new ActivateModel
{
Key = "KMZEW-SBRAE-VWCEK-CDLQE",
ProductId = 3349,
MachineCode = userPrincipalName;
});
```
### Username and password authentication
If there is no way to identify a user automatically, you can ask users to identify themselves. In this case, it is important that users identify themselves with password that only they know, to prevent other users from using their seat.
The idea is to store their password hash inside the machine code field, allowing your application to verify that a certain password is correct but not be able to obtain the real password. `Helpers.ComputePasswordHash` uses a special algorithm (more precisely PBKDF2) to compute hash in such a way that it is harder for an adversary to guess. Normal hash functions such as SHA256 are very fast to compute, whereas PBKDF2 makes the computation slow on purpose so that it takes longer time to guess the original password.
To add a new user, the code below can be used:
```c# theme={null}
var username = "testuser";
var password = Helpers.ComputePasswordHash("testpassword");
// Adding a user
var res = Key.Activate(AccessToken.AccessToken.Activate, new ActivateModel
{
Key = "KMZEW-SBRAE-VWCEK-CDLQE",
ProductId = 3349,
MachineCode = password,
FriendlyName = username
});
Assert.IsTrue(Helpers.IsSuccessful(res));
```
In the verification phase, we need to use `Key.GetKey` instead of `Key.Activate`, since we don’t want to allow the end user to add new users. If you want to allow users to do that, feel free to change back to using `Key.Activate`.
```c# theme={null}
//verifying password
var res2 = Key.GetKey(AccessToken.AccessToken.GetKey, new KeyInfoModel
{
Key = "KMZEW-SBRAE-VWCEK-CDLQE",
ProductId = 3349
});
Assert.IsTrue(Helpers.VerifyPassword(res2.LicenseKey, "testuser", "testpassword"));
Assert.IsFalse(Helpers.VerifyPassword(res2.LicenseKey, "testuser", "testpassword2"));
```
## FAQ
### How is this approach different from the original user account authentication tutorial?
The method described in the [user account authentication](/licensing-models/user-login-intro) tutorial differs from the method we described in this tutorial. In the user account authentication tutorial, we are assuming that the customer has a Cryptolens customer account linked to the customer object in your dashboard.
Currently, a customer object can only have one user associated with it. Since user based activations (described in this tutorial) stores each user as an activation of a license, you can create any number of users. This also means that you have more control over user creation.
# User authentication
Source: https://help.cryptolens.io/licensing-models/user-credentials
Covers different ways that you can authenticate customers using user credentials instead of license keys.
## Introduction
The easiest way to authenticate end users is by using a 20 character long license key. This allows you to restrict which features your clients have access to, the expiry date, and more.
If you anticipate that your clients will have many licenses, or if you prefer to authenticate them using username and password, Cryptolens supports several ways to accomplish this.
## Approaches
**Method 1**: If you are using the Customer portal and your customers have an account with us, you can use [this approach](/licensing-models/user-login-intro). A new browser window will open where the customer can authenticate, allowing you to retreive all their licenses inside your application once authentication succeeds. Currently supported out of the box in **.NET**.
**Method 2**: If you have assigned licenses to a [Customer object](https://app.cryptolens.io/Customer) in the portal, you can also authenticate them inside your application. This is covered in [this article](/examples/user-verification). This approach allows you to contol the user experience and customers do not need to have access to the customer portal. Currently supported out of the box in **.NET** and **Python**, and support for more languages is coming soon.
**Method 3**: If your customer has many licenses and you want to issue them one main license, you can use [this approach](/feature/web-ui/customer-secret). This is similar to Method 2, except that users authenticate using a long string value instead of user name and password.
**Method 4**: If you would like to limit the number of users that many use a certain license, you can use [this approach](/licensing-models/user-based-activations). This is different from Method 1-3, where the goal was to make it easier for users to have many licenses and not have to keep track of them. In Method 4, the idea is to limit concurrent use based on users and not machine code.
# (Example) Automating customer and license creation
Source: https://help.cryptolens.io/payments/forms/automating-customer-and-license-creation
Description of your new file.
## Introduction
In this tutorial we will go through the steps needed to automatically create new customers and assign them a license key upon a successful transaction. We will assume that you already have a payment form with at least one payment processor. Please review these articles before you continue reading this tutorial:
* [Overview of Payment Forms](https://help.cryptolens.io/payment-form/index)
* [Overview of Requests and Custom Field](https://help.cryptolens.io/payment-form/request)
**Update since 2020.05.04**: A customer can automatically be created in the CreateKey request. If you set `NewCustomer=true`, a new customer will be created and if `AddOrUseExistingCustomer=true`, an existing customer with the same email will be used or a new customer will be created. In both cases, you can call [CreateKey](https://app.cryptolens.io/docs/api/v3/CreateKey) with the same parameters that [AddCustomer](https://app.cryptolens.io/docs/api/v3/AddCustomer) accepts.
## Idea
In order to both create a new customer and then assign them a new license key, we will send two requests to the [Web API](https://app.cryptolens.io/docs/api/v3), one to **create a customer** and one to **create license** key associated with that customer. The links to the required methods are provided below.
Note, we will look more into the details of how to set things up in the next section.
* [Create a customer (Web API docs)](https://app.cryptolens.io/docs/api/v3/AddCustomer)
* [Create a license (Web API docs)](https://app.cryptolens.io/docs/api/v3/CreateKey)
## Implementation
### Creating an access token
In order to ensure that API calls we perform (i.e. the requests) work, we need an access token with **CreateKey** and **AddCustomer** permission, which you can create below:
* [Create an access token](https://app.cryptolens.io/User/AccessToken#/newtoken)
Please tick the right permissions as shown below and save the token that is generated.
Here is an example of a token you can get:
```
WyIxMjc0IiwieUN2RjYveDZTVktsVm9XZFhVcmtTdzZJSm1yTU1zTnZoTXdKQmNsSSJd
```
### Setting up API calls
#### Creating a customer
A customer can be created using the url below. You can review other parameters of this method [here](https://app.cryptolens.io/docs/api/v3/Customer).
In the request below we have added name parameter that comes from the custom field.
```
https://app.cryptolens.io/api/customer/AddCustomer?Name=[custom]&token=