Skip to content

Commit 979f2f9

Browse files
authored
Add support for saving and loading licenses as a string (#4)
* Add support for saving and loading licenses as a string * Update examples
1 parent 8d45628 commit 979f2f9

File tree

6 files changed

+97
-11
lines changed

6 files changed

+97
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,4 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
licensefile.skm

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,38 @@ if res[0] == None:
3535
print("An error occured: {0}".format(res[1]))
3636
else:
3737
print("Success")
38+
39+
license_key = res[0]
40+
print("Feature 1: " + str(license_key.f1))
41+
print("License expires: " + str(license_key.expires))
3842
```
3943

4044
* `pubKey` - the RSA public key (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
4145
* `token` - the access token (can be found [here](https://app.cryptolens.io/docs/api/v3/QuickStart#api-keys), in *API Keys* section).
4246
* `product_id` - the id of the product can be found on the product page.
4347
* `key` - the license key to be verified
4448
* `machine_code` - the unique id of the device (we are working on adding a method similar to `Helpers.GetMachineCode()`).
49+
50+
### Offline activation (saving/loading licenses)
51+
52+
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.
53+
54+
```python
55+
# res is obtained from the code above
56+
57+
if res[0] != None:
58+
# saving license file to disk
59+
with open('licensefile.skm', 'w') as f:
60+
f.write(res[0].save_as_string())
61+
```
62+
63+
When loading it back, we can use the code below:
64+
65+
```python
66+
# read license file from file
67+
with open('licensefile.skm', 'r') as f:
68+
license_key = LicenseKey.load_from_string(pubKey, f.read())
69+
70+
print("Feature 1: " + str(license_key.f1))
71+
print("License expires: " + str(license_key.expires))
72+
```

licensing/methods.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,4 @@ def activate(token, rsa_pub_key, product_id, key, machine_code, fields_to_return
5454
else:
5555
return (None, "The signature check failed.")
5656
except Exception:
57-
return (None, "The signature check failed.")
58-
59-
60-
57+
return (None, "The signature check failed.")

licensing/models.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import json
99
import base64
1010
import datetime
11+
import copy
12+
13+
from licensing.internal import Helpers
1114

1215
class LicenseKey:
1316

@@ -40,7 +43,7 @@ def __init__(self, ProductId, ID, Key, Created, Expires, Period, F1, F2,\
4043
self.allowed_machines = AllowedMachines
4144
self.data_objects = DataObjects
4245
self.sign_date = SignDate
43-
self.raw_respone = RawResponse
46+
self.raw_response = RawResponse
4447

4548
def from_response(response):
4649

@@ -56,6 +59,44 @@ def from_response(response):
5659
obj["Customer"], obj["ActivatedMachines"], obj["TrialActivation"], \
5760
obj["MaxNoOfMachines"], obj["AllowedMachines"], obj["DataObjects"], \
5861
datetime.datetime.fromtimestamp(obj["SignDate"]), response)
62+
63+
def save_as_string(self):
64+
"""
65+
Save the license as a string that can later be read by load_from_string.
66+
"""
67+
res = copy.copy(self.raw_response.__dict__)
68+
res["licenseKey"] = res["license_key"]
69+
res.pop("license_key", None)
70+
return json.dumps(res)
71+
72+
def load_from_string(rsa_pub_key, string):
73+
"""
74+
Loads a license from a string generated by save_as_string.
75+
Note: if an error occurs, None will be returned. An error can occur
76+
if the license string has been tampered with or if the public key is
77+
incorrectly formatted.
78+
"""
79+
80+
response = Response("","","","")
81+
82+
try:
83+
response = Response.from_string(string)
84+
except Exception as ex:
85+
return None
86+
87+
if response.result == "1":
88+
return None
89+
else:
90+
try:
91+
pubKey = RSAPublicKey.from_string(rsa_pub_key)
92+
if Helpers.verify_signature(response, pubKey):
93+
return LicenseKey.from_response(response)
94+
else:
95+
return None
96+
except Exception:
97+
return None
98+
99+
59100

60101
class Response:
61102

@@ -66,7 +107,7 @@ def __init__(self, license_key, signature, result, message):
66107
self.message = message
67108

68109
def from_string(responseString):
69-
obj = json.loads(responseString)
110+
obj = json.loads(responseString)
70111
return Response(obj["licenseKey"], obj["signature"], obj["result"],obj["message"])
71112

72113
class RSAPublicKey:

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
setup(
33
name = 'licensing', # How you named your package folder (MyLib)
44
packages = ['licensing'], # Chose the same as "name"
5-
version = '0.2', # Start with a small number and increase it with every change you make
5+
version = '0.3', # Start with a small number and increase it with every change you make
66
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
77
description = 'Client library for Cryptolens licensing Web API.', # Give a short description about your library
88
author = 'Cryptolens AB', # Type in your name
99
author_email = '[email protected]', # Type in your E-Mail
1010
url = 'https://cryptolens.io', # Provide either the link to your github or to your website
11-
download_url = 'https://github.com/Cryptolens/cryptolens-python/archive/v_02.tar.gz', # I explain this later on
11+
download_url = 'https://github.com/Cryptolens/cryptolens-python/archive/v_03.tar.gz', # I explain this later on
1212
keywords = ['software licensing', 'licensing library', 'cryptolens'], # Keywords that define your package best
1313
install_requires=[ # I get to this in a second
1414
'pycrypto'

test.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,35 @@
66
"""
77

88
from licensing.internal import Helpers
9-
from licensing.models import Response, RSAPublicKey
9+
from licensing.models import Response, RSAPublicKey, LicenseKey
1010
from licensing.methods import Key
1111

1212
pubKey = "<RSAKeyValue><Modulus>sGbvxwdlDbqFXOMlVUnAF5ew0t0WpPW7rFpI5jHQOFkht/326dvh7t74RYeMpjy357NljouhpTLA3a6idnn4j6c3jmPWBkjZndGsPL4Bqm+fwE48nKpGPjkj4q/yzT4tHXBTyvaBjA8bVoCTnu+LiC4XEaLZRThGzIn5KQXKCigg6tQRy0GXE13XYFVz/x1mjFbT9/7dS8p85n8BuwlY5JvuBIQkKhuCNFfrUxBWyu87CFnXWjIupCD2VO/GbxaCvzrRjLZjAngLCMtZbYBALksqGPgTUN7ZM24XbPWyLtKPaXF2i4XRR9u6eTj5BfnLbKAU5PIVfjIS+vNYYogteQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
1313

14-
res = Key.activate(token="WyIyNTU1IiwiRjdZZTB4RmtuTVcrQlNqcSszbmFMMHB3aWFJTlBsWW1Mbm9raVFyRyJd",\
14+
res = Key.activate(token="WyIyNjA1IiwiTjhZQUpIYXJPaVdBV0ozQUlKVC9tamdDbFZDRzhZRHpaU243L2R2eCJd",\
1515
rsa_pub_key=pubKey,\
1616
product_id=3349, key="ICVLD-VVSZR-ZTICT-YKGXL", machine_code="test")
1717

1818
if res[0] == None:
1919
print("An error occured: {0}".format(res[1]))
2020
else:
21-
print("Success")
21+
print("Success")
22+
23+
license_key = res[0]
24+
print("Feature 1: " + str(license_key.f1))
25+
print("License expires: " + str(license_key.expires))
26+
27+
28+
if res[0] != None:
29+
# saving license file to disk
30+
with open('licensefile.skm', 'w') as f:
31+
f.write(res[0].save_as_string())
32+
33+
34+
# read license file from file
35+
with open('licensefile.skm', 'r') as f:
36+
license_key = LicenseKey.load_from_string(pubKey, f.read())
37+
38+
print("Feature 1: " + str(license_key.f1))
39+
print("License expires: " + str(license_key.expires))
40+

0 commit comments

Comments
 (0)