Introduction to PKI API

The PKI allows users and systems to verify the legitimacy of certificate-holding entities and securely exchange information between them over the ether. The introduction of a PKI enables stronger, certificate-based security and delivery of identity services and management tools that maximize network efficiency and security.

Elements of security

  • Authenticity  : The message is truly sent from the people we are expecting.
  • Integrity  :  The message is not altered or intercepted by unauthorised sources.
  • Confidentiality :  The message is encrypted and can only be decrypted by authorised people.

Why PKI API?

  • While using PKI API, Payload will be covered with end-to-end security on public network. This will remove the dependency of VPN to create secure tunnel communication between Contis and Client server.
  • PKI API allows you to develop mobile APP without developing wrapper between App and Contis API.
  • PKI API will improve the performance by server to server calling whereas in VPN calls are routing via specific VPN tunnel.

PKI API Implementation User Guide


Consuming the Contis API

Client should set up the necessary infrastructure to implement the PKI, enabling Contis’ APIs to be consumed. Client should enrol for the client certificate by providing the Contis team with its own PKI certificate (public key) or by requesting the PKI certificate from Contis (in which case private & public keys will be made available via Client’s SFTP address and notification sent to Client’s registered email with unique GroupCode which must be passed in each request header).

Contis will also provide Client with Contis’ PKI certificate (public key) via Client’s SFTP address which Client will require for encryption of sensitive payload data such as password, mPIN etc. It is the integrator’s responsibility to save both the Client’s private key and Contis’ public key safely. A notification email will also be sent to Client’s registered email address. Further certificate enrolment details can be found in Certification Process.

Summary of PKI API

Clients must complete the following processes to obtain access and consume Contis’ API:

  1. Enrol for the PKI certificates.
  2. Obtain a Contis PKI certificate (public key) and Client’s private key.
  3. Obtain security access token (via login method) and generate a digital signature of the payload to authenticate
    API requests. For anonymous methods, only a digital signature is required.
  4. Follow the format of Contis’ API request payload.

Registration

Client is required to register itself with Contis as a unique identity. This process allows Contis to confirm Client has a PKI (public key infrastructure) in place and is ready to provide a reliable solution by accepting Contis’ expected standards. The detailed registration process can be found in Certification Process.

Unique identity for authentication

Client must have valid Contis PKI (public key) and PKI (private key) certificates and a valid GroupCode which will be used to identify the users’ secure request and response payloads by generating digital signatures and verifying the integrity of the payloads.

Authenticate Client API Requests

Client is required to pass the client GroupCode & digital signature in every API request header which will be used to identify both Client and the PKI certificate the Client is using to sign the message.

Request payload format

Any request sent to the Contis API must contain certain pieces of information before the system will process them. If these are missing or incorrect, they will be rejectedand an error message returned.

Client should note GroupCode and x-signature when authoring requests to the Contis API. Further details on the API message schema.

Request Authorised and Processed

Contis receives the integrator’s request and uses the information contained in the request message header to validate Client’s identity by:

  • Fetching the Client’s public key.
  • Generating and verifying the digital signature sent in the request header.

When Contis has authenticated the integrator’s identity, the API request can be authorised and processed.

Response Header

The x-signature is a digital signature generated from the response payload using Contis’ PKI certificate (private key) and is included in every API response header. To validate and verify the response message integrity, the integrator can use this signature to match the signature generated using Contis’ PKI certificate (public key) (as initially shared with the integrator).

RESPONSE HEADERDescription
x-signatureDigital signature generated

Certification Process ​

Client is required to complete and pass Contis’ certification process so customer applicants can be identified safely and easily, connected, and consume the Contis API. It also ensures sufficient control and understanding exists between Contis and the customer.

To complete the process:

  1. Contact Contis’ onboarding team to provide PKI certificate (public key) OR to request PKI certificate. Certificates will be provided via SFTP.
  2. Contis’ onboarding team will email Client the unique GroupCode and SFTP information from which Client can access Contis’ PKI certificate (public key). Contis’ team can help provide Client’s PKI certificate (private & public key) if the certificate is generated by Contis.
  3. Use Contis’ public key to encrypt sensitive data (eg. password, mPIN etc) while passing in request payload and generate digital signature of  Client’s request payload using Client’s private key. Pass the digital signature in the request header and complete any Contis API call.
  4. Contis will verify the contents and, if satisfied, will provide further access to the actual API.

Authenticating API Requests

