Skip to content

Flow 3 – AES-256 Stream Encrypt / Decrypt (Detached JWET)

This scenario demonstrates a symmetric encryption round-trip using the streaming endpoints:

  1. Generate an AES-256 key (kty="oct", alg="AES-256").
  2. Stream-encrypt a plaintext file – server returns a detached JWET (General JSON).
  3. Stream-decrypt the ciphertext.
  4. Validate the plaintext matches byte-for-byte.

Key points

  • Purely symmetric, streaming workflow—encryptFileStream / decryptFileStream push raw bytes in one direction while you read them from the other, so RAM never spikes.
  • Returns a detached JWET: header and encryption parameters stay in a tiny JSON blob while the ciphertext flows as native binary—no Base64 inflation, no line-length limits.
  • Shows end-to-end lifecycle telemetry (actual key used, algorithm, soft-limit warnings) that arrives in HTTP headers, ready for dashboards or automated alerting.

When to use it

  • Mass-volume files – perfect for multi-gigabyte database dumps, video archives, or nightly VM images where holding everything in memory is impossible.
  • Low-overhead transfers – detached JWET keeps bandwidth lean and lets you pipe encrypted data straight into S3, GCS, or object storage without re-encoding.
  • Regulated workloads – AES-256 is NIST-approved and FIPS-validated; combining it with streaming endpoints satisfies strict mandates for data-at-rest & in-motion encryption while avoiding temp-file sprawl.

Shared helperExampleUtil (configuration-loading, authentication, JSON, etc.).
If you haven’t copied it yet, fetch example-util.md and place the Java file beside the scenario sources.


Complete Java implementation

src/main/java/co/ankatech/ankasecure/sdk/examples/ExampleScenario3.java

/*
 * Copyright 2025 ANKATech Solutions Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
package co.ankatech.ankasecure.sdk.examples;

import co.ankatech.ankasecure.sdk.AuthenticatedSdk;
import co.ankatech.ankasecure.sdk.model.DecryptResultMetadata;
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.util.FileIO;

import java.nio.file.Path;
import java.util.Properties;

import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;

/**
 * Scenario&nbsp;3 &mdash; AES-256 <em>Streaming</em> Encrypt&nbsp;/&nbsp;Decrypt.
 *
 * <p>This scenario demonstrates a <strong>symmetric</strong> workflow via the
 * streaming APIs. In streaming mode the service returns a
 * <strong>detached&nbsp;JWE&nbsp;(General&nbsp;JSON)</strong>: the header
 * portion is delivered separately from the raw ciphertext, keeping memory
 * usage constant.</p>
 *
 * <ol>
 *   <li>Generate an <code>AES-256</code> key&nbsp;(<code>kty="oct"</code>).</li>
 *   <li>Stream-encrypt a plaintext file (detached&nbsp;JWE).</li>
 *   <li>Stream-decrypt the ciphertext.</li>
 *   <li>Validate that the plaintext round-trips.</li>
 * </ol>
 *
 * <p>All artefacts are written under <kbd>temp_files/</kbd>.</p>
 *
 * <p><b>Implementation notes (Java&nbsp;21+):</b></p>
 * <ul>
 *   <li>All filesystem operations use the {@link java.nio.file.Path} API.</li>
 *   <li>UTF-8 is enforced explicitly to avoid platform defaults.</li>
 *   <li>Directory creation relies on {@link ExampleUtil#ensureTempDir(Path)}.</li>
 * </ul>
 *
 * <p><b>Thread-safety:</b> this class is stateless and immutable.</p>
 *
 * @author ANKATech Solutions Inc.
 * @since 3.0.0
 * @see ExampleUtil
 * @see AuthenticatedSdk
 */
public final class ExampleScenario3 {

    /** No instantiation &mdash; this class only exposes {@link #main(String[])}. */
    private ExampleScenario3() { }

