Sending without prior setup

This section shows you how to setup a sender and a SENDER_INITIATED_RECEIVER. We assume that you have completed all steps from the continuously_receiving_data guide.

Note

The following guide assumes basic knowledge of the bash shell. In order to execute the commands, you need to have jq for parsing JSON installed.

Warning

SENDER_INITIATED_RECEIVER services give the users of an app for example no way of verifying the authenticity of the public key used. SENDER_INITIATED_RECEIVER services should only be used, if no prior setup (for example using a QR code) is possible. They are ideal for collecting non-sensitive data such as anonymous analytics data.

Creating a receiver service

  1. Open a shell window and type the following commands to create a RSA keypair and to show the public key.
openssl genpkey -algorithm RSA -out receiver-initiated.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in receiver-initiated.pem -out receiver-initiated.pub
cat receiver-initiated.pub
  1. Open the Linker Service view in the datalinker console.

  2. Click on Create a new Linker Service.

  3. In the dialog, you can specify the properties of your linker service:

    • Specify the properties as done for the AD_HOC_RECEIVER in the previous guide.
    • Select Sender-initiated receiver as your linker service type.
    • This will make the public key field visible. Copy it from the shell window and paste it from into the field.
  4. Click the save button.

_images/my_sender_initiated_service.png

Creating a receiver client

In the sending_first_data guide we used the datalinker API to create a client for an AD_HOC_RECEIVER service. Now we are using the datalinker console to do the same for a SENDER_INITIATED_RECEIVER

Note

Clients for RECEIVER_INITITATED_RECEIVER and SENDER_INITIATED_RECEIVER services can only be created by a datalinker user with access to the respective linker service.

  1. Open the Client view in the datalinker console.

  2. Click on Create a new Client.

  3. In the dialog, you can specify the properties of your linker service:

    • Specify a strong password. You will pass it to the script later so you won’t need to remember it.
    • Select My Sender-Initiated Receiver Service as your linker service.
  4. Click the save button.

_images/my_sender_initiated_receiver_client.png

Sending data

This section quickly explains the steps for sending to a SENDER_INITIATED_RECEIVER service.

Note

Remember, that a linker session between a SENDER service and a SENDER_INITIATED_RECEIVER service has to receiving client attached to it. Instead, the information for encrypting the documents will be provided by the receiving service. As a consequence, all sending clients will use the same public key for encrypting the symmetric (i. e. AES128) key. Conversely, all receiving clients of the SENDER_INITIATED_RECEIVER service will need to have access to its private key.

  1. We will reuse the code for logging in our client and creating a linker session from the continuously_receiving_data guide.
#!/usr/bin/env bash

BASE_URL='https://developer.datalinker.io/api'
PASSWORD='test'
SERVICE_ID='com.myorg.sender'
TO_SERVICE_ID='com.myorg.sender-initiated'
DOCUMENT_TYPE='com.myorg.my-document-type'

CLIENT_ID=$(curl -X POST -s -i --header 'Content-Type: application/json' -d "{\"linkerServiceId\": \""${SERVICE_ID}"\", \"password\": \""${PASSWORD}"\"}" ${BASE_URL}/clients | grep Location)
CLIENT_ID="${CLIENT_ID##*/}"
CLIENT_ID="${CLIENT_ID//$'\r'/}"
echo "clientId:\t\t$CLIENT_ID"

JWT=$(curl -X POST -s --header 'Content-Type: application/json' -d "{\"password\": \""${PASSWORD}"\"}" ${BASE_URL}/clients/${CLIENT_ID}/authenticate)
JWT=$(jq -r '.id_token' <<< $JWT)
echo "jwt:\t\t\t$JWT"
  1. Instead of joining an existing linker session as in the previous guide, we will create our own, indicating the SENDER_INITIATED_RECEIVER service.
SESSION_ID=$(curl -X POST -s -i --header 'Accept: */*' --header "Authorization: Bearer ${JWT}" --header 'Content-Type: application/json' -d "{\"clientId\": \""${CLIENT_ID}"\", \"toServiceId\": \""${TO_SERVICE_ID}"\"}" ${BASE_URL}/linker-sessions | grep Location)
SESSION_ID="${SESSION_ID##*/}"
SESSION_ID="${SESSION_ID//$'\r'/}"
echo "sessionId:\t\t$SESSION_ID"
  1. Next, we retrieve the public key without verifying it.
PUBLIC_KEY=$(curl -X GET -s --header 'Content-Type: application/json' --header "Authorization: Bearer ${JWT}" -d "{\"clientId\": \""${CLIENT_ID}"\"}" ${BASE_URL}/linker-sessions/${SESSION_ID}/publickey)
PUBLIC_KEY=$(jq '.publicKey' <<< ${PUBLIC_KEY})
PUBLIC_KEY="${PUBLIC_KEY#\"}"
PUBLIC_KEY="${PUBLIC_KEY%\"}"

echo ${PUBLIC_KEY} > public.pub
  1. All that’s left is to actually encrypt the data and send it. Nothing new here, just for completeness:
RANDOM_PASSWORD=$(openssl rand -base64 32)
KEY_IV=$(openssl enc -nosalt -aes-128-cbc -base64 -k ${RANDOM_PASSWORD} -P)
AES_KEY=$(echo ${KEY_IV} | grep key)
AES_KEY="${AES_KEY##*=}"
AES_KEY="${AES_KEY//$'\r'/}"
echo ${AES_KEY} > aes.key
IV=$(echo ${KEY_IV} | grep iv)
IV="${IV##*=}"
IV="${IV//$'\r'/}"

