Columba Jacobi

Days-off Scheduler


Back-end integration for the iOS app

Overview

When posting schedules to a URL for back-end integration, the app offers an optional hybrid encryption mechanism to enhance data security:

This mechanism protects your data during transmission, making it unreadable to unauthorized parties. To use encryption, you must provide a valid public key for the receiving server, which is securely stored in the device's keychain.

The sections below provide guidance for server developers and integrators.

Posting the schedule in the JSON format

Back to Top

When the JSON payload is sent in the request body without encryption and authentication:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
application/json

The user-agent header is system-generated.

The request body will contain the JSON data.

When the JSON payload is sent in the request body without encryption but with authentication (for example, Username: User1, Password: password123):

Content of HTTP headers:
authorization:
Basic VXNlcjE6cGFzc3dvcmQxMjM=
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
application/json
php-auth-user:
User1
php-auth-pw:
password123

The app includes the Authorization header with the value Basic VXNlcjE6cGFzc3dvcmQxMjM=. The server decodes this value (Base64 decoding yields User1:password123) and extracts:

The request body will contain the JSON data.

Server Behavior (Example: PHP-Based Server Application)

When a server-side application is running on PHP, the server processes the Authorization header, decodes it, and populates the php-auth-user and php-auth-pw headers for internal use or debugging purposes.

When the JSON payload is sent in the request body with encryption but without authentication:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
x-original-content-type:
application/json
content-type:
application/octet-stream

The data in the request body will look like this:

{
"encryptedPayload":
"j55/484d2/yg72NobP4pHVPD/6O0uWVux7DbO58AAVn7+pFBt . . . NQvx9yG",
"encryptedIV":
"BCU/RSoQ2/r8tkU . . . S5+NSRg==",
"encryptedAESKey":
"AmqqgFCscIJULf8 . . . NZhu1Sg=="
}

When the JSON payload is sent in the file without encryption:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
multipart/form-data; boundary="Boundary-BD268869-F928-47D4-AF68-02956E4C5B72"

Form values:

postButton:
Upload File

The file will be uploaded as 'daysoff.json'. The name 'daysOffSchedulerData' with the filename 'daysoff.json' are specified in the 'Content-Disposition' of body prefix string.

When the JSON payload is sent in the file with encryption:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
multipart/form-data; boundary="Boundary-8110F0F0-FBB8-41DD-9059-2DA95FBCDF33"

Form values:

encryptedAESKey:
tUNY6jFM/V . . . APi0zQNXgQ==
encryptedIV:
MndUgctYcb . . . 0hQyjYx+Qw==
postButton:
Upload File

The file will be uploaded as 'daysoff.json.bin'. The name 'daysOffSchedulerData' with the filename 'daysoff.json.bin' are specified in the 'Content-Disposition' header within the body prefix string.

Posting the schedule in the HTML format

Back to Top

When the HTML payload is sent in the request body without encryption and authentication:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
text/html

If you use the HTML options 'Include HTML and BODY tags' and/or 'Include CSS', the query strings 'includeHTMLAndBodyTags' and 'includeCSS' will be set to 'yes' or 'no' accordingly. The HTML option 'Inline CSS' is not included.

The request body will contain the HTML data.

When the HTML payload is sent in the request body with encryption:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
x-original-content-type:
text/html
x-encrypted-aes-key:
V/dB/+VPyf . . . jmLB8YALe0
x-encrypted-iv:
dwGHi4AR/L . . . YfwwcRLMii
content-type:
application/octet-stream

To keep the HTML body clean, the AES and IV keys are included in the HTTP headers as 'x-encrypted-aes-key' and 'x-encrypted-iv', respectively. The encrypted payload is sent directly in the HTML body.

<html> <body> <h1>Encrypted Payload</h1> <p>jsU/uNoSV/ . . . d7qensJho=</p> </body> </html>

When the HTML payload is sent in the file without encryption:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
multipart/form-data; boundary="Boundary-BD268869-F928-47D4-AF68-02956E4C5B72"

Form values:

postButton:
Upload File

The file will be uploaded as 'daysoff.html'. The name 'daysOffSchedulerData' with the filename 'daysoff.html' are specified in the 'Content-Disposition' header within the body prefix string.

When the HTML payload is sent in the file with encryption:

Content of HTTP headers:
user-agent:
DaysOffScheduler/1 CFNetwork/1498.700.2 Darwin/23.6.0
content-type:
multipart/form-data; boundary="Boundary-6D4A0878-17AA-44A2-B5EE-AE61E62F334C"

Form values:

encryptedAESKey:
xdm+DSStVc . . . xTmESjpFoQ==
encryptedIV:
Yeo6mBvEz0 . . . 6jFRbw91Hg==
postButton:
Upload File

The file will be uploaded as 'daysoff.json.bin'. The name 'daysOffSchedulerData' with the filename 'daysoff.json.bin' are specified in the 'Content-Disposition' header within the body prefix string.

A new boundary is generated every time you post a file. Authentication for posting the schedule in the HTML format is handled the same way as for posting in the JSON format.

NOTE: The postButton form value is not included when posting data in the request body. In this case, the request contains only the raw payload without a multipart/form-data boundary structure to organize "parts."

For more information about the format of the prefix and suffix, see:

Encryption Details

