NAV

Conversation & visitor data encryption

Overview

Conversation and visitor data encryption is an extra feature in our service. If it is enabled you can toggle encryption for any room in your company.

When encryption is on, whenever a new chat session is created, an AES key is also generated and stored to two places. A plaintext version of the key is stored in cache with a 90-minute expiration time. Another copy of this key is encrypted with customer provided RSA public key and stored to our database.

During chat session this AES key is used to encrypt all message content. Every time a new chat log is saved to database, the cached AES key is given a new expiration time of 90 minutes. While the cached key exist, all chat logs are automatically decrypted.

Visitor API data values are also encrypted. The AES key used in encryption is different from the one used with chat sessions. This key is also cached but only for 30 minutes. The timer resets on every visitor pageload. An RSA encrypted version of this AES key is stored as an API data entry with name _encrypted_aes_key.

Symmetric AES encryption

Our implementation of AES encryption uses https://github.com/google/keyczar library maintained by Google. We use 256-bit AES keys and encryption is done in CBC mode.

Keys are stored in json format used by keyczar.

Example keyczar encryption key

{
    "hmacKey": {
        "hmacKeyString": "4PTHmiELMFxA_O8yYTtl6mhdkNBi01TYSUe8vsD6NV4",
        "size": 256
    },
    "aesKeyString": "gIhuVsdgLYnsko6h-3GTIe2PIkB3W0hyUtQYG0A2WUw",
    "mode": "CBC",
    "size": 256
}

Encrypted data is also signed with a HMAC key. The ciphertext resulting from encryption has following format:

Please note that the signature is generated over already encrypted data. Before storing the ciphertext it is base64 encoded.

Asymmetric RSA encryption

After generating the AES key it is encrypted with customer’s RSA public key and stored. Our implementation uses PyCrypto’s RSA module. RSA key has to be at least 2048-bits long. RSA encryption follows PKCS #1 OAEP standard

How to generate keys?

To generate RSA key pair you can use following openssl commands.

How to generate keys with OpenSSL

openssl genrsa -des3 -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

These will generate you a RSA key pair and save them to files private.pem and public.pem. Public key from public.pem can be pasted to our public key settings interface as is.

How to decrypt?

While the AES key exists in our cache chat logs are decrypted automatically. After that decryption has to be done by customer.

The easy way

The easiest way to decrypt your data is to install python packages python-keyczar and requests and use the following script. Familiarity with python and our HTTP API and Public HTTP API documentations are recommended.

First install the required dependencies and then use the example code to implement decryption

Installing dependencies

pip install python-keyczar
pip install requests
pip install pycrypto

Python example

import json
import requests
import base64
import subprocess
from keyczar.keys import AesKey
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

# Use openssl to read private key from file. This will ask your password for the key if one is set.
# If you are not using openssl, or have private key stored some other way replace this as you see fit
private_key = subprocess.check_output(['openssl', 'rsa', '-in', '/path/to/your/private_key.pem'])

api_key = '[insert your API token here]'

base_url = 'https://service.giosg.com/api/v3/chat/chatsessions/'
headers = {'Authorization': 'Token ' + api_key}

# You should replace params with whatever you are looking for
params = '?since_id=399'
url = base_url + params

response = requests.get(url, headers=headers)

content = json.loads(response.content)

# In this case we pick the first chat session in our response
chat_session_result = content['results'][0]

# Chat session API contains three encryption specific fields:
# is_encrypted is a self-explanatory boolean value
# encrypted_aes_key contains the RSA encrypted AES key required to decrypt chat logs
# public_key contains the RSA public_key used in encrypting the AES key, useful in determining
# which private key to use
if chat_session_result['is_encrypted']:
    # This is the AES key encrypted with customer's RSA public key
    encrypted_key = chat_session_result['encrypted_aes_key']

    # All ciphertext is stored base64 encoded, and has to be decoded for decryption
    bytes_key = base64.decodestring(encrypted_key)

    # Decrypt the stored AES key with RSA private key using PyCrypto
    rsa = RSA.importKey(private_key)
    cipher = PKCS1_OAEP.new(rsa)
    aes_key = cipher.decrypt(bytes_key)

    # Use keyczar.keys.AesKey class to do the AES magic
    key = AesKey.Read(aes_key)

    for log in chat_session_result['logs']:
        bytes_msg = base64.decodestring(log['msg'])
        log['msg'] = key.Decrypt(bytes_msg)

    # Now the logs in chat_session_result should be plaintext

    # Let's do the same to API data
    if chat_session_result['api_data']:

        # Key used in encrypting API data is different from the key used in encrypting conversation
        # but the functionality is identical

        # Let's dig the AES key from the API data, it is saved same way as any other API data parameter
        encrypted_key = None
        for data in chat_session_result['api_data']:
            if data['name'] == '_encrypted_aes_key':
                encrypted_key = data['value']

        # Naturally the decryption requires that the key was found
        if encrypted_key is not None:
            bytes_key = base64.decodestring(encrypted_key)

            rsa = RSA.importKey(private_key)
            cipher = PKCS1_OAEP.new(rsa)
            aes_key = cipher.decrypt(bytes_key)
            key = AesKey.Read(aes_key)

            for data in chat_session_result['api_data']:
                if data['name'] != '_encrypted_aes_key':
                    bytes_value = base64.decodestring(data['value'])
                    data['value'] = key.Decrypt(bytes_value)

Decryption before & after

In case you want or need to implement your own decryption app, here are inputs and outputs to test its functionality

Example input

If you implement your own decryption you can use these keys and inputs to verify that the your decryption code works