Any request sent to the Contis API must contain certain pieces of information before the system will process them. If these are missing or incorrect, the API will reject them and return an error message.

  • To authenticate your API requests:
  • Generate the Message Digital Signature Supply the following Request Headers:
    1. groupcode
    2. x-signature

Generating the Digital Signature

The Message Digital Signature is required to prove:

  1. A message has been signed by the Holder of the PKI certificate.
  2. Data integrity of the message (ie. no data has been intercepted).

The Digital Signature is specific to the body of the request.

NB: The entire HTTP request body is used to generate the Digital Signature after sensitive elements are encrypted (eg.password and mPIN). To generate the digital signature using Secure Hash Algorithm (SHA):

  1. Obtain the Hash of request body using SHA256 Managed & Client’s PKI certificate (private key). Data must be encoded using UTF8 while computing hash.
  2. Request the RSA encryption algorithm to generate the digital signature using the Hash produced above.
  3. The output generated is the Digital Signature.
  4. Pass this Digital Signature in the API request headers for all your API requests with the request body.

The Digital Signature needs to be placed into the specific request header named “x-signature”.

RESPONSE HEADERDescription
x-signatureHGbgcNSF2TVD6b8czWYBvJbG4FD+hYyZXi//7YnolCaRYKC+MD7FnFhM9Ar1eL2OinBfDM0y7jt9Ipjyf5F4mL0ecgF+
ugs9PWoePjewho3o0av5eo4f1MntPJSBc+/g8BwolSjXlaOBW7i0XH0C7y3knaywnrk14ctwmxEDkQxzoxgnehDWQe
VvGD2Cjk5VtXb8qPaUFGlUcx3Ide20Mi3IM6uD3sOqJPtoLFIzGPemesDyyRN9gcGBXi+bUcMZUcK9xc7Ctd6U2bJIr
wmpnMurr0Eo0J936ESXVr5qZa0dhBLPWMP1QgREwGNO2j8rUySO6sJc8+CiNfTAe4OrQ==

This value will help Contis validate and verify the integrity of the message request sent in the request body. If the signature cannot be verified, an HTTP 403 Forbidden response will be returned.

API Message Schema

Headers cited in groupcode and x-signature header must be supplied by integrators in addition to endpoint information and message schema such as Authorisation and grant_type headers.

groupcode

This must be supplied and valid in all API requests.

RESPONSE HEADERDescription
groupcodeB80E7823-740B-4C6F-9B9B-D2643B19B0F3

This value will help Contis to decrypt sensitive elements such as password and mPIN sent in the request body. If this value is not found in the request header, an HTTP 406 NotAcceptable response will be returned. If the value is not matched and PKI certificates for the value are not found, an HTTP 403 Forbidden response will be
returned.

x-signature header

A request body is used to create the Digital Signature for requests that have a payload. All requests require the groupcode and x-signature  included in the request header.

Example of an encoded Digital Signature:

RESPONSE HEADERDescription
groupcodeB80E7823-740B-4C6F-9B9B-D2643B19B0F3
x-signatureHGbgcNSF2TVD6b8czWYBvJbG4FD+hYyZXi//7YnolCaRYKC+MD7FnFhM9Ar1eL2OinBfDM0y7jt9Ipjyf5F4mL0ecgF+
ugs9PWoePjewho3o0av5eo4f1MntPJSBc+/g8BwolSjXlaOBW7i0XH0C7y3knaywnrk14ctwmxEDkQxzoxgnehDWQe
VvGD2Cjk5VtXb8qPaUFGlUcx3Ide20Mi3IM6uD3sOqJPtoLFIzGPemesDyyRN9gcGBXi+bUcMZUcK9xc7Ctd6U2bJIr
wmpnMurr0Eo0J936ESXVr5qZa0dhBLPWMP1QgREwGNO2j8rUySO6sJc8+CiNfTAe4OrQ==

Examples

Client Key Pair

