/api/rke/decrypt
GETDecrypts 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
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-CTR mode is symmetric - the same operation encrypts and decrypts. The key and nonce determine the keystream, which is XORed with the data.
Parameters
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.
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.
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)
atob() (JavaScript) or base64.decode() to get original data.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)
}