NUMBER=$(((RANDOM % 5) + 1))

i=$NUMBER
while [ $i -gt 0 ]
do
    CONTENT="{\"number\":$(($NUMBER - $i + 1))}"
    echo "content:\t\t$CONTENT"

    echo ${CONTENT} > document.txt
    ENCRYPTED_CONTENT=$(openssl enc -nosalt -aes-128-cbc -in document.txt -base64 -iv ${IV} -K ${AES_KEY})
    rm document.txt

    openssl rsautl -encrypt -inkey public.pub -pubin -in aes.key -out aes.enc
    base64 aes.enc > aes.b64
    rm aes.enc
    ENCRYPTED_KEY=`cat aes.b64`
    rm aes.b64

    DOCUMENT="{\"documentTypeId\": \""${DOCUMENT_TYPE}"\", \"parameters\": {\"key\": \""${ENCRYPTED_KEY}"\", \"iv\": \""${IV}"\"}, \"data\": \""${ENCRYPTED_CONTENT}"\"}"

    DOCUMENT_ID=$(curl -X POST -s -i --header 'Accept: */*' --header "Authorization: Bearer ${JWT}" --header 'Content-Type: application/json' -d "${DOCUMENT}" ${BASE_URL}/linker-sessions/${SESSION_ID}/documents | grep Location)
    DOCUMENT_ID="${DOCUMENT_ID##*/}"
    echo "documentId:\t\t$DOCUMENT_ID"

    i=$(($i-1))
done

rm aes.key
    rm public.pub

Receiving data

Again, there is nothing really new here. We will just check for all linker sessions of the particular receiver service.

  1. The login remains the same:
#!/usr/bin/env bash

BASE_URL='https://developer.datalinker.io/api'
CLIENT_ID='8adc1fa3-8372-43b4-9316-5b455cebd828'
PASSWORD='somethingverylongandcomplex'
SERVICE_ID='com.myorg.sender-initiated'

echo "clientId:\t$CLIENT_ID"

JWT=$(curl -X POST -s --header 'Content-Type: application/json' -d "{\"password\": \""${PASSWORD}"\"}" ${BASE_URL}/clients/${CLIENT_ID}/authenticate)
JWT=$(jq -r '.id_token' <<< $JWT)
echo "jwt:\t\t$JWT"
  1. Next, we will retrieve a list of all linker sessions for the $SERVICE_ID:
SESSION_IDS=$(curl -X GET -s --header 'Accept: application/json' --header "Authorization: Bearer ${JWT}" --header 'Content-Type: application/json' ${BASE_URL}/linker-sessions)
SESSION_IDS=($(echo ${SESSION_IDS} | jq -c '.[] | select(.sessionId) | .sessionId' | tr -d '"' | tr '\n' ' '))
  1. Finally, we’re left with not only iterating through the linker session IDs but also through all documents of each linker session:
for SESSION_ID in "${SESSION_IDS[@]}"
do
    echo "sessionId\t$SESSION_ID"
    DOCUMENTS=$(curl -X GET -s --header 'Accept: application/json' --header "Authorization: Bearer ${JWT}" --header 'Content-Type: application/json' ${BASE_URL}/linker-sessions/${SESSION_ID}/documents)
    DOCUMENTS=$(echo ${DOCUMENTS} | jq -r '.[] | @base64')

    for DOCUMENT in ${DOCUMENTS}
    do
        DOCUMENT=$(echo ${DOCUMENT} | base64 --decode)

        DOCUMENT_ID=$(echo ${DOCUMENT} | jq -r '.documentId')
        echo "\tdocumentId:\t$DOCUMENT_ID"

        TIMESTAMP=$(echo ${DOCUMENT} | jq -r '.timestamp')
        echo "\t\ttimestamp:\t\t$TIMESTAMP"

        DOCUMENT_TYPE_ID=$(echo ${DOCUMENT} | jq -r '.documentTypeId')
        echo "\t\tdocumentTypeId:\t\t$DOCUMENT_TYPE_ID"

        PARAMETERS=$(echo ${DOCUMENT} | jq -r '.parameters')

        ENCRYPTED_KEY=$(echo ${PARAMETERS} | jq -r '.key')
        echo ${ENCRYPTED_KEY} > key.b64
        base64 -D key.b64  > key.enc
        rm key.b64

        AES_KEY=$(openssl rsautl -decrypt -inkey sender-initiated.pem -in key.enc)
        echo "\t\tkey:\t\t\t$AES_KEY"

        IV=$(echo ${PARAMETERS} | jq -r '.iv')
        echo "\t\tiv:\t\t\t$IV"

        ENCRYPTED_CONTENT=$(echo ${DOCUMENT} | jq -r '.data')
        echo ${ENCRYPTED_CONTENT} > document.enc

        CONTENT=$(openssl aes-128-cbc -K ${AES_KEY} -iv ${IV} -d -base64 -in document.enc)
        echo "\t\tcontent:\t\t$CONTENT"
        rm document.enc

            curl -X DELETE -s --header 'Accept: */*' --header "Authorization: Bearer ${JWT}" ${BASE_URL}/linker-sessions/${SESSION_ID}/documents/${DOCUMENT_ID}
    done

    if [ -z "key.enc" ]
    then
        rm key.enc
    fi
done

Sample scripts

The following two scripts implement what we’ve just discussed. Both scripts have been extended to run forever. You could still improve them by stopping the sender when the user presses a key.