Below is the example private key of the client which is used to generate the digital signature

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAhsPMmR2RizzAE63kdKJRCjn/MZH8memnxJUEsvP/pvIChZMM
xXY7sSHUQa+zb0iR0kZrHx/QuHQAea/E1QAHzo9U3dM0I5+bEXObuVkJq9owJ//Q
eZxXrpSyccFX3pdSLQuIdg1QVCvZrSxVvIna4sa5qBNbdR7nQhxoVdLi3Hv9uyyI
3tk4bK1d/klyY9XP7cL/3JeCZf/kbOfyb3KcgKuB2bi/fgdJhifvJGMj4ayVmBfe
gJEfCu9SXZQvJJ/ZPuO0HVWMRVV65rJvhtgovezbYHErBqtwarvG4zyz9S60VOYC
maA4OzvejgjU86MY/sALVcDvv3I+ekq1QW8EPwIDAQABAoIBADsqVjZ41U+4bamW
JCESrBqdyMyWB7z46Kd1Nxlr5zb/tBy5qRc+J8nGDN6DyXbXePHE6b3B5Yw9nYHP
Kc7L0yGcKsmysobbSSS5yUnv7b+NrKu682eYvKQQc2Fe3XmDnNfa4t/VS4mQsOIG
/2MlcLw0PAku6m26khQjzai0S9Y5U7xVBLujMZSX9htxXA3vBOsae4V/ZT/kFOeK
eIBc5MHYDRco4ePjRU/8XLj9GofAWSGHIDTz931TvF3jQjV7T3fmv1OltVeO3zYY
t+HMUQG3psUFs7HXbLptFg2uGnAH5HOKf6J06J0ZSC1XHjfkAdSuyddOrSPKxUh0
XwkF/YkCgYEAys+Rp2ZbbU+bOAr6LV19sMvtYkNEfDTbfJHh1m+SRmqBanjeKd2e
XqciP4HWFCW6+a/H9VshsT8p6q9Es6vZOoJQ68eYi2tJZr6zweqhiq17Fr73xoEk
4qB285WLx5YRPvNPUKzVDfJz56FSKjn4p8YqhaxHjWiOf8j2nzjJq9cCgYEAqhu4
RKqNnGwGohweJ/V4PO/zvtk/kV8HC8+fe2vGnOTw++oolTy2lLlzkkU9wkd9z4Nu
avMQzR/YTkcbzQ3gyIRQM47oewGKb97pj3c5o8xdWmhkrXiJ7ys60Oc8gAANbzqw
kk3XaWuKKAqlazYRJ/nfzmElrG7DGYdOA4GDHdkCgYEAtX37Qmr+9lt+9DAeoepA
SK61yHGaH+zNXjTOfS9rH0jAd5+icKS6zMmUhHCUO4NoR3le39ql1BBKWpJuULtA
snNcJyN/B4UDMscF5ksqNQf6Vuieoy1+7K/cwy7Y38sTs9nY6MFCCDEoN+jNtqwa
MUnNU8JQZeNjYkddLC+NihECgYEAnT3qY27UYBdrOkaLiZxafNnBklT3ccVJmh1d
6pSBj14BdJvKz8jDbU7QChF/OSsknMF9O9dbXIFnTVRl2nJNqozEJiSfubR+51JC
y58F5OdwE7YT+lZGCpMHVzuorRlfUcYKmfLigEwr9T+CEUx1J2LPAtLCJFZuehac
gpkhPmECgYB/euG3LxhlwG/J9RKg8s/56Zw1+mfKrGKlfIYBOeeLadS90cyZLVp/
jttfbgTJ3CDLxA3haMHODR7ghLGgcfyp+8uVLWSZ4nVmOwuMXyttNb1DkB+ANx4R
KOZ54R6PHnH/dBIQtxEikNzEjDoIyqKJfJKRTdWzVz2b47eQrIaqtA==
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhsPMmR2RizzAE63kdKJR
Cjn/MZH8memnxJUEsvP/pvIChZMMxXY7sSHUQa+zb0iR0kZrHx/QuHQAea/E1QAH
zo9U3dM0I5+bEXObuVkJq9owJ//QeZxXrpSyccFX3pdSLQuIdg1QVCvZrSxVvIna
4sa5qBNbdR7nQhxoVdLi3Hv9uyyI3tk4bK1d/klyY9XP7cL/3JeCZf/kbOfyb3Kc
gKuB2bi/fgdJhifvJGMj4ayVmBfegJEfCu9SXZQvJJ/ZPuO0HVWMRVV65rJvhtgo
vezbYHErBqtwarvG4zyz9S60VOYCmaA4OzvejgjU86MY/sALVcDvv3I+ekq1QW8E
PwIDAQAB
-----END PUBLIC KEY-----

Contis Key Pair