Back to Top
AES Encryption:
Algorithm:
AES-256-CBC
Key size:
256 bits (32 bytes)
Mode:
CBC (Cipher Block Chaining)
IV size:
128 bits (16 bytes)
Padding:
PKCS#7
RSA Encryption:
Algorithm:
RSA
Key size:
2048 bits (ensure key sizes match)
Padding Scheme:
OAEP (Optimal Asymmetric Encryption Padding)
Hash Function for OAEP:
SHA-256
MGF1 Function Hash Algorithm:
SHA-256

Data is encrypted in chunks. The AES key and IV (Initialization Vector) are encrypted using RSA and Base64-encoded.

Data Format in Request

Back to Top
Form Fields:
encryptedAESKey:
Base64-encoded string of the encrypted AES key
encryptedIV:
Base64-encoded string of the encrypted IV
File Part:
encryptedPayload:
Base64-encoded data of the payload encrypted with AES and IV keys

Server-side Data Extraction and Decryption

Back to Top

Sample Pseudocode for Server-Side Decryption

Back to Top
Step 1: Base64 decode the received data
encryptedPayload = Base64Decode(receivedEncryptedPayload)
encryptedAESKey = Base64Decode(receivedEncryptedAESKey)
encryptedIV = Base64Decode(receivedEncryptedIV)
Step 2: Decrypt AES Key and IV using RSA private key
AESKey = RSADecrypt(encryptedAESKey, privateKey, padding: OAEP, hash: SHA-256, mgf1Hash: SHA-256)
IV = RSADecrypt(encryptedIV, privateKey, padding: OAEP, hash: SHA-256, mgf1Hash: SHA-256)
Step 3: Decrypt the payload using AES
payload = AESDecrypt(encryptedPayload, key: AESKey, iv: IV, mode: CBC, padding: PKCS#7)

The payload is now decrypted and can be processed.

NOTE: While we continue to improve our security measures, the app does not currently support additional integrity verification mechanisms, such as tamper detection or HMAC or AES-GCM with authentication tag.

Generating RSA Keys Using OpenSSL using command-line

Back to Top
The server-side developers need to generate an RSA key pair:
Generate a Private Key:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
where
Extract the Public Key:
openssl rsa -pubout -in private_key.pem -out public_key.pem
where
Verify the Private Key:
openssl pkey -in private_key.pem -check
Verify the Public Key:
openssl pkey -pubin -in public_key.pem -text -noout
Share the Public Key:
Provide the public_key.pem file to the app securely.

Testing

Back to Top

We provide a simple Encryption-Decryption Tool that you can use to test the encrypted data and see the expected decrypted results.

Sample unencrypted JSON payload:

{
    "schedule": [
        {
            "rowNumber": 0,
            "employee": {
                "firstName": "John",
                "lastName": "Doe"
            },
            "daysOff": [
                true,
                false,
                false,
                false,
                false,
                false,
                true
            ]
        },
        {
            "rowNumber": 1,
            "employee": {
                "firstName": "Jane",
                "identifier": "123",
                "lastName": "Smith"
            },
            "daysOff": [
                true,
                false,
                false,
                false,
                false,
                false,
                true
            ]
        },
        {
            "rowNumber": 2,
            "employee": {
                "firstName": "Sarah",
                "identifier": "T-800",
                "lastName": "Connor"
            },
            "daysOff": [
                true,
                false,
                false,
                false,
                false,
                false,
                true
            ]
        },
        {
            "rowNumber": 3,
            "employee": {
                "firstName": "Sherlock",
                "identifier": "221b",
                "lastName": "Holmes"
            },
            "daysOff": [
                true,
                false,
                false,
                false,
                false,
                true,
                false
            ]
        },
        {
            "rowNumber": 4,
            "daysOff": [
                true,
                false,
                false,
                false,
                false,
                true,
                false
            ]
        },
        {
            "rowNumber": 5,
            "daysOff": [
                false,
                false,
                false,
                false,
                false,
                true,
                true
            ]
        },
        {
            "rowNumber": 6,
            "daysOff": [
                false,
                false,
                false,
                false,
                false,
                true,
                true
            ]
        }
    ],
    "header": {
        "startAndEndDatesOfWeek": "Dec 1, 2024 - Dec 7, 2024",
        "week": [
            {
                "month": "Dec",
                "dayOfWeek": "Sun",
                "dayOfMonth": 1
            },
            {
                "month": "Dec",
                "dayOfWeek": "Mon",
                "dayOfMonth": 2
            },
            {
                "month": "Dec",
                "dayOfWeek": "Tue",
                "dayOfMonth": 3
            },
            {
                "month": "Dec",
                "dayOfWeek": "Wed",
                "dayOfMonth": 4
            },
            {
                "month": "Dec",
                "dayOfWeek": "Thu",
                "dayOfMonth": 5
            },
            {
                "month": "Dec",
                "dayOfWeek": "Fri",
                "dayOfMonth": 6
            },
            {
                "month": "Dec",
                "dayOfWeek": "Sat",
                "dayOfMonth": 7
            }
        ],
        "weekNumber": 2
    }
}                       

Encryption-Decryption Tool

Back to Top
Encryption

Encrypted Data
Encrypted Payload - File Upload:
NOTE: The file data is not Base64 encoded.
Encrypted Payload (Base64) - Request Body
Encrypted Payload (Base64) - Request Body
Encrypted Payload - File Upload
Decryption

Decrypted Payload:

No data yet