Flow 25 – External Key Interoperability
Demonstrates how to perform cryptographic operations using externally-provided public keys without importing them into the AnkaSecure server keystore. This is critical for B2B integration scenarios where you receive a partner's public key and need to encrypt data or verify signatures without registering their key in your system.
Real-World Use Cases:
- Partner integration: Encrypt data with partner's public key for transmission
- Signature verification: Verify documents signed by external parties
- One-time operations: Cryptographic operations without key persistence
- Distributed trust: Public key distribution via out-of-band channels
Utility Functions vs. Server-Side Operations:
| Feature | Utility Functions | Server-Side Operations |
|---|---|---|
| Key storage | Caller provides public key | Key stored in server keystore |
| Use case | External partners, one-time ops | Internal operations, repeated use |
| Key management | Caller manages key lifecycle | Server manages expiry, rotation |
Steps:
- Generate ML-KEM-768 key (simulating external partner)
- Export partner's public encryption key
- Encrypt file using only public key (no server-side key registration)
- Generate ML-DSA-87 signing key (simulating external signer)
- Sign file with server key
- Verify signature using only signer's public key (utility function)
Key Algorithms:
- ML-KEM-768: NIST FIPS 203 post-quantum key encapsulation for encryption
- ML-DSA-87: NIST FIPS 204 post-quantum digital signature
API Endpoints:
- POST
/api/key-management/keys(generate keys) - GET
/api/migration/keys/{kid}(export public keys) - POST
/api/interoperability/encrypt(utility encryption with external public key) - POST
/api/crypto/stream/sign(server-side signing) - POST
/api/interoperability/verify(utility verification with external public key)
When to use:
- B2B integration where you receive partner public keys via secure channels (email, API)
- One-time signature verification from external PKI systems
- Encrypting data for external recipients without permanent key storage
- Cross-organization cryptographic collaboration without trust infrastructure
Maps to CLI commands (NEW in v3.0.0):
verify-external- Verify signatures with external public keysencrypt-external- Encrypt with external public keys
Dependency — this example imports
co.ankatech.ankasecure.sdk.examples.ExampleUtil. If you have not copied that class yet, see example_util.md.
Complete Java Implementation
Source: src/main/java/co/ankatech/ankasecure/sdk/examples/ExampleScenario25.java
package co.ankatech.ankasecure.sdk.examples;
import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
import co.ankatech.ankasecure.sdk.model.*;
import co.ankatech.ankasecure.sdk.util.FileIO;
import java.nio.file.Path;
import java.util.List;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
/**
* <h2>Scenario 25: External Key Interoperability</h2>
* <p>
* Demonstrates how to perform cryptographic operations using externally-provided public keys
* without importing them into the AnkaSecure server keystore. This is critical for B2B
* integration scenarios where you receive a partner's public key and need to encrypt data
* or verify signatures without registering their key in your system.
* </p>
*
* <h3>Real-World Use Cases:</h3>
* <ul>
* <li><strong>Partner integration:</strong> Encrypt data with partner's public key for transmission</li>
* <li><strong>Signature verification:</strong> Verify documents signed by external parties</li>
* <li><strong>One-time operations:</strong> Cryptographic operations without key persistence</li>
* <li><strong>Distributed trust:</strong> Public key distribution via out-of-band channels</li>
* </ul>
*
* <h3>Utility Functions vs. Server-Side Operations:</h3>
* <table border="1">
* <tr>
* <th>Feature</th>
* <th>Utility Functions</th>
* <th>Server-Side Operations</th>
* </tr>
* <tr>
* <td>Key storage</td>
* <td>Caller provides public key</td>
* <td>Key stored in server keystore</td>
* </tr>
* <tr>
* <td>Use case</td>
* <td>External partners, one-time ops</td>
* <td>Internal operations, repeated use</td>
* </tr>
* <tr>
* <td>Key management</td>
* <td>Caller manages key lifecycle</td>
* <td>Server manages expiry, rotation</td>
* </tr>
* </table>
*
* <h3>Steps:</h3>
* <ol>
* <li>Generate ML-KEM-768 key (simulating external partner)</li>
* <li>Export partner's public encryption key</li>
* <li>Encrypt file using only public key (no server-side key registration)</li>
* <li>Generate ML-DSA-87 signing key (simulating external signer)</li>
* <li>Sign file with server key</li>
* <li>Verify signature using only signer's public key (utility function)</li>
* </ol>
*
* <h3>Key Algorithms:</h3>
* <ul>
* <li><strong>ML-KEM-768:</strong> Post-quantum key encapsulation for encryption</li>
* <li><strong>ML-DSA-87:</strong> Post-quantum digital signature</li>
* </ul>
*
* <h3>API Endpoints:</h3>
* <ul>
* <li>POST /api/key-management/keys (generate keys)</li>
* <li>GET /api/migration/keys/{kid} (export public keys)</li>
* <li>POST /api/interoperability/encrypt (utility encryption with external public key)</li>
* <li>POST /api/crypto/stream/sign (server-side signing)</li>
* <li>POST /api/interoperability/verify (utility verification with external public key)</li>
* </ul>
*
* @since 3.0.0
*/
public final class ExampleScenario25 {
private static final Path TEMP_DIR = Path.of("temp_files");
private ExampleScenario25() {
/* static only */
}
public static void main(String[] args) {
System.out.println("===== SCENARIO 25: EXTERNAL KEY INTEROPERABILITY =====");
System.out.println("Purpose: Demonstrate cryptographic operations with external public keys");
System.out.println("Pattern: Utility functions for partner integration");
System.out.println();
try {
ensureTempDir(TEMP_DIR);
Properties props = loadProperties();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
System.out.println("===== SCENARIO 25 END =====");
} catch (Exception ex) {
fatal("Scenario 25 failed", ex);
}
}
private static void runScenario(AnkaSecureSdk sdk) throws Exception {
// ============================================================
// PHASE 1: Encryption Interoperability
// ============================================================
System.out.println("╔═══════════════════════════════════════════════════════════════╗");
System.out.println("║ PHASE 1: ENCRYPTION INTEROPERABILITY ║");
System.out.println("║ (Encrypt with external public key, decrypt with server key) ║");
System.out.println("╚═══════════════════════════════════════════════════════════════╝");
System.out.println();
demonstrateEncryptionInterop(sdk);
// ============================================================
// PHASE 2: Signature Verification Interoperability
// ============================================================
System.out.println("╔═══════════════════════════════════════════════════════════════╗");
System.out.println("║ PHASE 2: SIGNATURE VERIFICATION INTEROPERABILITY ║");
System.out.println("║ (Sign with server key, verify with external public key) ║");
System.out.println("╚═══════════════════════════════════════════════════════════════╝");
System.out.println();
demonstrateSignatureInterop(sdk);
// ============================================================
// FINAL STATUS
// ============================================================
System.out.println("╔═══════════════════════════════════════════════════════════════╗");
System.out.println("║ ✅ SCENARIO 25 SUCCESSFUL ║");
System.out.println("║ ║");
System.out.println("║ External key interoperability validated: ║");
System.out.println("║ • Utility encryption with partner public key works ║");
System.out.println("║ • Utility verification with signer public key works ║");
System.out.println("║ • No server-side key registration required ║");
System.out.println("╚═══════════════════════════════════════════════════════════════╝");
}
/**
* Demonstrates encryption using external public key.
*/
private static void demonstrateEncryptionInterop(AnkaSecureSdk sdk) throws Exception {
System.out.println("[Step 1/3] Generating ML-KEM-768 key (simulating external partner)...");
String kemKid = "sc25_external_kem768_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(kemKid)
.setKty("ML-KEM")
.setAlg("ML-KEM-768")
.setKeyOps(List.of("encrypt", "decrypt")));
System.out.println(" Partner key ID: " + kemKid);
System.out.println();
System.out.println("[Step 2/3] Exporting partner's public key...");
ExportedKeySpec kemExport = sdk.exportKey(kemKid);
String kemPubB64 = kemExport.getPublicKey();
// Save to file (simulates receiving from partner via email, API, etc.)
Path kemPubFile = TEMP_DIR.resolve("scenario25_partner_kem_public.b64");
FileIO.writeUtf8(kemPubFile, kemPubB64);
System.out.println(" Public key saved: " + kemPubFile);
System.out.println(" (In production: received via secure channel)");
System.out.println();
System.out.println("[Step 3/3] Encrypting with utility function (using public key only)...");
Path plainFile = TEMP_DIR.resolve("scenario25_plain.txt");
FileIO.writeUtf8(plainFile, "Scenario-25: External key interoperability demo.");
Path utilEncFile = TEMP_DIR.resolve("scenario25_utility_encrypted.enc");
// CRITICAL: This encryption uses ONLY the public key, no server-side key storage
EncryptResult encMeta = sdk.encryptFileUtilityStream("ML-KEM", "ML-KEM-768", kemPubB64, plainFile, utilEncFile);
System.out.println(" ✅ Encrypted with utility function");
System.out.println(" Output: " + utilEncFile);
System.out.println(" Key used: Partner's public key (not in server keystore)");
System.out.println(" Algorithm: " + encMeta.getAlgorithmUsed());
System.out.println();
System.out.println(" Note: Partner would decrypt this file with their private key");
System.out.println(" in their own AnkaSecure instance (simulated by KID: " + kemKid + ")");
System.out.println();
}
/**
* Demonstrates signature verification using external public key.
*/
private static void demonstrateSignatureInterop(AnkaSecureSdk sdk) throws Exception {
System.out.println("[Step 4/6] Generating ML-DSA-87 signing key (simulating external signer)...");
String signKid = "sc25_external_mlsa87_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(signKid)
.setKty("ML-DSA")
.setAlg("ML-DSA-87")
.setKeyOps(List.of("sign", "verify")));
System.out.println(" Signer key ID: " + signKid);
System.out.println();
System.out.println("[Step 5/6] Signing with server key...");
Path plainFile = TEMP_DIR.resolve("scenario25_plain.txt");
Path signedFile = TEMP_DIR.resolve("scenario25_signed.jws");
SignResult signMeta = sdk.signFileStream(signKid, plainFile, signedFile);
System.out.println(" Signature created");
printSignMeta(signMeta);
System.out.println();
System.out.println("[Step 6/6] Verifying signature with utility function (using public key only)...");
// Export signer's public key
ExportedKeySpec signExport = sdk.exportKey(signKid);
String signPubB64 = signExport.getPublicKey();
// Save to file (simulates receiving from signer)
Path signPubFile = TEMP_DIR.resolve("scenario25_signer_mlsa_public.b64");
FileIO.writeUtf8(signPubFile, signPubB64);
System.out.println(" Public key exported: " + signPubFile);
System.out.println(" (In production: received from signer for verification)");
System.out.println();
// CRITICAL: This verification uses ONLY the public key, no server-side key storage
// Read detached JWS file and Base64 encode it for utility method
String signedJwsB64 = java.util.Base64.getEncoder().encodeToString(
java.nio.file.Files.readAllBytes(signedFile));
// Verify the detached signature against the original payload
VerifySignatureResult verifyMeta = sdk.verifySignatureUtilityStream(
"ML-DSA", "ML-DSA-87", signPubB64, signedJwsB64, plainFile);
System.out.println(" Signature verified with utility function");
System.out.println(" Signature valid: " + verifyMeta.isValid());
System.out.println(" Key used: Signer's public key (not in server keystore)");
if (verifyMeta.isValid()) {
System.out.println(" ✅ Validation: Signature is VALID");
System.out.println(" Utility verification works correctly!");
} else {
System.out.println(" ❌ Validation: Signature verification FAILED");
}
System.out.println();
}
}
Running This Example
# Compile
javac -cp "ankasecure-sdk-3.0.0.jar:." ExampleScenario25.java
# Run
java -cp "ankasecure-sdk-3.0.0.jar:." co.ankatech.ankasecure.sdk.examples.ExampleScenario25
Expected Output:
===== SCENARIO 25: EXTERNAL KEY INTEROPERABILITY =====
Purpose: Demonstrate cryptographic operations with external public keys
Pattern: Utility functions for partner integration
╔═══════════════════════════════════════════════════════════════╗
║ PHASE 1: ENCRYPTION INTEROPERABILITY ║
║ (Encrypt with external public key, decrypt with server key) ║
╚═══════════════════════════════════════════════════════════════╝
[Step 1/3] Generating ML-KEM-768 key (simulating external partner)...
Partner key ID: sc25_external_kem768_1735152000000
[Step 2/3] Exporting partner's public key...
Public key saved: temp_files/scenario25_partner_kem_public.b64
(In production: received via secure channel)
[Step 3/3] Encrypting with utility function (using public key only)...
✅ Encrypted with utility function
Output: temp_files/scenario25_utility_encrypted.enc
Key used: Partner's public key (not in server keystore)
Algorithm: ML-KEM-768
Note: Partner would decrypt this file with their private key
in their own AnkaSecure instance
╔═══════════════════════════════════════════════════════════════╗
║ PHASE 2: SIGNATURE VERIFICATION INTEROPERABILITY ║
║ (Sign with server key, verify with external public key) ║
╚═══════════════════════════════════════════════════════════════╝
[Step 4/6] Generating ML-DSA-87 signing key (simulating external signer)...
Signer key ID: sc25_external_mlsa87_1735152000100
[Step 5/6] Signing with server key...
Signature created
[Step 6/6] Verifying signature with utility function (using public key only)...
Public key exported: temp_files/scenario25_signer_mlsa_public.b64
(In production: received from signer for verification)
Signature verified with utility function
Signature valid: true
Key used: Signer's public key (not in server keystore)
✅ Validation: Signature is VALID
Utility verification works correctly!
╔═══════════════════════════════════════════════════════════════╗
║ ✅ SCENARIO 25 SUCCESSFUL ║
║ ║
║ External key interoperability validated: ║
║ • Utility encryption with partner public key works ║
║ • Utility verification with signer public key works ║
║ • No server-side key registration required ║
╚═══════════════════════════════════════════════════════════════╝
===== SCENARIO 25 END =====
Key Concepts
Interoperability vs Migration
This example demonstrates Interoperability operations, which are distinct from Migration operations:
| Aspect | Interoperability (This Flow) | Migration (Flow 13, 26) |
|---|---|---|
| Key Storage | Keys NOT stored in server | Keys imported into keystore |
| Use Case | Continuous cross-platform ops | One-time legacy migration |
| Duration | Ongoing operations | One-time setup |
Utility Functions
Utility functions (encryptFileUtilityStream, verifySignatureUtilityStream) enable operations with caller-supplied public keys:
- No keystore pollution: Keys not permanently stored
- Stateless: Each request provides the public key
- Temporary: Ideal for one-time or infrequent operations
- B2B friendly: Partner manages their own key lifecycle
Public Key Exchange
Secure public key distribution requires:
- Secure transmission: Email, HTTPS API, secure file transfer
- Fingerprint verification: Verify SHA-256 fingerprint via out-of-band channel (phone, SMS)
- Format: Base64-encoded DER or PEM format
- Metadata: Include algorithm, expiry, intended use