Below is the example public key which is used for the verification of digital certificates generated using the private key (cited above). This key can be used to encrypt the sensitive data field in the API request while making an API call.

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAnsaZAhnm+x+2UvABvNdfkp/yOG5A6jvGakRR781Qij+flg2o
R3jwgWekUmNExsddVPWfjDFRUbtzxkg8wlRMaU61/Wcdi8MUNGGsFVmz1yDpg7um
y7sW3Nev+/spHN/kbZqBf8P5z1C+d/hcPHYxEKM71b60gS5+SCVP+x54AlE2U0cC
2WPqXjCgHrQFF/x+XsRoIhvlyngn/uBLumV3m2DFBYb6BB7QflLdSBd74KLxr80F
UGB0r7tSLi9brHJf4sa4HPCf+x0UV4UZiA0nI564Iy+qmOfWAfd6Tdn1o2ZK2pDM
dg28cUKX06C4pgN4fV2JFZe0Xk2vUsyAa0D68QIDAQABAoIBABgmZHEDoBSrzOde
DqGaz7RlZCoNqQ2HpIUW2bCkFb9FcKBZ+PHQFSLVsRJ/+17RxaItzwP7n18TaEdt
RuKXAF1XJcrCk6WluS3DenFv6LEM1j/de8AKt65FF0U4PVdhPaUnJlHY7OcJ5MKq
MdtXbdyWO3xmKp3ohLg0Bq+PQZk2ZDsivFBsSIbulo+kOlFMRnR8FrCadYeoDPBO
S8l6N3qIkWkjA36NyXkBbIB1fmE2YxUwuGGTpCWcz53AO3yPtYUpiyLSZrgdxTml
WzTT0vyWTAUbaGNPKxmLOsz74fQkE+qhELj5QyuIFyZjkdIgNT6Eb4uJ5XbdTPwT
NdJN3f0CgYEAzDb8iuT899ShffUQFvbI05WJOUJ41rw5TmivOHGTQnC77ao4Q5LS
UNYo+9EyMfA1b2lto3+ipK5bfSDH74EVgqYYigaJO/cqM/GHvnIfF93Uudldd20s
W//kJDJltRYLVsiUR5QqYV0PgAbJdWThQgktuyYdfBSzl3j9ROyFXQUCgYEAxwnY
kn4UD8BAXKcjfCrN6Q6fH+9RcIBTxyhOpS5A48tsB1gCY+M1XyssmKqtGgz3SGL/
7atfzlET/SB2rX0/7o5dhP2qSJs54jevCzLSJGXgk9WdLjPJ2QY74O+n7M3XQCWB
PRhVVu/GxsM5UoEWG3GlZGXYWwCTekWndFCpaf0CgYEAk4GcRQ9GEhVKWNrstkmn
of0/U1bKRgFLO3GuLw0Km1EmzXLIlTa2J6GplMr0gNHLJyB2C0UkS+ONPgKxqDQL
P4WN8BTsh53uo/pwXIW+I9Ud1OhG9P6srf9V4Tdt87Fqm1LimBlTy2QW0BkW527o
rnRGzgmn/npNhrnj5ycY5akCgYAxyHyq524NIoD2q7dsbyhhio6yZiFwiihqP+Kr
3g3M7CxxCcpPQZ0v2JSm+smhIm0XZeutfpfP+ueNAHmumw1KlcE+alQVJP4tXtAh
dOyqvfCWCW/vBnUOG0kO0eKmkDWujbLtnRB7Vi3ZuSq1QCRPaPV9Txv2ZwZ8Jr/j
j+lP4QKBgBVDTqWIe0e6OYsBY1m4epUq9malh+L3434JJ88YzwE/Ue81WhkaHcWP
OAcpKMPTRbBOeH5irzaJ64si+/NiAseFFZle8aTV0MRbhGweguprDbZYFwLSeX2C
p3kGkLTXJbDpjiUhsphVCuSCfxRnnF6SCuRQmv6bY2QkVt5yCKrZ
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnsaZAhnm+x+2UvABvNdf
kp/yOG5A6jvGakRR781Qij+flg2oR3jwgWekUmNExsddVPWfjDFRUbtzxkg8wlRM
aU61/Wcdi8MUNGGsFVmz1yDpg7umy7sW3Nev+/spHN/kbZqBf8P5z1C+d/hcPHYx
EKM71b60gS5+SCVP+x54AlE2U0cC2WPqXjCgHrQFF/x+XsRoIhvlyngn/uBLumV3
m2DFBYb6BB7QflLdSBd74KLxr80FUGB0r7tSLi9brHJf4sa4HPCf+x0UV4UZiA0n
I564Iy+qmOfWAfd6Tdn1o2ZK2pDMdg28cUKX06C4pgN4fV2JFZe0Xk2vUsyAa0D6
8QIDAQAB
-----END PUBLIC KEY-----

