/api/rke/decrypt

GET

Decrypts data that was encrypted using an RKE session. Requires the ciphertext and the nonce that was returned during encryption.

Description

Decrypts ciphertext that was previously encrypted using the /api/rke/encrypt endpoint. The decryption requires:

  • The same session (or a session with the same derived key)
  • The exact ciphertext returned from encryption
  • The exact nonce that was returned with the ciphertext
Session Requirement

Decryption must use the same session (or a session derived from the same Content Server domain) that was used for encryption. The session must not be expired.

AES-256-CTR Decryption

AES-CTR mode is symmetric - the same operation encrypts and decrypts. The key and nonce determine the keystream, which is XORed with the data.

Parameters

session_id
string (query)
Required

The 32-character hexadecimal session ID.

Format: 32 hexadecimal characters (a-f, 0-9)

Example: a1b2c3d4e5f67890a1b2c3d4e5f67890

Note: Must be the same session used for encryption, or a session with the same domain/key.

ciphertext
string (query)
Required

The encrypted data, Base64 encoded.

Format: Standard Base64 encoding

Source: The ciphertext field from /api/rke/encrypt response

Example: xK7mPqR5tY2wZn8=

Note: URL-encode if it contains +, /, or = characters.

nonce
string (query)
Required

The 16-byte nonce as a 32-character hexadecimal string.

Format: 32 hexadecimal characters (a-f, 0-9)

Source: The nonce field from /api/rke/encrypt response

Example: 0123456789abcdef0123456789abcdef

Critical: Must be the exact nonce returned during encryption. Wrong nonce = garbage output.

Response

Response Properties (Success)

command string
Always "rke_decrypt".
success boolean
True if decryption succeeded.
plaintext string
The decrypted data, Base64 encoded. Decode with atob() (JavaScript) or base64.decode() to get original data.
length integer
Decrypted data length in bytes.

Example Response (Success)

{
  "command": "rke_decrypt",
  "success": true,
  "plaintext": "SGVsbG8gV29ybGQh",
  "length": 12
}

The plaintext "SGVsbG8gV29ybGQh" decodes to "Hello World!" in Base64.

Example Response (Error - Invalid Nonce)

{
  "error": true,
  "message": "Missing or invalid 'nonce' parameter (32 hex chars)",
  "code": 400
}

Complete Encrypt/Decrypt Example

Here's a complete round-trip example showing encryption followed by decryption:

const API_HOST = 'http://localhost:8080';

class RkeClient {
    constructor(sessionId) {
        this.sessionId = sessionId;
    }

    async encrypt(text) {
        const b64 = btoa(text);
        const url = `${API_HOST}/api/rke/encrypt?session_id=${this.sessionId}&plaintext=${encodeURIComponent(b64)}`;
        const resp = await fetch(url);
        return await resp.json();
    }

    async decrypt(ciphertext, nonce) {
        const url = `${API_HOST}/api/rke/decrypt?session_id=${this.sessionId}&ciphertext=${encodeURIComponent(ciphertext)}&nonce=${nonce}`;
        const resp = await fetch(url);
        const result = await resp.json();
        if (result.success) {
            result.decodedText = atob(result.plaintext);
        }
        return result;
    }
}

// Usage
async function demo() {
    const client = new RkeClient('a1b2c3d4e5f67890a1b2c3d4e5f67890');

    // Original message
    const original = 'Hello, Content Server!';
    console.log('Original:', original);

    // Encrypt
    const encrypted = await client.encrypt(original);
    console.log('Encrypted:', encrypted.ciphertext);
    console.log('Nonce:', encrypted.nonce);

    // Decrypt (using the same nonce!)
    const decrypted = await client.decrypt(encrypted.ciphertext, encrypted.nonce);
    console.log('Decrypted:', decrypted.decodedText);

    // Verify
    console.log('Match:', original === decrypted.decodedText);
}

demo();

Examples

JavaScript (fetch)

const API_HOST = 'http://localhost:8080';