Decryption process:

Example data 1: Example RSA private key (do not use in production)

-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAw3XH/gCi6AvM19+lMxl1/AqRWq5PxJyWSBT/UMpjrIUlE/ET
e/aZ2skB8YqT+CPVpNY/2+xUnKhnqwZzoDlkw/vsGeEJLaMCsN3eWzO4IdK9cV6s
gf/XfiJIYgd6QCVAtww7hzn83aoKUyL7fWFvN8s0Bi9OsVLXEyEk3ZqeBeoSPv3b
PZsPjmi3cTxdsSRRsYXItrjxNIu8CD6SxHwrswTwknFzldCQ/GgsTMRblJYb80UM
dZLIJeQuXt948jVwU+OttSmld9jExXDIESjeyH0mUidTjg/w4rKi6DXqdfxevSxq
G/pD1101Zmun8WdtlTPD93V5ZGKRM3H3OiqQUwIDAQABAoIBAQCpJiVzuANCwut7
qcxiA1eqTU7q5cycPlKDgMBOn0dQxkyHDGqrLYaJ4sfuytkwtXou00aiQMwcYlSM
meG4N9LvE/WHk8qIlvg+LVM/I+vmQWS2VpL9UXWgb3SqeQQfvN1bS5e8d79P6NZU
igKPx6Ei7JRSAeCc7i64HEe2CxdTz26cSYjyfPDaS952af1VwVBVBCcb8TIyx5mc
iQBt8PWpw+wTlBxdNeU29ofx49beEQpkmNUKDLCRrP24uGtBo/pRnco6rFCX70lU
CYM1hWqdq85C+s50fRZm5e3bc8UP7RotXjj1s653tCGYCCkfIdb7Q7kMvKgX0EVE
ObmuTQfRAoGBAPkxBwU3drXwBTkrg+VMPSFb3yR27XrWV7pQ/v7VIJQ5W5Od0u/a
xE54dWra+SoFIx+X38pVo6qSOeLM/uyRW90FZKynMBZY5EMWZcVOTjFeOd9LAEI1
6ksYyAYJGW1Vy/g9ocAZVdcI16tkbqrV7vWcOsG2qwlN/c07bYkRo4FZAoGBAMjM
7cMiNru0+0CxoBjPpsWizZcwuxlm5KOLOh7wCHjPS3DT2SKxDHTeEmb8ceu6UlXc
2/fh4SyUOz62GYfXeK/btIr/p5UyA002w7t4V5ZLhsXGvYv4JSpF9EA3EHYxpKcb
SK4QZb7klRZ4zS/Yd8uqujPRKCiYiKUzVnEg1l2LAoGAGHunq+yKHlAJw1cyK0o1
S1zCbMu+g24hym2A0DrSa53DEJrWCbjdxrPxHWGMT4OXstfyJegKH40kYNMxTL7v
XqTUAl6ey+NJ8gl2UE9kQVd3qietS9QOQ/iYVnd4ZxSh6K7tgHk8xNbakb1ZXjGM
u9bwsu2mmlBuWka3eVshTGkCgYEAt+7/3D764/ZkMF6EyDVuDpuSt2tr91jJFwXx
t2UoXYGFGVT1EVIclvDU6oFT2RpTUVN+KxI9sRgXLNqhiE1Xv78BIlIMsB2f6JrB
sHgLwyBr07644f3sDU0G78uAyMOwj2HusYNTsAiyiGMfkTQ4h9dbuRteLdGLWcgs
5nOt6QECgYEAjFEXz78FC9di3jBCbdVHehyzF9aIzEa9+5eqBza2FqFpeAX29wuE
yG245fwb5DmMPl8V33+SdZWpa38W8ruG265Q+cOXkHVhO+h03tQnmiJAzUDE8OpS
hqk8XHdtcOmpX3Zn5i6ewuDewTmfJu6jPcdTsboGPFD0YGf2NpvL7j0=
-----END RSA PRIVATE KEY-----

Example data 2: RSA encrypted AES key

MyBY7CGGixwKYM88/CBvGT2cN7v3yzldk+YkYjwB92Nyk41cQyFf0eGFZ9DmVXQ0NbACan4v71+z
ulothiDQVQ8I94xTgJmRK16Vu0FhFYfW15grYmVMnnPG9pp0PUFIMd3VWZHKMkkBVmX9l02Irzne
MunalUjKQTCO5HZaJTDIbAIsRiFKAw0bQZyvv5X5WDY1s888WRYXQikJRwqjKYxKqncd6F9dy+6a
0pzNe+ZUNOKST6dc4phWCoxxGXgWcZNG1eTagzM9WScLvJPwkYawQi385oENPzjLU3P477pVjFDm
Hd3KZvEHjJ6IFUgm6FNxee6XsxiYXITSPVxJ0g==

Example data 3: Output RSA decrypted AES key

{
    "hmacKey": {
        "hmacKeyString": "Wf7oZEWQTpspt0g1ZISN0SNFS21w5DdVOp579RqBg20",
        "size": 256
    },
    "aesKeyString": "dIz4N3wn4V3RfRjyteWRS5_fkXzuo8fFP1ZLMgck_wg",
    "mode": "CBC",
    "size": 256
}

Example data 4: AES encrypted message content

AJxNkp3ZS9McVTYPDyckR7ub7ri5oIObzl0a45t13owvG8ouGfAdfZiosw4hxCgc1iYg88v1xZRL
Ff0/UJr/CcqfCzJjSA4ZWg==

Example data 5: AES decrypted message content

The Line is Open!