    /**
     * Runs the AES-256 streaming encrypt/decrypt scenario.
     *
     * <p>Loads CLI properties, authenticates against ANKASecure&copy;,
     * and delegates to the scenario logic. On any unrecoverable error
     * the JVM terminates via
     * {@link ExampleUtil#fatal(String, Throwable)}.</p>
     *
     * @param args command-line arguments (ignored)
     */
    public static void main(final String[] args) {

        System.out.println("===== SCENARIO 3 START =====");
        System.out.println("""
                Purpose :
                  * AES-256 symmetric streaming life-cycle
                  * Demonstrates detached-JWE pipeline (encrypt -> decrypt)
                Steps   :
                  1) Generate AES-256 key
                  2) Create sample payload
                  3) Encrypt payload (detached JWE)
                  4) Decrypt ciphertext
                  5) Validate integrity
                --------------------------------------------------------------""");

        try {
            ensureTempDir(TEMP_DIR);

            Properties   props = loadProperties();
            AuthenticatedSdk sdk  = authenticate(props);

            runScenario(sdk);

        } catch (Exception ex) {
            fatal("Scenario 3 failed", ex);
        }

        System.out.println("===== SCENARIO 3 END =====");
    }

    /**
     * Executes the 5-step AES-256 streaming encryption scenario.
     *
     * <ol>
     *   <li>Prepare plaintext file.</li>
     *   <li>Generate AES-256 symmetric key.</li>
     *   <li>Stream-encrypt to detached JWE.</li>
     *   <li>Stream-decrypt ciphertext.</li>
     *   <li>Validate round-trip integrity.</li>
     * </ol>
     *
     * @param sdk an authenticated {@link AuthenticatedSdk} instance;
     *            must not be {@code null}
     * @throws Exception if any step fails
     */
    private static void runScenario(final AuthenticatedSdk sdk) throws Exception {

        /* 1 -- prepare plaintext -------------------------------------------- */
        Path plainFile = TEMP_DIR.resolve("scenario3_plain.txt");
        FileIO.writeUtf8(plainFile,
                "Scenario-3 - AES-256 streaming encryption demo.");
        System.out.println("[1] Plaintext ready          -> " + plainFile.toAbsolutePath());

        /* 2 -- generate AES-256 key ----------------------------------------- */
        String kid = "sc3_aes256_" + System.currentTimeMillis();
        sdk.generateKey(new GenerateKeySpec()
                .setKid(kid)
                .setKty("oct")
                .setAlg("AES-GCM-256"));
        System.out.println("[2] Key generated            -> kid = " + kid);

        /* 3 -- streaming encrypt -------------------------------------------- */
        Path cipherFile = TEMP_DIR.resolve("scenario3.enc");
        EncryptResult encMeta = sdk.encryptFileStream(kid, plainFile, cipherFile);
        System.out.println("[3] Ciphertext written       -> " + cipherFile.toAbsolutePath());
        printEncryptMeta(encMeta);

        /* 4 -- streaming decrypt -------------------------------------------- */
        Path decFile = TEMP_DIR.resolve("scenario3_dec.txt");
        DecryptResultMetadata decMeta = sdk.decryptFileStream(cipherFile, decFile);
        System.out.println("[4] Decrypted file           -> " + decFile.toAbsolutePath());
        printDecryptMeta(decMeta);

        /* 5 -- validation --------------------------------------------------- */
        String original  = FileIO.readUtf8(plainFile);
        String recovered = FileIO.readUtf8(decFile);
        System.out.println(original.equals(recovered)
                ? "[5] Validation OK - plaintext matches."
                : "[5] WARNING - plaintext mismatch!");
    }
}

How to run


mvn -q compile exec:java\
  -Dexec.mainClass="co.ankatech.ankasecure.sdk.examples.ExampleScenario3"

Console milestones

-   AES-256 key creation (`kid = sc3_aes256_<timestamp>`)

-   Detached-JWET stream-encryption → `scenario3.enc`

-   Stream-decryption → `scenario3_dec.txt`

-   **Validation OK** confirming bit-perfect round-trip

Where next?