Message Level Encryption

Message Level Encryption allows you to store information or to communicate with other parties while helping to prevent uninvolved parties from understanding the stored information or understanding the communication.

Generation of public and private keys

The encryption service is based on JWE and works on top of SSL and requires separate key-pairs for Request and Response. Merchant and KUNA Core share public keys to encrypt and decrypt data from each other.

  1. Merchant (your) key pair: The merchant (you) encrypts Request (message payload) using the KUNA Core public key and KUNA Core would decrypt the payload using the KUNA Core private key.
  2. KUNA Core key pair: KUNA Core will encrypt Response (message payload) using the merchant (your) public key and merchant would decrypt the payload using the merchant (your) private key.

Diagram of the interaction between merchant and KUNA Core with MLE

Click on the picture to see it in full size.

Click on the picture to see it in full size.

Generating keys with OpenSSL

openssl genrsa -out my-private-key.pem 4096                                     // generates a private key

nano my-private-key.pem                                                         // display private key

openssl rsa -in my-private-key.pem -outform PEM -pubout -out my-public-key.pem  // generates a public key

nano my-public-key.pem                                                          // display public key

Implementing MLE in API calls

Secure authentication

Detailed description of authentication via JWT token or API key is described in the "Authentication" guide.

Encrypt the request body

Use the public key to encrypt the body of the request and pass it through the token field:

const nodeJose = require("node-jose");

const email = "YOUR_EMAIL"; // put here your email
const password = "YOUR_PASSWORD"; // put here your password
const merchantId = "YOUR_MERCHANT_ID"; // put here your merchant_id
const publicKey = "YOUR_PUBLIC_KEY"; // put here you public key

async function encryptJwe(publicKey, data) { // encryption function
  const key = await nodeJose.JWK.asKey(publicKey, 'pem');
  const payload = Buffer.from(JSON.stringify(data));

  return nodeJose.JWE.createEncrypt({ format: 'compact', contentAlg: 'A256GCM', fields: { alg: 'RSA-OAEP' } }, key)
      .update(payload)
      .final();
}

const url = BASE_URL;
const path = "/v2/auth/login";
const body = {
  token: email,
  password: password,
};

encryptJwe(publicKey, body).then(jwe => {
  const options = {
    method: "POST",
    headers: {
      accept: "application/json",
      "Content-Type": "application/json",
      merchant_id: merchantId,
    },
    body: JSON.stringify({ token: jwe }),
  };

  fetch(url + path, options)
    .then((response) => response.json())
    .then((showResponse) => console.log(showResponse.data));
});

You will receive an encrypted response from KUNA Core:

{
  "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJ2MnBVZlVVQ3kwY3lRQzFwcGNTdDdoX3o4aVVZX2RkLWxOMld3Z2lfd2VBIn0.A7wMRSak_2JOH_yOUvhIIoq6Qptcr02JrTvjFycW11Hde6mxPUpoGjUZcr9LqaG4gqU8RuLlI76PuQCklP1lmFu_tfpwxqGAdlWQMxrvgYABVFR8O9iG2UxshP5qqGepFrX7O74DRVbdxeDBGMQDwpdH_M1LcnBFZj_49-REUArEGp26wGC4rs3M-Af2mdP_2QJi3yCDRdWTvJ5UONPSUvGCb-3LYao_-RwQJVl_Y6ieYuHWfPA-1qKbVNgz1miQBhl5aUZE7dmtTkRS1vndXhcSM_rMw50sEMzVGy-X3BKm7otBQNpzNupiEUparXSNM_0hdw7P2cfRJl8Atr8PDxQ8_-A1rlUOJtoD8KPP8Z_pmG1uEhjl_XJZvlJPv0iQ6iTk8-vBN81-ufyOAEspbhTcZ1Cbqm_CpY7jH34DOLuAecTmRKq0bp4vkML-dyMUt-cq3AYVv0TPNUUY3-xwXV-D7dqqGGxc2TgejwVCD64isbqR7vcsxJf1xtm1o3QdmDwj6j9GkItY72i8AgCef4TM2R09SiQYuJCDaLySnw-onHzN9Io9Z-merp1Bq9M7fzx6wTrxc1sKrIVNaL_gpYZBOAo7fZ_kxVMbvI7-YQFkfZEdlCBXrO2egkKzKmVQPDUwKGfN_85yCRvf4D3ubgah-kMvj-A7fnD76UeLVO0.UNSN7GCNZ1E-cVqO.aU_gVhnUiwqN1hBunSC6_wrNtaCf3mWfySqpEfUQcFFOpOcOBTqxmYdsWbd3HPBYJ8wi0s3OjzaH0jqn2KlM_6qoinohiPB5P8LZAw1r63kKOnRQ1qd01M3UdpCxB9O-pQvOaaf7NOO-VOT7Pq0KrDd65qgUvIKULlFsmY0Tb48EYaIdChAdJ7UXDsuZznm9VKs_aW4hlrKF75xJvOqUOcanwdu2COhkYP13tLoLw-RrLfU2-gVXQHsSZzCtT6zfCv-zJnJzq9HNOupRftg2UB9xzyMgkSFuOKaW6rgauDIcjP3ssb49C7An5Wf28RbA8-RS-FGwGmRGUsmiW9w1ONfWnpVOpiIEjIz9tSbFaPjQX9syzTDmF7lf2sBc77qJrFtKW73vlV9Pobsekh2rxYuPS09RGxtyF9pwfKWm7dXuMkPrRhVDWvMsKpRrfNktpeZok1V_xxM6_eGF0WGm_Mhpa7r78IBul50tft_Jk_df7vbfCi25AhTWmH1Foc8KFnqX3oP-bsPdgYUOsBYBxKeI52BklfV6OMhcM4e7NMH0Qa_ShU4YNM5sEMlXVWJBr4pnhCTG6q2EF1WODr19BFzxjRn8E2P4MaUjn8yGuZ92LTw8uSdl13szkxiwNszPqZyAR-A2vElSiM9fyBMNzNGc9M2gG7c7gnxCowTEzwz74yhyIcdkoRlVNXV1OaKKPlrFZGUlNxhCpSqMLyb2WdXlKHTN9NiK75pU6W6052zDzUdxlNFp0E-iSp4fTdt_Npfrpx6xMrnVGn8E9f8YAIH7kcofSoXcY7KwDYO55p0AkT7otbSovO-XA7EDAwDHipKN6FxhmjNdO45X9IPWerOjg6aq-iVK8SVlxldUAsOug9_t83c5P4l1cAp1Jab8IvDE7eELUGaDe7TToen_eo09xC8ifnkHRTazgo0zDTPBH9tvrudKJP6WJBOZo4rOFy_oErh8_T4YE27iiOZ4lur92vRaVNHNagMuFftvCJYUfr8rWOqQAxuc2lD2mD6qeYSV3OWEFmm12iYYhRePdEjmSviV0YRObcGZXAwMyPXKeb5B-2GmlOggxVJCL8ry6XU-AxU6g4FoWeOgdJYO2fg1VA7Tk9JqKBt-U4pzMlCv5K0ms0tR-NUpgCU2Pp3uKgouD7FcAEHytuKOSGWDNNY_HHieyTdcBqJwiNXpEQj85kaOCclWw-iQdj_rxus6WkLyjCs3k-OkwcV_nXQ9lWRGJKXuVnGWZRMtxKh8KBOgNjU7DhUqYwImMEt00U1eQpZ18L2ljAxMwg837cSy8uLcdPpLqAbUh5zfrJx2qcSg9svwf7uwgg6MCnNPSdVwaONHKAcDEcQNQzPD0mIhJJckLM4eZqVdf2rxPKRQPn6BOm2KwuW8FQOGhT07lwjVcGYn95ptl37G8tGw6J1YaDWZVK5M53zcnYw0Z4h7uf1Hula97xjLW006KgsS3Mn7MMmnE_tdi4c627MRqzHkjr4Xt_a5unN_n6axeBx7AscC0Slc3vTkJUggQBFUg07rrfR2FiXY_biDcTGZCEByDAIWfIrjS-yy1V5tzVreW8R5CAHP6mhCKYRu0EpSEmVv9WBv9YQWM20qsslYBETziRtWRF_sozPoy5AApCJ16M2ApaVEEliQVLkeX7CtJ5o5rQcxstR5oXzIVhDNZFo5W78_Vpv86BnzNzZz76ZIwvOBEglthV2h2t7w_UuURsQHbxa7Rtq4-kJTYHd4N1-Xy-9k-JGzZP1sjPWf6RDiHW0A4fynI_VO3JAjC-y6WUUjvMyv9ealMZ95ino5rr4-L-_w37DX9jw3GKwq3pUJr6QOBPXKwRrqVt-uSTEFutUTqSC8tuidiCfkAUgSrzR_Ij82wOGofkGAJeE4sH5I9-xeIRmpT-4X7F_AcL2tUj2H4xHuVh32ZkidofKQJGj5YqIZeXJ6kPQrOvfRt4EsNDuP4Im1knnTHtWy21X_TwOr1yWlMJjmajrDY5y7kLPRjdiSqYWDc51mx_2q2Rhlv27wtbaZd-mUVcICLP5xblGjS219pX2vwiWYvjUa_bjn-BMKIY7wSGnsmCm1kWwO4Lc_r95dMN7JqB-H_BPZMxpBsf72zRzdnsU9OTc_c7c160qdarDbNSVlR4QjOYIuKYXKBZtlrQGPjzMpTnltLaY627c5UiSPj5mLNOyyi3JnnlaHSTrjHxPWQ_80Ps5dj2yTAYiMa0njOVhPmZHOXDpSEujBYlApzgMIvAPuyiz_8qtv-_lYdrKfmoQjch8JgV5in0Lo7FbSXFvfMUMmD14gtdJy8rxAZPLI8ybnjXdsQzJScLKl7x33M-jnESrz4tTsSaJpvyPLcn2MFgFOmeukmNjBkytkNlT-a4DjDOsYT2_j123p20C9Nmlvc_zWQPHc71hMDO6IVSa3oEl-YiCmH8tXQifgQ-NNzTvI0lsOtxPzyyiNvOEz__jyiXEBiOIJBLchmBZfNZSaY52nXYYWIECqx2MBURNuxd3Hm8c_IN2IyznGOf6fF38AoPFuA1G6bFvIjaPm4rWw2gxmpnQAnNPbLEPtf3roodZg-r9OVONtpjR1ShIInTNKYa0FBGXp39moglP-2DypYZZ4_VdXwqMdKhi6Yfbep67xy497oQls5A6mDWdwmgnLwpqyQOPskwDRA5IVe0bR6s9QMBvrvIqELzTALlgWeWYBy-HXBd9OS8KOY-E-wM7XzKRyQM_MFSoyN6JTLCsKshUAA7hPb5HS2BaBjUNVmIhOu8_xBs-URhfuSiM9yhB8TCL6g36FexFn9J7cvH1TFJCFXGrFrJTqmiWPW2ghsMi8MhaA4cXMUUHCb4STCDpCEZnOkQerwGFRCKGoUESIXQPooJ2vNnusqqFwyXQSW0Zk-oluNFKY5FCSaxafS-KdHudBd0QESv5-8eUP6zmgVM2pcOT4LglZ9Flua3RY6LeH2JB5VdobG_b3wPT85YunZpqPWh4R7nz8N9nrGpBiqURC1osflun2o97pouMo0DOVlRJHl4ySJJREF60RmcQWjn6wcIR93N_1CQGEbP-KODgBEamhALrXvnfSJnggJExoKQpLvOiumXTPhUvvE7A25qmNGnQPjIvJ3wWnuAyoumYqWpG9yz0_LRHHqGrUBwgPO6U844FeZyWXaXUAd-wDYxCm6hQtPUecRVMpDsfdyy-EKm-rTUxU-gorUVLslBfTPFmb2oNjOHU2B6BzNEaZqvJBM_ybGB55T-UV7uoFFx7KFjKWXlFFd3_ifGo4Pq4J7vTqjQmAk_ElKjf8e4KOIFi8incKSnPd4JsNYl6pgB7U3CHcxte5t4QTci2hPZuZ3wc3WIzRPygJYjS3zZ50xg0crngm81cwqaJziBwhAaAy6JbZjQjoDBAuGAGrTwNQkax5KNe5Duf_HfC8DciyXv9nOy_DyknIRc25NlpNxmCMLXEndG2pBmZ2IUUhlKnFQBuwYeyScszLcGYA-kSinNdVfCcan24Hau8Z7xTnj39JfWvoyQUGx8GLuFN8xRtyjGnBHKE2_4pF0u9_alnUQpvkN9nQvZTTsiG5jf7ZlOxzNPjQOSlay2GW5M1MbGnjcqFlYM7rrPMHlzZDn2OykGq4ZvuJaxQ829ikVfa-E0Hh64dpB4AaCqltc7-hi9DZoHd7zT9ELynraQeHFzTlv06y3VcisvhjByBIN4t4aFztRqvDkGO0oqISB0-XRZfLO8uLBVPucjxlRdc4ggPC_v9DGoO4lYmj4Gf5knDzGY8iUt2b-6DvkNZxOn4_bbFlr9RJPuxH7_6DvNaDuirOqlki_STvFICu3UWt_JHBMCAunlrAoqNN6c4cIyEnaOyKkpZs0WXm3b_e9ApsnAFJB72E.IgGqvLg7GHe9LbuyhVfznA"
}