/**
 * Decrypt data using an RKE session
 * @param {string} sessionId - The 32-char session ID
 * @param {string} ciphertext - Base64 encoded ciphertext
 * @param {string} nonce - 32-char hex nonce
 * @returns {Promise} - Decrypted plaintext
 */
async function rkeDecrypt(sessionId, ciphertext, nonce) {
    const params = new URLSearchParams({
        session_id: sessionId,
        ciphertext: ciphertext,
        nonce: nonce
    });

    const response = await fetch(`${API_HOST}/api/rke/decrypt?${params}`);
    const result = await response.json();

    if (!result.success) {
        throw new Error(result.message);
    }

    // Decode Base64 plaintext to string
    return atob(result.plaintext);
}

// Decrypt a previously encrypted message
async function decryptStoredMessage() {
    const stored = JSON.parse(localStorage.getItem('encrypted_message'));
    const sessionId = sessionStorage.getItem('rke_session_id');

    if (stored && sessionId) {
        const plaintext = await rkeDecrypt(
            sessionId,
            stored.ciphertext,
            stored.nonce
        );
        console.log('Decrypted message:', plaintext);
    }
}

decryptStoredMessage();

cURL

# Decrypt with session ID, ciphertext, and nonce
SESSION_ID="a1b2c3d4e5f67890a1b2c3d4e5f67890"
CIPHERTEXT="xK7mPqR5tY2wZn8="
NONCE="0123456789abcdef0123456789abcdef"

curl "http://localhost:8080/api/rke/decrypt?session_id=$SESSION_ID&ciphertext=$CIPHERTEXT&nonce=$NONCE"

# Decrypt and decode Base64 result
curl "http://localhost:8080/api/rke/decrypt?session_id=$SESSION_ID&ciphertext=$CIPHERTEXT&nonce=$NONCE" \
  | jq -r '.plaintext' | base64 -d

# Full encrypt → decrypt round-trip
# Step 1: Encrypt
ENCRYPT_RESULT=$(curl -s "http://localhost:8080/api/rke/encrypt?session_id=$SESSION_ID&plaintext=SGVsbG8=")
CT=$(echo $ENCRYPT_RESULT | jq -r '.ciphertext')
NONCE=$(echo $ENCRYPT_RESULT | jq -r '.nonce')

# Step 2: Decrypt
curl "http://localhost:8080/api/rke/decrypt?session_id=$SESSION_ID&ciphertext=$CT&nonce=$NONCE" | jq

Go

package main

import (
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)

const ApiHost = "http://localhost:8080"

type DecryptResponse struct {
    Command   string `json:"command"`
    Success   bool   `json:"success"`
    Plaintext string `json:"plaintext"`
    Length    int    `json:"length"`
}

func RkeDecrypt(sessionId, ciphertext, nonce string) (string, error) {
    apiUrl := fmt.Sprintf("%s/api/rke/decrypt?session_id=%s&ciphertext=%s&nonce=%s",
        ApiHost,
        sessionId,
        url.QueryEscape(ciphertext),
        nonce)

    resp, err := http.Get(apiUrl)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
    var result DecryptResponse
    json.Unmarshal(body, &result)

    if !result.Success {
        return "", fmt.Errorf("decryption failed")
    }

    // Decode Base64 plaintext
    decoded, _ := base64.StdEncoding.DecodeString(result.Plaintext)
    return string(decoded), nil
}

func main() {
    sessionId := "a1b2c3d4e5f67890a1b2c3d4e5f67890"
    ciphertext := "xK7mPqR5tY2wZn8="
    nonce := "0123456789abcdef0123456789abcdef"

    plaintext, err := RkeDecrypt(sessionId, ciphertext, nonce)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Decrypted: %s\n", plaintext)
}

Related Endpoints

/api/rke/encrypt

Encrypt data before sending to Content Server.

/api/rke/session

Create a session for encryption.

/api/rke/sessions

List active sessions.