iPEK License API - v.1.0

Basics

All request and responses described in this document are encrypted - information's about algorithm can be found at the bottom of the page

 

Endpoints

Note: All request uses the same model, described in the next section - the only difference is in a Type field in the request and in the extra fields in request/response

Pre-prod: https://pre.beta.wincan.com/licensing/api

Prod: https://web.wincan.com/licensing/api

Endpoint

Method

Type field

Additional request field

Additional response field

Additional response field

Content-Type header

Endpoint

Method

Type field

Additional request field

Additional response field

Additional response field

Content-Type header

/api/ipek/activate

POST

Activation

"activationCode": string

 

 

application/ipek-alic

/api/ipek/deactivate

POST

Deactivation

"licenseNumber": int

"activationCode": string

"alternativeEmail": string

application/ipek-dlic

/api/ipek/check

POST

Check

"licenseNumber": int

 

 

application/ipek-clic

Request

{
  "version": "1.0",
  "requestId": "45449232-1f2d-4b98-a987-1c64fc856c7c",
  "sessionId": "42c2d6f6-6faa-41af-85c6-93e34d9c605a",
  "hardwareId": "ee1ff1b9-fd3e-4931-ae46-908e5ad4537b",
  "type": "Activation",
  "activationCode": "233254-434-d24a-015c-25d5a3326ac1",
  "localTime": "2019.07.15 13:50:42 +02:00",
  "email” : “j.kowalski@gmail.com”
}

 

api version
unique for each request - GUID
unique for each application run (set on program start) - GUID
unique for each machine (change when hardware changed)

depends on command type, basically operation argument

 

Email of end-user

 

requestIdsessionIdhardwareId are GUIDs.

 

Response

{
  "version": 1.0,
  "requestId": "45449232-1f2d-4b98-a987-1c64fc856c7c",
  "status": "ERROR",
  "errorReason": "wrong_number",
  "errorDetails": "",
  "localTime": "2019.15.07 13:50:42 +02:00",
  "licenseXML": "",
    "activationCode":  "737250-434-d24a-015c-25d5a3326ac1"
}

 

 

ERROR or OK
only if status is ERROR

 

 

for deactivation
for activation and check - optional

 

PoSSIBLE HTTP Responses

Code

Name

Returns data?

Remarks

Code

Name

Returns data?

Remarks

200

OK

Yes

used for status ERROR too

401

Unauthorized

No

 

500

Internal Server Error

No

 

Errors

Common

  • validation_error

  • unsupported_api_version

  • unsupported_product

Activation

  • invalid_code

  • already_activated

  • license_expired

  • license_deleted

Deactivation

  • wrong_number

Check

  • not_activated

  • wrong_number

  • license_deleted

Authorization

We are using the HMAC SHA256 authorization. Server and client have the same SECRET (need to calculate signature, see below).

Creating authorization headers steps:

Header name

Description

Example

Header name

Description

Example

Date

Current date in UTC format. You need it to create representation string.

IMPORTANT! Date should be synchronized with our servers!

Current date you can get from GET webapi/Status/Get

PRE - https://pre.beta.wincan.com/licensing/api/webapi/Status/Get

PROD- https://web.wincan.com/licensing/api/webapi/Status/Get

 

The best approach is to get current date from server when you run your app, count the offset between application current date and Wincan Service date and add this offset to current date when you send request to Wincan Service

Wed, 04 Mar 2015 09:38:51 GMT

Authorization

Authorization Scheme - constant string “WincanLicenseService“

Signature - it is calculation of secret key and representation string

See below

WincanLicenseService LUR9P9zv5WsUAu/OXPKflGoVDPgalBIsXmfAnidZW5k=

X-WinCanLicenseService-MessageToken

Random token. Can be guid. You need it to create representation string

5f57e08a-bdc4-4c78-9798-d31685851ecd

X-WinCanLicenseService-ClientId

The name of client. You need it to create representation string

constant string “IPEK“

IPEK

 

Authorization - Signature

Signature it is a calculation of secret key and representation string.

To genereate this signature we are using method like:

public string Generate(string secret, string representation) { var secretBytes = Encoding.UTF8.GetBytes(secret); var valueBytes = Encoding.UTF8.GetBytes(value); string signature; using (var hmac = new HMACSHA256(secretBytes)) { var hash = hmac.ComputeHash(valueBytes); signature = Convert.ToBase64String(hash); } return signature; }
public string Generate(string secret, string representation) { var secretBytes = Encoding.UTF8.GetBytes(secret); var valueBytes = Encoding.UTF8.GetBytes(value); string signature; using (var hmac = new HMACSHA256(secretBytes)) { var hash = hmac.ComputeHash(valueBytes); signature = Convert.ToBase64String(hash); } return signature; }

 

Authorization - How to generate representation string?

The representation string should be in format like:

METHOD\n UtcDateString\n MessageToken\n ClientId\n AbsoluteUrl

where:

Representation part

Description

Example

Representation part

Description

Example

METHOD

The rest method

PUT

UtcDateString

Current date in UTC format. This value should be the same which is in header in Date

Wed, 04 Mar 2015 09:38:51 GMT

MessageToken

Random token. Can be guid. This value should be the same which is in header in X-WinCanLicensingService-MessageToken

5f57e08a-bdc4-4c78-9798-d31685851ecd

ClientId

The name of client. This value should be the same which is in header in X-WinCanLicensingService-ClientId

IPEK

AbsoluteUrl

The absolute url from request. (Without query string and /api/ prefix)

ipek/activate

It is important to split data by sign \n

Example:

 

Body encryption - Implementation samples

 

You can check out a working sample of the application [SAMPLE_APP_NAME] (C#).

Sample implementation:

 

 

 

 

 

Body encryption

To encode request body, we need following things:

  • RSA CSP public key (private belongs to the licensing server)

  • AES 256-bit - new for each request

Encode body steps:

  1. Generate new AES
     

  2. Encode body with AES and convert to Base64 string
    var body_encoded = ToBase64Str(aes.Encode(body))
     

  3. AES IV convert to Base64, next encrypt IV parameter using RSA (encryption using OAEP padding) and convert it to Base64 string again:
    var iv_base64str = ToBase64Str(aes.iv)
    var iv_str = ToBase64Str(rsa.Encode(iv_base64str))
     

  4. Do the same for Key parameter
    var key_base64str = ToBase64Str(aes.key)
    var key_str = ToBase64Str(rsa.Encode(key_base64str))
     

  5. Create message
    var result = iv_str + "|" + key_str + "|" + body_encoded

 

To decode response body, we need following things:

  • RSA CSP private key (public is used by the licensing server, it is a different one than the key used during request encryption)

Decode body steps:

  1. Split message on 3 parts, using "|" character (the same format as in the request)
    ASDOASKD234=|2ASD+2fDSF=|asdasdasd...
     

  2. First part is IV parameter of AES, second part is Key parameter of AES. We have to convert them from Base64 to bytes and then decode them using RSA private key
     

  3. Now we can use generated AES to decode third part of body, which is an appropriate response