Encrypt/Decrypt sensitive data fields

The Client needs to encrypt sensitive fields like password or mPINusing the Contis Public key.

For example, the string “password” is encrypted as:

NdlX+axA1dVB8CdQ+eioHKGfw6H4bc/m/tf2uow6yHHg8MDddF0G0C2sr+mIZE9y0v476Rai3gcXhLlXlAv
LXfJO4GWggXWTDQCnNILgiLzugtIqD5V/JyVzkmNvzBVevXGS0xgg4ydh/2rzZQp+lenn35OEKkpY1IAHhS
uJRHGvTBDIq97ZugxZBSzaTu7DlaYm3xMENGq2tDyeJeTC3QKC5pn2nEZs+mFqHW2VML4Xo4LVyTyPWUybt
x6Ly0vElnrbxw+noERTnfFV5qIW5XicCtqJnOcIW7Ys4JDt2mghVihpcW13PfeTZxF7BcInsKYzG0r1ruXy
ZXTiIgHk+w==

To encrypt the field data, the following code is used.
C# code

public static string EncryptString(string content, string publicKey)
{
    RSACryptoServiceProvider rsaProvider = ImportPublicKey(publicKey);
    byte[] encryptedContent = rsaProvider.Encrypt(Encoding.ASCII.GetBytes(content), false);
    return Convert.ToBase64String(encryptedContent);
}

The Client needs to use its own private key to decrypt field data. Contis will decrypt the encrypted data fields using Contis’ Private key. Contis has encrypted the string “password” using Contis’ Public key.

Using the below code we will be able to decrypt the below string:

NdlX+axA1dVB8CdQ+eioHKGfw6H4bc/m/tf2uow6yHHg8MDddF0G0C2sr+mIZE9y0v476Rai3gcXhLlXlAv
LXfJO4GWggXWTDQCnNILgiLzugtIqD5V/JyVzkmNvzBVevXGS0xgg4ydh/2rzZQp+lenn35OEKkpY1IAHhS
uJRHGvTBDIq97ZugxZBSzaTu7DlaYm3xMENGq2tDyeJeTC3QKC5pn2nEZs+mFqHW2VML4Xo4LVyTyPWUybt
x6Ly0vElnrbxw+noERTnfFV5qIW5XicCtqJnOcIW7Ys4JDt2mghVihpcW13PfeTZxF7BcInsKYzG0r1ruXy
ZXTiIgHk+w==

To password

public static string DecryptString(string content, string privateKey)
{
    byte[] bytContent = Convert.FromBase64String(content);
    RSACryptoServiceProvider rsaProvider = ImportPrivateKey(privateKey);
    byte[] decryptedContent = rsaProvider.Decrypt(bytContent, false);
    return Encoding.ASCII.GetString(decryptedContent);
}

Please note that while encrypting and decrypting Contis has used ASCII encoding.

Generate Digital Siganture

To generate the digital signature of any request payload, the Client needs to use Client’s Private key. For example, if the Client initiates an API call, Contis can generate the digital signature of the request

{
    "ReferenceNumber": "d61e51f5-f701-4330-9609-5cdf40200622"
}
To
HGbgcNSF2TVD6b8czWYBvJbG4FD+hYyZXi//7YnolCaRYKC+MD7FnFhM9Ar1eL2OinBfDM0y7jt9Ipjyf5F4mL0ecgF+
ugs9PWoePjewho3o0av5eo4f1MntPJSBc+/g8/BwolSjXlaOBW7i0XH0C7y3knaywnrk14ctwmxEDkQxzoxgnehDWQeV
vGD2Cjk5VtXb8qPaUFGlUcx3Ide20Mi3IM6uD3sOqJPtoLFIzGPemesDyyRN9gcGBXi+bUcMZUcK9xc7Ctd6U2bJIrwm
pnMurr0Eo0J936ESXVr5qZa0dhBLPWMP1QgREwGNO2j8rUySO6sJc8+CiNfTAe4OrQ==

So the whole request will contain something like the below:

Request Header:

RESPONSE HEADERDescription
groupcodeB80E7823-740B-4C6F-9B9B-D2643B19B0F3
x-signatureHGbgcNSF2TVD6b8czWYBvJbG4FD+hYyZXi//7YnolCaRYKC+MD7FnFhM9Ar1eL2OinBfDM0y7jt9Ipjyf5F4mL0ecgF+
ugs9PWoePjewho3o0av5eo4f1MntPJSBc+/g8BwolSjXlaOBW7i0XH0C7y3knaywnrk14ctwmxEDkQxzoxgnehDWQe
VvGD2Cjk5VtXb8qPaUFGlUcx3Ide20Mi3IM6uD3sOqJPtoLFIzGPemesDyyRN9gcGBXi+bUcMZUcK9xc7Ctd6U2bJIr
wmpnMurr0Eo0J936ESXVr5qZa0dhBLPWMP1QgREwGNO2j8rUySO6sJc8+CiNfTAe4OrQ==

Request Body:

RESPONSE HEADERDescription
groupcodeB80E7823-740B-4C6F-9B9B-D2643B19B0F3
x-signatureHGbgcNSF2TVD6b8czWYBvJbG4FD+hYyZXi//7YnolCaRYKC+MD7FnFhM9Ar1eL2OinBfDM0y7jt9Ipjyf5F4mL0ecgF
+ugs9PWoePjewho3o0av5eo4f1MntPJSBc+/g8BwolSjXlaOBW7i0XH0C7y3knaywnrk14ctwmxEDkQxzoxgnehDWQ
eVvGD2Cjk5VtXb8qPaUFGlUcx3Ide20Mi3IM6uD3sOqJPtoLFIzGPemesDyyRN9gcGBXi+bUcMZUcK9xc7Ctd6U2bJIr
wmpnMurr0Eo0J936ESXVr5qZa0dhBLPWMP1QgREwGNO2j8rUySO6sJc8+CiNfTAe4OrQ==

Which can be verified using Client’s Public key, which will return as either true or false. To generate the digital signature we have used the following code.

C# code.

public static byte[] GetDataHash(string sampleData, byte[] key)
{
    SHA256Managed shaManaged = new SHA256Managed();
    byte[] hashHMAC = shaManaged.ComputeHash(Encoding.UTF8.GetBytes(sampleData));

    return hashHMAC;
}

public static string GenerateDigitalSignature(string body, string privateKeyText)
{
    var hash = GetDataHash(body, Encoding.UTF8.GetBytes(privateKeyText));
    byte[] signedHash;
    
    using (RSACryptoServiceProvider rsa = ImportPrivateKey(privateKeyText))
    {
        signedHash = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    }

    var encodedHash = Convert.ToBase64String(signedHash);
    return encodedHash;
}

public static RSACryptoServiceProvider ImportPublicKey(string pem)
{
    var pr = new PemReader(new StringReader(pem));
    var publicKey = (AsymmetricKeyParameter)pr.ReadObject();
    //Pkcs10CertificationRequest csr = (Pkcs10CertificationRequest)pr.ReadObject();
    //var rsaParameters = DotNetUtilities.ToRSAParameters((RsaKeyParameters)csr.GetPublicKey());
    var rsaParameters = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
    var csp = new RSACryptoServiceProvider();
    csp.ImportParameters(rsaParameters);
    return csp;
}

public static RSACryptoServiceProvider ImportPrivateKey(string pem)
{
    var pr = new PemReader(new StringReader(pem));
    //var rsaParameters = DotNetUtilities.ToRSAParameters((((AsymmetricCipherKeyPair))pr.ReadObject()));

    AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
    RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);

    var csp = new RSACryptoServiceProvider();
    csp.ImportParameters(rsaParameters);
    return csp;
}