Decrypt the response

Use a private key that will allow you to decrypt the response from KUNA Core:

const nodeJose = require("node-jose");

const privateKey = "YOUR_PRIVATE_KEY"; // put here you private key

const kunaCoreResponse = showResponse.data; // encrypted response from KUNA Core

async function decryptJwe(privatekey, jwe){ // decryption function
  const key = await nodeJose.JWK.asKey(privatekey, 'pem');

  const result = await nodeJose.JWE.createDecrypt(key).decrypt(jwe);

  return {
      headers: result.header,
      payload: JSON.parse(result.payload.toString()),
  };
}

decryptJwe(privateKey, kunaCoreResponse).then(result => console.log(result));

Example of the decrypted response

{
  "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Ijc4YTEyNjQ0LWExMjMtNGMyYi04ZTJmLTIyNWY1NGUyMGI4NCIsImVtYWlsIjoib3duZXJAYWRtaW4uY29tIiwicm9sZSI6Ik93bmVyIiwidXNlcm5hbWUiOiJTRUVEX0lOU1RJVFVUSU9OX09XTkVSX1BST0ZJTEUiLCJ0IjoidSIsIm1lcmNoYW50SWQiOiJiMzQ1NTViNy1kY2E0LTVlYzEtYWRlMy02NGEzYWQ1NDUzMTAiLCJpbnN0aXR1dGlvbiI6eyJpZCI6ImEyNjliYjI1LTk4OTgtNDJiNy1hYTkyLWMwZTdjMWE4ZjQ5MyIsInRpdGxlIjoiQ29vbCBDb21wYW55IiwiZ3JvdXBzIjpbeyJpZCI6ImJhbmsyX2RlcG9zaXQiLCJ0eXBlIjoiRGVwb3NpdCJ9LHsiaWQiOiJiYW5rMl9leGNoYW5nZSIsInR5cGUiOiJFeGNoYW5nZSJ9LHsiaWQiOiJiYW5rMl90cmFuc2ZlciIsInR5cGUiOiJUcmFuc2ZlciJ9LHsiaWQiOiJiYW5rMl93aXRoZHJhdyIsInR5cGUiOiJXaXRoZHJhdyJ9XSwicHVibGljS2V5IjoiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBNjhSNW9oMUYwdXJEUWZVVGtGQlhcblVSSWt5VklRN1dHeTAzMGJiMHgxMFF4ZElPZlZnVEFNM3N6MTZqOXZVR1ZCd2dxUExjZXF5RFdpTk03MEF1VFdcbjZqSnR1VWF6Z2JjMzhiVTU4Rk5FRU1mNU9SN2o5Q1dEYlVwT2pqbTN3M2tFUisraGxROFZlN1oyUnJoWnphMWhcbkV3anl6M0xxLzFqWmhjVlNPK3doY1Z4SG5mZmcwei9iTS9QUG5hVmdHZGFyTm85a1ZGMll4d205SVR2N0xLVG1cbldEV1JvSlRCUmNqS0RRQ2VSMmNONi9VaVhoNU8xMC9JVHA3S0hOOGlUQlExVnA5VG04Y3l5RVJOdVVSbkJBY1lcbncyaDByT09Yb1Z1L3p3NzErR1RNNVMwYlI5Y0twa2pXQndMdnRHUFlnOTRua0FFNkVKM0hMNHhLVmsvRVhpczRcblRtYlFRWjFlZjMzZzJIVzFHS1BtTDQwTUFwY1BVekdFV1YzeExGY2F0NGd1VW9ZSnp3Yjh5Ny9oTDFnNTFrYVhcbmNoaWRyWjMzLzRKTG9Ya0NBbThBMjUrVDFjNUNuNVBKV05rRTFxcTNBMS9MNkRiTjFROWdJd2lLNm9jVTcwemRcbnYxNlZRUHFsRzIrTnpHN0hOOWluZ3pzNE5IU3ZmcUpHQ0x1MzlKNjJUODFUNUJ1MlZicFV2K0dSN0h5b3lHTEpcbkRnSUpCOFBUeUFNQWlkeUJBRG1IanhjcjJmWHRJdXZKVWVkb20vOW5EZzB1UStnTVp4YnNLMldRVlBRL1JvemVcbldxTmFkdlpuRzVwYnA2L0ZVQ1ozYmZjL3pJeDg3OUhaT3RRdDhPMi9ZeUJ3R0dIdkF4UGFqTTBFQThaSUhJWUZcbnZRMTBVcGhsSmlWK2lZQ1UyNGNieURjQ0F3RUFBUT09XG4tLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0ifSwiZmluZ2VycHJpbnQiOiJkN2I2MDhlZTYzMDQ5MWYwMTE5MDhlZjkzZjU4ZDE0NiIsImp3dElkIjoiYzYzYjE1YzYtY2FhMi00MWJiLTkwMWUtZDUwODk4YzFkMTM1IiwiaWF0IjoxNzExNTQ5MTIwLCJleHAiOjE3MTE1NTA5MjB9.jJI-hXkcpbrxwJ9-G5oZZvb6Ko_xanPxUjQSDcYzGrfSQlrgSZKUCIECQVoH2-0uvQKjkw5Bfy19xddtmLn2CwTQRsXoqGEd5ImJzeuxpEvB0VNhZJ0Vnry36PmLuNk0PYm5L1wEevPAK4iJT-OXUjZm23Nr8ORvCTqPn6hybh6EEL3ey-jojb2I_xMGAwsRAylPDKo0ggx_c3aLM8JuruHsDTxVaVuNVMRyY_PCmkHT_TQQOUAXn_l5SoJnQ9ie5fG5N9F_aPLDPFmL9LIWcct049YDr7sNO0JUJStXYQssgxMf_e6JbMZxCD5Sn8C95MrvUgf3YTAnPaxhNq5Unw",
  "expiresIn": 1711550920,
  "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Ijc4YTEyNjQ0LWExMjMtNGMyYi04ZTJmLTIyNWY1NGUyMGI4NCIsImZpbmdlcnByaW50IjoiZDdiNjA4ZWU2MzA0OTFmMDExOTA4ZWY5M2Y1OGQxNDYiLCJzZXNzaW9uQ3JlYXRlZEF0IjoxNzExNTQ5MTIwMTM3LCJpYXQiOjE3MTE1NDkxMjAsImV4cCI6MTcxMTcyMTkyMH0.SUjEQNVrjp8hQ63FGEi9DrVieLSIW_3rjWmWzAbIrra9yo68uk0GpCCzp3NZPdpaIJGKatYYKFQ0OTobceBNWs1NI1I7G_s8IJyt7Ez1w0UW5xrFSSlplB7a2vCh99kdl0uiCnDRIrlNYq6rSF-pPyxF_C0K480M_1_fNTJrSv061feKLTZzgMGCOVFsaPBq0IjcmJ0kaN8ztk5KymEJ6sQXmvxJhZzDo2tJikcsyE3A7L9O3cA75Uhdy4yZl2m-eD6R-NViI8B0QNghd7qHD8U9arr1mFF-x_-evbX6qS1qRbRo5KM1RAzE-7ZpY1cSjLk2U4dersF294UNBT0CdA"
}

👍

MLE is available for all KUNA Core endpoints.