Using the AnkaSecure SDK in Java
SDK Version: 3.0.0 Java Version: 17+ Artifact:
AnkaSecureSDK-3.0.0.jar
This guide shows how to integrate the AnkaSecure SDK into your own Java application. It assumes you have the AnkaSecureSDK JAR available (either through a Maven dependency or direct inclusion) and that you have valid credentials (clientId, clientSecret for application authentication or username/password for user authentication).
Code Examples
This documentation contains code examples showing how to use the SDK API. These examples are intended for developers integrating the SDK into their applications.
For complete working examples, see the Integration Flows section which provides 28 detailed scenarios.
1. Setup & Dependencies
There are two common approaches:
1.1 Maven Dependency (Recommended)
If the SDK is published to an internal or public artifact repository, add to your pom.xml:
<dependency>
<groupId>co.ankatech.secureclient</groupId>
<artifactId>AnkaSecureSDK</artifactId>
<version>3.0.0</version>
</dependency>
Gradle:
1.2 Local JAR (Alternative)
Download or build the AnkaSecure SDK JAR and add it to your project's classpath:
Maven (system scope):
<dependency>
<groupId>co.ankatech.secureclient</groupId>
<artifactId>AnkaSecureSDK</artifactId>
<version>3.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/AnkaSecureSDK-3.0.0.jar</systemPath>
</dependency>
Gradle:
2. Loading Configuration
The SDK requires connection details (scheme, host, port) and authentication credentials. You can store them in a Properties object, often loaded from a configuration file:
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream("cli.properties")) {
props.load(fis);
}
sdk-config.properties: # Application credentials
clientId=mySdkApp
clientSecret=mySdkSecret
# API connection
openapi.scheme=https
openapi.host=demo.ankatech.co
openapi.port=443
# TLS settings
openapi.insecureSkipTlsVerify=false
# Timeouts (milliseconds)
openapi.connectTimeoutMs=10000
openapi.readTimeoutMs=30000
openapi.writeTimeoutMs=30000
Security Note
Never commit clientSecret to version control. Use environment variables or secret managers in production. If your application must persist credentials locally, encrypt them at rest using AES-256-GCM with PBKDF2 key derivation.
See Security Best Practices for complete credential management guidance including encryption examples.
3. Instantiating the SDK Factory
After loading properties, create an unauthenticated SDK factory:
import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
import java.util.Properties;
Properties props = loadProperties(); // from previous section
// Create the SDK factory (unauthenticated)
AnkaSecureSdk factory = new AnkaSecureSdk(props);
Note: The factory itself does not perform cryptographic operations. You must authenticate to obtain an AuthenticatedSdk instance (see next section).
4. Authentication
Authenticate the factory to obtain an immutable, thread-safe AuthenticatedSdk instance:
import co.ankatech.ankasecure.sdk.AuthenticatedSdk;
try {
// Application-based authentication (recommended)
String clientId = props.getProperty("clientId");
String clientSecret = props.getProperty("clientSecret");
// Returns AuthenticatedSdk instance (immutable, thread-safe)
AuthenticatedSdk sdk = factory.authenticateApplication(clientId, clientSecret);
// Or, user-based authentication if required:
// AuthenticatedSdk sdk = factory.authenticateUser("username", "password", "tenantId");
// Now use sdk for all operations
EncryptResult result = sdk.encrypt("my-key", data);
} catch (AnkaSecureSdkException e) {
System.err.println("Authentication failed: " + e.getMessage());
return;
}
Key architectural points:
- Factory pattern:
AnkaSecureSdkis the factory, not the operational SDK - Return value: Authentication methods return
AuthenticatedSdk(not void) - Thread-safety:
AuthenticatedSdkis immutable and can be shared across 10,000+ threads - Token lifecycle: The authenticated instance remains valid until the JWT expires (check with
sdk.isTokenExpired())
Authentication methods:
- Application authentication (recommended): Use
clientIdandclientSecretfrom Tenant Admin CLI - User authentication: Use username and password (for user-based operations)
5. Key Operations
5.1 Generating Keys
Simple Keys (classical, PQC, symmetric):
import co.ankatech.ankasecure.sdk.model.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.model.KeyGenerationSummarySpec;
// Generate ML-KEM-768 key
GenerateKeySpec spec = new GenerateKeySpec()
.setKid("my-ml-kem-key")
.setKty("ML-KEM")
.setAlg("ML-KEM-768")
.setExpiresAt(ZonedDateTime.now().plusYears(1))
.setMaxUsageLimit(100000);
KeyGenerationSummarySpec result = sdk.generateKey(spec);
System.out.println("Key generated: " + result.getKid());
Composite Keys (hybrid classical + PQC):
import co.ankatech.ankasecure.sdk.model.GenerateCompositeKeySpec;
import co.ankatech.ankasecure.sdk.model.ComponentSpec;
// Generate HYBRID_KEM_COMBINE composite key
GenerateCompositeKeySpec spec = new GenerateCompositeKeySpec()
.setKid("hybrid-encryption-key")
.setMode(GenerateCompositeKeySpec.Mode.HYBRID_KEM_COMBINE)
.addComponent(ComponentSpec.classical(ClassicalCompositeAlgorithm.X25519))
.addComponent(ComponentSpec.pqc(PqcCompositeAlgorithm.ML_KEM_768))
.setKdf(Kdf.HKDF_SHA256);
KeyGenerationSummarySpec result = sdk.generateCompositeKey(spec);
System.out.println("Hybrid key generated: " + result.getKid());
Supported key types:
- Simple keys: 81 algorithms (classical, PQC, symmetric)
- Composite keys: 35 validated combinations (24 HYBRID_KEM_COMBINE + 11 DUALSIGN)
5.2 Listing Keys
import co.ankatech.ankasecure.sdk.model.KeyMetadata;
import java.util.List;
List<KeyMetadata> keys = sdk.listKeys();
System.out.println("Total keys: " + keys.size());
for (KeyMetadata key : keys) {
System.out.println(" - " + key.getKid() +
" (" + key.getAlg() + ")" +
" - Status: " + key.getStatus());
}
5.3 Export Keys
import co.ankatech.ankasecure.sdk.model.ExportedKeySpec;
ExportedKeySpec exported = sdk.exportKey("my-ml-kem-key");
String publicKeyB64 = exported.getPublicKey();
System.out.println("Public key: " + publicKeyB64);
5.4 Remove Key
6. Encryption & Decryption
6.1 In-Memory Operations (< 100 MB)
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.DecryptResult;
import java.nio.charset.StandardCharsets;
// Encrypt
byte[] plaintext = "Sensitive data".getBytes(StandardCharsets.UTF_8);
EncryptResult encResult = sdk.encrypt("my-ml-kem-key", plaintext);
String jweToken = encResult.getJweToken();
// Decrypt
DecryptResult decResult = sdk.decrypt(jweToken);
byte[] recovered = decResult.getDecryptedData();
System.out.println("Decrypted: " + new String(recovered, StandardCharsets.UTF_8));
6.2 File Operations (Non-Streaming, < 100 MB)
import java.nio.file.Path;
Path inputFile = Path.of("/data/document.pdf");
Path encryptedFile = Path.of("/data/document.pdf.jwe");
Path decryptedFile = Path.of("/data/document_decrypted.pdf");
// Encrypt file
EncryptResult encResult = sdk.encryptFile("my-key", inputFile, encryptedFile);
// Decrypt file
DecryptResultMetadata decMeta = sdk.decryptFile(encryptedFile, decryptedFile);
6.3 Streaming Operations (Large Files > 100 MB)
Path largeFile = Path.of("/data/backup_500MB.tar.gz");
Path encrypted = Path.of("/data/backup_500MB.tar.gz.jwe");
Path decrypted = Path.of("/data/backup_500MB_restored.tar.gz");
// Encrypt with streaming (detached JWE format)
EncryptResult encResult = sdk.encryptFileStream("archive-key", largeFile, encrypted);
// Decrypt with streaming
DecryptResultMetadata decMeta = sdk.decryptFileStream(encrypted, decrypted);
7. Signing & Verification
7.1 In-Memory Operations
import co.ankatech.ankasecure.sdk.model.SignResult;
import co.ankatech.ankasecure.sdk.model.VerifySignatureResult;
byte[] document = Files.readAllBytes(Path.of("contract.pdf"));
// Sign
SignResult signResult = sdk.sign("my-signing-key", document);
String jwsToken = signResult.getJwsToken();
// Verify
VerifySignatureResult verifyResult = sdk.verifySignature(jwsToken);
if (verifyResult.isValid()) {
System.out.println("Signature is VALID");
} else {
System.out.println("Signature is INVALID");
}
7.2 File Operations
Path document = Path.of("/legal/contract.pdf");
Path signature = Path.of("/legal/contract.pdf.jws");
// Sign file
SignResult signResult = sdk.signFile("legal-signing-key", document, signature);
// Verify signature
VerifySignatureResult verifyResult = sdk.verifySignature(signature);
System.out.println("Valid: " + verifyResult.isValid());
7.3 Streaming Operations (Large Files)
Path largeLog = Path.of("/audit/access_log_500MB.csv");
Path signature = Path.of("/audit/access_log.sig");
// Sign with streaming
SignResult signResult = sdk.signFileStream("audit-key", largeLog, signature);
// Verify with streaming
VerifySignatureResult verifyResult = sdk.verifySignatureStream(largeLog, signature);
8. Re-encrypt & Re-sign (Key Rotation)
8.1 Re-encrypt
Switch encrypted data from one key to another without exposing plaintext:
import co.ankatech.ankasecure.sdk.model.ReencryptResult;
String oldJwe = "eyJhbGc..."; // Encrypted with old-key-2023
ReencryptResult result = sdk.reencrypt("new-key-2024", oldJwe);
String newJwe = result.getJweToken();
System.out.println("Migrated from: " + result.getOldKeyAlgorithmUsed());
System.out.println("Migrated to: " + result.getNewKeyAlgorithmUsed());
8.2 Re-sign
import co.ankatech.ankasecure.sdk.model.ResignResult;
String oldJws = "eyJhbGc..."; // Signed with old-rsa-key
ResignResult result = sdk.resign("new-ml-dsa-key", oldJws);
String newJws = result.getJwsToken();
For file-based rotation, use sdk.reencryptFile(...) or sdk.resignFile(...). For streaming (files > 100MB), use sdk.reencryptFileStream(...) or sdk.resignFileStream(...).
9. Composite Hybrid Keys (Quantum-Resistant)
Composite keys combine classical and post-quantum algorithms for defense-in-depth security.
9.1 Generate Hybrid Encryption Key (HYBRID_KEM_COMBINE)
import co.ankatech.ankasecure.sdk.model.GenerateCompositeKeySpec;
import co.ankatech.ankasecure.sdk.model.ComponentSpec;
import co.ankatech.ankasecure.sdk.model.Kdf;
GenerateCompositeKeySpec spec = new GenerateCompositeKeySpec()
.setKid("hybrid-encryption-key")
.setMode(GenerateCompositeKeySpec.Mode.HYBRID_KEM_COMBINE)
.addComponent(ComponentSpec.classical(ClassicalCompositeAlgorithm.X25519))
.addComponent(ComponentSpec.pqc(PqcCompositeAlgorithm.ML_KEM_768))
.setKdf(Kdf.HKDF_SHA256);
KeyGenerationSummarySpec result = sdk.generateCompositeKey(spec);
Security: Requires BOTH X25519 and ML-KEM-768 to decrypt (AND-decrypt model).
9.2 Generate Dual Signature Key (DUALSIGN)
GenerateCompositeKeySpec spec = new GenerateCompositeKeySpec()
.setKid("dual-signature-key")
.setMode(GenerateCompositeKeySpec.Mode.DUALSIGN)
.addComponent(ComponentSpec.classical(ClassicalCompositeAlgorithm.ED25519))
.addComponent(ComponentSpec.pqc(PqcCompositeAlgorithm.ML_DSA_65))
.setVerificationPolicy(GenerateCompositeKeySpec.VerificationPolicy.ALL);
KeyGenerationSummarySpec result = sdk.generateCompositeKey(spec);
Security: Requires BOTH Ed25519 and ML-DSA-65 signatures to verify (ALL policy).
9.3 Using Composite Keys (Transparent API)
Composite keys work with the same encryption/signing methods as simple keys:
// Encryption with composite key (transparent - same API)
EncryptResult encResult = sdk.encrypt("hybrid-encryption-key", data);
// Signing with composite key (transparent - same API)
SignResult signResult = sdk.sign("dual-signature-key", document);
The SDK automatically handles the hybrid encryption or dual signature based on the key type.
Supported combinations: 35 validated pairings - 24 HYBRID_KEM_COMBINE (encryption) - 11 DUALSIGN (signatures)
See the Composite Hybrid Keys documentation for complete details on supported combinations.
10. Error Handling & Logging
Exception Handling
AnkaSecureSdkException: Thrown for server-side errors (HTTP 401, 404, 500, etc.) or internal SDK errors (invalid responses, configuration issues).
Common error scenarios:
- 401 Unauthorized: Invalid credentials or expired token
- 404 Not Found: Key ID does not exist
- 403 Forbidden: Insufficient permissions for operation
- 500 Internal Server Error: Server-side error (check correlation ID)
Logging
The SDK uses two loggers:
devLogger: Debug-level information (stack traces, HTTP details)userLogger: User-friendly messages (WARN/INFO level)
Configuration:
To enable debug logging, configure your logging framework (Logback, Log4J) to display DEBUG or TRACE messages for the co.ankatech.cli.dev logger.
Example logback.xml:
<logger name="co.ankatech.cli.dev" level="DEBUG"/>
<logger name="co.ankatech.cli.user" level="INFO"/>
11. Interoperability Operations
These operations enable cross-platform cryptographic collaboration using caller-supplied public keys that are NOT stored in the keystore.
11.1 Verify Signature with External Public Key
Verify a digital signature using a public key provided by an external partner without importing it into your keystore.
import co.ankatech.ankasecure.sdk.model.VerifyExternalSpec;
import co.ankatech.ankasecure.sdk.model.VerifySignatureResponse;
import java.nio.file.Paths;
// Build verification specification
VerifyExternalSpec spec = VerifyExternalSpec.builder()
.inputFile(Paths.get("contract.pdf"))
.signatureFile(Paths.get("contract.jws"))
.kty("ML-DSA")
.alg("ML-DSA-87")
.publicKeyFile(Paths.get("partner_pubkey.pem"))
.build();
// Verify signature
VerifySignatureResponse response = sdk.verifySignatureExternal(spec);
if (response.isValid()) {
System.out.println("✓ Signature verification: VALID");
System.out.println("✓ Signer: " + response.getKeyRequested());
System.out.println("✓ Algorithm: " + response.getAlgorithmUsed());
} else {
System.out.println("✗ Signature verification: INVALID");
}
Use cases:
- Verify documents from external organizations without importing their keys
- Continuous interoperability with partner PKI systems
- One-time signature verification for untrusted sources
11.2 Encrypt with External Public Key
Encrypt a file for an external recipient using their public key without storing it in your keystore.
import co.ankatech.ankasecure.sdk.model.EncryptExternalSpec;
import java.nio.file.Paths;
// Build encryption specification
EncryptExternalSpec spec = EncryptExternalSpec.builder()
.inputFile(Paths.get("data.bin"))
.outputFile(Paths.get("data.jwe"))
.kty("ML-KEM")
.alg("ML-KEM-1024")
.publicKeyFile(Paths.get("recipient_pubkey.pem"))
.build();
// Encrypt file
sdk.encryptFileExternal(spec);
System.out.println("✓ File encrypted for external recipient");
System.out.println("✓ Algorithm: ML-KEM-1024 + AES-256-GCM");
Use cases:
- Encrypt files for external partners without storing their keys
- Continuous secure data exchange with external systems
- Temporary encryption for one-time recipients
Key distinction:
- Interoperability operations: Keys NOT stored in keystore (temporary use)
- Standard operations: Keys stored in keystore (persistent use)
12. Migration Operations
These operations support one-time migration of legacy cryptographic data from external systems. Keys ARE imported into the AnkaSecure keystore for permanent storage and reuse.
12.1 Import PKCS#12 Bundle
Import private keys and certificate chains from PKCS#12 (.p12/.pfx) bundles.
import co.ankatech.ankasecure.sdk.model.Pkcs12ImportSpec;
import co.ankatech.ankasecure.sdk.model.Pkcs12ImportResponse;
import co.ankatech.ankasecure.sdk.model.CertificateValidationMode;
import co.ankatech.ankasecure.sdk.model.KidStrategy;
import java.nio.file.Paths;
// Build import specification
Pkcs12ImportSpec spec = Pkcs12ImportSpec.builder()
.kid("client-2024")
.p12File(Paths.get("mycert.p12"))
.p12Password("changeit")
.validationMode(CertificateValidationMode.STRICT)
.kidStrategy(KidStrategy.AUTO)
.build();
// Import PKCS#12
Pkcs12ImportResponse response = sdk.importPkcs12(spec);
System.out.println("✓ Imported " + response.getImportedCount() + " keys:");
response.getImportedKeys().forEach(key -> {
System.out.println(" - " + key.getKid() + " (" + key.getAlg() + ")");
});
if (response.getImportedTrustedCertCount() > 0) {
System.out.println("✓ Imported " + response.getImportedTrustedCertCount() + " trusted certificates");
}
Certificate validation modes:
STRICT(production) - Reject expired/invalid certificatesIMPORT_ONLY(recovery) - Allow expired certs with restricted operationsSKIP(testing only) - No validation
Kid generation strategies:
AUTO(default) - Generates kids as{baseKid}-{sanitizedAlias}MANUAL- Use explicit kid mappings viakidMappingsparameter
12.2 Analyze PKCS#7 Structure
Introspect PKCS#7/CMS files to understand their structure before conversion.
import co.ankatech.ankasecure.sdk.model.Pkcs7AnalysisSpec;
import co.ankatech.ankasecure.sdk.model.Pkcs7AnalysisResponse;
import java.nio.file.Paths;
// Build analysis specification
Pkcs7AnalysisSpec spec = Pkcs7AnalysisSpec.builder()
.pkcs7File(Paths.get("legacy_signed.p7s"))
.validateCertificates(true)
.build();
// Analyze PKCS#7
Pkcs7AnalysisResponse response = sdk.analyzePkcs7(spec);
System.out.println("Format: " + response.getDetectedFormat().getStructure());
System.out.println("Standard: " + response.getDetectedFormat().getStandard());
if (response.getSigners() != null && !response.getSigners().isEmpty()) {
System.out.println("Signers:");
response.getSigners().forEach(signer -> {
System.out.println(" - " + signer.getIssuerDN());
System.out.println(" Algorithm: " + signer.getSignatureAlgorithm());
});
}
if (response.getConversionFeasibility().isCanConvert()) {
System.out.println("✓ Conversion feasible to: " + response.getConversionFeasibility().getTargetFormat());
} else {
System.out.println("✗ Conversion not supported");
}
Supported structures:
- SignedData (signed documents)
- EnvelopedData (encrypted data)
- SignedAndEnvelopedData (combined signature + encryption)
12.3 Convert PKCS#7 to JOSE Format
Convert legacy PKCS#7/CMS files to modern JOSE format (JWS/JWE).
import co.ankatech.ankasecure.sdk.model.Pkcs7ConversionSpec;
import co.ankatech.ankasecure.sdk.model.Pkcs7ConversionResponse;
import co.ankatech.ankasecure.sdk.model.TargetFormat;
import co.ankatech.ankasecure.sdk.model.Serialization;
import java.nio.file.Paths;
// Build conversion specification
Pkcs7ConversionSpec spec = Pkcs7ConversionSpec.builder()
.pkcs7File(Paths.get("legacy_encrypted.p7m"))
.outputFile(Paths.get("modern_encrypted.jwe"))
.decryptionKid("legacy-recipient-key")
.targetFormat(TargetFormat.AUTO)
.serialization(Serialization.AUTO)
.upgradeEncryption(true) // Upgrade CBC → GCM
.build();
// Convert PKCS#7 to JOSE
Pkcs7ConversionResponse response = sdk.convertPkcs7ToJose(spec);
System.out.println("✓ Converted " + response.getStructure() + " → " + response.getTargetFormat());
System.out.println("✓ JOSE token saved to: modern_encrypted.jwe");
if (response.getSigners() != null && !response.getSigners().isEmpty()) {
System.out.println("Signers:");
response.getSigners().forEach(signer -> {
System.out.println(" - " + signer.getKid() + " (" + signer.getAlgorithm() + ")");
});
}
if (!response.getWarnings().isEmpty()) {
System.out.println("Warnings:");
response.getWarnings().forEach(System.out::println);
}
if (!response.getNextSteps().isEmpty()) {
System.out.println("\nNext steps:");
response.getNextSteps().forEach(System.out::println);
}
Conversion process:
- Parse PKCS#7 structure
- Verify signatures (if SignedData)
- Decrypt content (if EnvelopedData, requires private key)
- Convert to JOSE format (preserves classical algorithms)
- Optionally upgrade algorithms (CBC→GCM)
Prerequisites:
- Private keys must be imported via
importPkcs12()before conversion - Signer/recipient matching uses issuerDN + serialNumber
Next steps for PQC:
After conversion to JOSE, use standard SDK methods to upgrade to post-quantum:
// Generate PQ signing key
sdk.generateKey(new GenerateKeySpec()
.setKid("pq-signing-key")
.setKty("ML-DSA")
.setAlg("ML-DSA-87"));
// Re-sign with PQ algorithm (compact JWS)
ResignResult resignResult = sdk.resign("pq-signing-key", oldJws);
// Generate PQ encryption key
sdk.generateKey(new GenerateKeySpec()
.setKid("pq-encryption-key")
.setKty("ML-KEM")
.setAlg("ML-KEM-1024"));
// Re-encrypt with PQ algorithm (compact JWE)
ReencryptResult reencResult = sdk.reencrypt("pq-encryption-key", oldJwe);
Key distinction:
- Migration operations: Keys ARE imported into keystore (one-time setup)
- Interoperability operations: Keys NOT stored in keystore (continuous use)
13. Next Steps
Integration Examples
Explore real-world integration scenarios:
- Integration Examples: Common integration patterns
- Integration Flows: 34 detailed flows covering all SDK operations
- Flow 1-4: Basic operations (encrypt/decrypt, sign/verify)
- Flow 5-9: Post-quantum operations (ML-KEM, ML-DSA)
- Flow 10-28: Advanced scenarios (rotation, migration, interoperability)
- Flow 29-34: Composite hybrid keys (quantum-resistant defense-in-depth)
Additional Resources
- CLI Overview: Command-line tool built on the SDK
- Algorithm Selection Guide: Choosing the right algorithms
- Supported Algorithms: Complete algorithm catalog
© 2025 ANKATech Solutions INC. All rights reserved.