public static bool VerifyDigitalSignature(string digitalSignature, string contents, string publicKeyText)
{
    var hash = GetDataHash(contents, Encoding.UTF8.GetBytes(publicKeyText));

    bool verified;
    try
    {
        using (var publicKey = ImportPublicKey(publicKeyText))
        {
            verified = publicKey.VerifyHash(hash, Convert.FromBase64String(digitalSignature), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }
    catch
    {
        verified = false;
    }
    return verified;
}

NB: Contis has used Bouncy Castle library in the above code.

Java sample code

1. For Genenating Signature

generateSignature(String strPrivateKey, String strPayload) {

    String signature = "";

    try {

        Signature privateSignature = Signature.getInstance("SHA256withRSA");

        privateSignature.initSign(getPrivateKey(strPrivateKey));
        privateSignature.update(strPayload.getBytes(UTF_8));

        byte[] signatureByte = privateSignature.sign();

        signature = Base64.encodeToString(signatureByte, Base64.DEFAULT);
        signature = signature.replace("\n", "");

    } 
    catch (Exception e) {
        return signature;
    }

    return signature;

}

private PrivateKey getPrivateKey(String strPrivateKey) {

    PrivateKey privateKey = null;

    try {

        String privateString = strPrivateKey;

        privateString = privateString.replace("-----END RSA PRIVATE KEY-----", "");
        privateString = privateString.replace("-----BEGIN RSA PRIVATE KEY-----", "");
        privateString = privateString.replace("\n", "");

        if (!privateString.isEmpty()) {

            byte[] binCpk = Base64.decode(privateString, Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(binCpk);

            privateKey = keyFactory.generatePrivate(privateKeySpec);

        }

    } 
    catch (Exception e) {

    }

    return privateKey;
}

2. For Verifying Singature

boolean verifySignature(String strPublicKey, String strPayload, String signature) {

    try {

        Signature publicSignature = Signature.getInstance("SHA256withRSA");

        publicSignature.initVerify(getPublicKey(strPublicKey));
        publicSignature.update(strPayload.getBytes(UTF_8));

        byte[] signatureBytes = Base64.decode(signature, Base64.DEFAULT);

        return publicSignature.verify(signatureBytes);

    }
    catch (NoSuchAlgorithmException e) {

    }
    catch (InvalidKeyException e) {

    }
    catch (SignatureException e) {

    }

    return false;

}

private PublicKey getPublicKey(String strPublicKey) {

    PublicKey pKey = null;

    try {

        strPublicKey = strPublicKey.replace("-----BEGIN PUBLIC KEY-----", "");
        strPublicKey = strPublicKey.replace("-----END PUBLIC KEY-----", "");
        strPublicKey = strPublicKey.replace("\n", "");

        if (!strPublicKey.isEmpty()) {

            byte[] keyBytes = Base64.decode(strPublicKey, Base64.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyBytes);

            pKey = keyFactory.generatePublic(pubKeySpec);

        }

    }
    catch (Exception e) {

    }

    return pKey;

}

3. For Encryption

public String encryptData(String plainText, String strKey) throws Exception {

    byte[] cipherText = null;

    String strEncryptedData="";

    try {

        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(strKey.trim().getBytes(UTF_8), Base64.DEFAULT));
        Key publicKey = keyFac.generatePublic(keySpec);

        // get an RSA cipher object and print the provider

        final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

        // encrypt the plain text using the public key

        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        cipherText = cipher.doFinal(plainText.getBytes(UTF_8));
        strEncryptedData = new String(Base64.encode(cipherText,Base64.DEFAULT), UTF_8);

    }
    catch (Exception e) {

        if (BuildConfig.DEBUG) {

            Log.e("Exp", e.toString());

        }

        throw e;

    }

    return strEncryptedData.replaceAll("(\\r|\\n)", "");

}

4. For Decryption

public String decryptData(String encryptedText, String strKey) throws Exception {

    String strDecryptedData="";

    try {

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(strKey));
       
        byte[] cipherText = cipher.doFinal(android.util.Base64.decode(encryptedText.getBytes(UTF_8), android.util.Base64.DEFAULT));
        strDecryptedData = new String(cipherText);

    } catch (Exception e) {
        if (BuildConfig.DEBUG) {
       
            Log.e("Exp", e.toString());
       
        }
       
        throw e;
    
    }
    
    return strDecryptedData.replaceAll("(\\r|\\n)", "");

}

PKI Guide for REST

PKI Implementation for GET, DELETE, PATCH and PUT methods.

Contis using Json header payload to create signature for above method which contains query string and path parameters.

Json header payload detail.

FieldDescriptionExamplePresent notation
methodHttp method to be used.
String value in upper case
GETRequired
hostHost of requested the URL.
String value in lower case
betaopenapi.contis.comRequired
pathPath of requested URL
String value in lower case
/v2/healthcheck/echoRequired
queryQuery string of requested URL
String value case sensitive, use same as provided in request.
?abc=123&def=123Conditional
Required when requested URL is with query string.

Use this json payload and create signature with existing PKI solution provided by Contis.

Example without query string

Request URL sample: https://betaopenapi.contis.com/V2/HealthCheck/echo

For above url http method is Get

{“method”:”GET”,”host”:”betaopenapi.contis.com”,”path”:”/v2/healthcheck/echo”}

x-signature:

f75aijG1QrqfkRteTaYtIIUalRQbTQ2IuM5FQEUI7IBocFCkVAOofVZH531j1OyQmKkEr7aQgTDMKQnJeB20jev
irC6nO05lwONGShCE4XxPaAQ31txjY6XcQ8+Boa8C/dKpMatN7ydN7AICH34qAvQY+rx0kRkPZFQ9Los0sM2Dsc5
DG+u16wD9g6KOZZhnqOXEFQbIZwVH9bU4LMXH0ZHP72SrqyLU0VRWrmcgUc3FOyfF7ahVdXRd46AFwNNaG8NlPBj
sPQtjpPWLA1tUfP8m8O9c5IVbr8YxU5ti1DHP3pb50tPIZ944aYJPAfg1lQc435JIiUrHJ3wRpN8rkA==

Example with query string

Request URL sample: https://betaopenapi.contis.com/V2/HealthCheck/echo?abc=123&Def=123

For above url http method is Delete

{“method”:”DELETE”,”host”:”betaopenapi.contis.com”,”path”:”/v2/healthcheck/echo”,”query”:”?abc=123&Def=123″}

x-signature:

f75aijG1QrqfkRteTaYtIIUalRQbTQ2IuM5FQEUI7IBocFCkVAOofVZH531j1OyQmKkEr7aQgTDMKQnJeB20jevirC
6nO05lwONGShCE4XxPaAQ31txjY6XcQ8+Boa8C/dKpMatN7ydN7AICH34qAvQY+rx0kRkPZFQ9Los0sM2Dsc5DG+u1
6wD9g6KOZZhnqOXEFQbIZwVH9bU4LMXH0ZHP72SrqyLU0VRWrmcgUc3FOyfF7ahVdXRd46AFwNNaG8NlPBjsPQtjpP
WLA1tUfP8m8O9c5IVbr8YxU5ti1DHP3pb50tPIZ944aYJPAfg1lQc435JIiUrHJ3wRpN8rkA==

Signature construction for different methods,

  • GET or DELETE

Single header signature is required.

x-signature:

Example,

x-signature:

f75aijG1QrqfkRteTaYtIIUalRQbTQ2IuM5FQEUI7IBocFCkVAOofVZH531j1OyQmKkEr7aQgTDMKQnJeB20jev
irC6nO05lwONGShCE4XxPaAQ31txjY6XcQ8+Boa8C/dKpMatN7ydN7AICH34qAvQY+rx0kRkPZFQ9Los0sM2Dsc
5DG+u16wD9g6KOZZhnqOXEFQbIZwVH9bU4LMXH0ZHP72SrqyLU0VRWrmcgUc3FOyfF7ahVdXRd46AFwNNaG8NlP
BjsPQtjpPWLA1tUfP8m8O9c5IVbr8YxU5ti1DHP3pb50tPIZ944aYJPAfg1lQc435JIiUrHJ3wRpN8rkA==
  • PUT or PATCH

Multiple header and body signature is required and will be separated by . (dot) character

x-signature: .

Example,

x-signature:

f75aijG1QrqfkRteTaYtIIUalRQbTQ2IuM5FQEUI7IBocFCkVAOofVZH531j1OyQmKkEr7aQgTDMKQnJeB20je
virC6nO05lwONGShCE4XxPaAQ31txjY6XcQ8+Boa8C/dKpMatN7ydN7AICH34qAvQY+rx0kRkPZFQ9Los0sM2D
sc5DG+u16wD9g6KOZZhnqOXEFQbIZwVH9bU4LMXH0ZHP72SrqyLU0VRWrmcgUc3FOyfF7ahVdXRd46AFwNNaG8
NlPBjsPQtjpPWLA1tUfP8m8O9c5IVbr8YxU5ti1DHP3pb50tPIZ944aYJPAfg1lQc435JIiUrHJ3wRpN8rkA==
.f75aijG1QrqfkRteTaYtIIUalRQbTQ2IuM5FQEUI7IBocFCkVAOofVZH531j1OyQmKkEr7aQgTDMKQnJeB20j
evirC6nO05lwONGShCE4XxPaAQ31txjY6XcQ8+Boa8C/dKpMatN7ydN7AICH34qAvQY+rx0kRkPZFQ9Los0sM2
Dsc5DG+u16wD9g6KOZZhnqOXEFQbIZwVH9bU4LMXH0ZHP72SrqyLU0VRWrmcgUc3FOyfF7ahVdXRd46AFwNNaG
8NlPBjsPQtjpPWLA1tUfP8m8O9c5IVbr8YxU5ti1DHP3pb50tPIZ944aYJPAfg1lQc435JIiUrHJ3wRpN8rkA==