Class AnkaSecureSdk
AnkaSecureSdk serves as the entry point for the ANKASecure© SDK.
It handles authentication and returns immutable AuthenticatedSdk
instances that provide all cryptographic and key management operations.
Architecture
The SDK separates concerns:
- AnkaSecureSdk (Factory) - Handles authentication only
- AuthenticatedSdk (Immutable) - Performs all operations
- Thread-safe concurrent access (10,000+ threads)
- Clear token lifecycle management
- Multiple independent sessions from one factory
Basic Usage
// 1. Load configuration
Properties props = new Properties();
try (var in = new FileInputStream("cli.properties")) {
props.load(in);
}
// 2. Create factory (reusable)
AnkaSecureSdk factory = new AnkaSecureSdk(props);
// 3. Authenticate and get SDK instance (the secret is held in a zeroizable wrapper)
// A secret read from an environment variable leaves a String copy that cannot be
// cleared; Console.readPassword() returns a char[] and creates none.
SecretChars clientSecret = new SecretChars(System.getenv("ANKASECURE_CLIENT_SECRET").toCharArray());
AuthenticatedSdk sdk = factory.authenticateApplication("my-client-id", clientSecret);
// 4. Perform operations
EncryptResult result = sdk.encrypt("my-key", data);
Thread-Safe Concurrent Usage
AnkaSecureSdk factory = new AnkaSecureSdk(props);
AuthenticatedSdk sdk = factory.authenticateApplication("my-client-id", clientSecret);
// Share SDK across thousands of threads safely
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<CompletableFuture<EncryptResult>> futures = dataList.stream()
.map(data -> CompletableFuture.supplyAsync(
() -> sdk.encrypt("key", data),
executor
))
.toList();
List<EncryptResult> results = futures.stream()
.map(CompletableFuture::join)
.toList();
}
Multiple Sessions
AnkaSecureSdk factory = new AnkaSecureSdk(props);
// Different authenticated sessions from same factory
AuthenticatedSdk appSdk = factory.authenticateApplication("my-client-id", clientSecret);
SecretChars password = new SecretChars(System.console().readPassword("Password: "));
AuthenticatedSdk userSdk = factory.authenticateUser("alice@example.com", password, "my-tenant-id");
// Each operates independently
appSdk.encrypt("key1", data1);
userSdk.listAllKeys(KeyFilters.none());
Token Lifecycle Management
AuthenticatedSdk sdk = factory.authenticateApplication("my-client-id", clientSecret);
// Check expiry before long-running operations
if (sdk.isTokenExpiredLocally()) {
sdk = factory.authenticateApplication("my-client-id", clientSecret);
}
processBatch(sdk, largeDataset);
Configuration
The SDK is configured via cli.properties file. Key settings for
high-concurrency scenarios:
# Connection pool (optimized for 10,000+ concurrent threads) okhttp.pool.maxIdleConnections=1000 okhttp.pool.keepAliveDurationMinutes=5 # Dispatcher limits okhttp.dispatcher.maxRequests=10000 okhttp.dispatcher.maxRequestsPerHost=5000 # HTTP/2 support (enabled by default) okhttp.http2.enabled=true # Rate limiting (0 = disabled) okhttp.rateLimitPerSecond=1000
-
Constructor Summary
ConstructorsConstructorDescriptionAnkaSecureSdk(Properties cliProperties) Constructs the SDK factory using the provided CLI properties. -
Method Summary
Modifier and TypeMethodDescriptionAuthenticates using whatever credential the deployment configured, without being told which.authenticate(AuthSettings settings) Authenticates using a caller-supplied configuration view.authenticateApplication(String clientId, SecretChars clientSecret) Authenticates as an application and returns an authenticated SDK instance.authenticateApplicationWithKeyPair(String clientId, ClientAssertionSigner signer, String expectedAudience) Authenticates a Cryptographic ACTOR that proves itself with a registered key pair.authenticateUser(String username, SecretChars password, String tenantId) Authenticates as a user and returns an authenticated SDK instance.Reports which credentialauthenticate()would use, without using it.
-
Constructor Details
-
AnkaSecureSdk
Constructs the SDK factory using the provided CLI properties.This constructor creates an unauthenticated SDK factory. To obtain an authenticated instance, call
authenticateApplication(String, co.ankatech.ankasecure.sdk.security.SecretChars)orauthenticateUser(String, co.ankatech.ankasecure.sdk.security.SecretChars, String).Typical properties include:
openapi.host– API server hostnameopenapi.port– API server portopenapi.connectTimeoutMs– connection timeout in millisecondsokhttp.pool.maxIdleConnections– connection pool size (default: 1000)okhttp.dispatcher.maxRequests– max concurrent requests (default: 10000)okhttp.http2.enabled– enable HTTP/2 (default: true). Setting it tofalsemakes the true-streaming operations refuse rather than downgrade: they send their request body as a duplex stream, which HTTP/1.1 cannot frame, so a downgrade would deadlock instead of degrading. The refusal is typed and is raised before any request byte is written.The same applies to the SCHEME: HTTP/2 is not negotiated over an
http://origin without prior knowledge, so a cleartext deployment (openapi.scheme=http) cannot use the streaming operations at all. Usehttps.
Properties props = new Properties(); try (var stream = new FileInputStream("cli.properties")) { props.load(stream); } AnkaSecureSdk factory = new AnkaSecureSdk(props); // A secret read from an environment variable leaves a String copy that cannot be // cleared; Console.readPassword() returns a char[] and creates none. SecretChars clientSecret = new SecretChars(System.getenv("ANKASECURE_CLIENT_SECRET").toCharArray()); AuthenticatedSdk sdk = factory.authenticateApplication("my-client-id", clientSecret);- Parameters:
cliProperties- properties loaded from yourcli.propertiesfile; must not benull- Throws:
NullPointerException- ifcliPropertiesisnullInvalidInputException- ifpqc.transport.enabled=trueandpqc.kem.algorithmis not a supported transport KEM
-
-
Method Details
-
authenticateApplication
public AuthenticatedSdk authenticateApplication(String clientId, SecretChars clientSecret) throws AnkaSecureSdkException Authenticates as an application and returns an authenticated SDK instance.This method performs application authentication via client credentials and returns an immutable
AuthenticatedSdkinstance bound to the JWT access token. The returned instance is thread-safe and can be shared across thousands of concurrent threads.Usage Pattern
AnkaSecureSdk factory = new AnkaSecureSdk(props); // A secret read from an environment variable leaves a String copy that cannot be // cleared; Console.readPassword() returns a char[] and creates none. SecretChars clientSecret = new SecretChars(System.getenv("ANKASECURE_CLIENT_SECRET").toCharArray()); AuthenticatedSdk sdk = factory.authenticateApplication("my-client-id", clientSecret); // Use sdk for all operations EncryptResult result = sdk.encrypt("my-key", data); // Share across threads safely executor.submit(() -> sdk.encrypt("key1", data1)); executor.submit(() -> sdk.encrypt("key2", data2));Token Lifecycle
The returned instance remains valid until the JWT token expires. Check
AuthenticatedSdk.isTokenExpiredLocally()before long-running operations and re-authenticate if needed.- Parameters:
clientId- the client identifier issued by the administration console; must not benullclientSecret- the client secret issued by the administration console; must not benull- Returns:
- an authenticated SDK instance with immutable token; never
null - Throws:
AnkaSecureSdkException- if the credentials are invalid, the server is unreachable, or authentication failsNullPointerException- ifclientIdorclientSecretisnull
-
authenticateApplicationWithKeyPair
public AuthenticatedSdk authenticateApplicationWithKeyPair(String clientId, ClientAssertionSigner signer, String expectedAudience) throws AnkaSecureSdkException Authenticates a Cryptographic ACTOR that proves itself with a registered key pair.The assertion is signed HERE, by the caller's signer, and only the signature travels. That is the difference this credential kind buys: the platform holds the public half, so it could not present this workload's identity even if it wanted to.
A fresh assertion is minted on every call, and that is not an optimisation to skip. The server claims each
jtiexactly once, so re-sending one — for instance after a timeout whose request was actually received — is a replay of the client's own credential, refused, and read by everyone as a server fault.- Parameters:
clientId- the actor UUIDsigner- holds the private key; it never leaves the caller's processexpectedAudience- the audience this deployment returned when the key was registered. Passed rather than derived: a client computing it would be guessing at a value the server owns.- Throws:
AnkaSecureSdkException
-
authenticateUser
public AuthenticatedSdk authenticateUser(String username, SecretChars password, String tenantId) throws AnkaSecureSdkException Authenticates as a user and returns an authenticated SDK instance.This method performs user authentication via username/password and returns an immutable
AuthenticatedSdkinstance bound to the JWT access token. Use this in scenarios where user-level context is required instead of application-level authentication.Usage Pattern
AnkaSecureSdk factory = new AnkaSecureSdk(props); SecretChars password = new SecretChars(System.console().readPassword("Password: ")); AuthenticatedSdk sdk = factory.authenticateUser("alice@example.com", password, "my-tenant-id"); // Use sdk for all operations with user context List<KeyMetadata> keys = sdk.listAllKeys(KeyFilters.none());- Parameters:
username- the username (email format); must not benullpassword- the user password; must not benulltenantId- the tenant identifier; must not benull- Returns:
- an authenticated SDK instance with immutable token; never
null - Throws:
AnkaSecureSdkException- if the credentials are invalid, the user does not exist, the tenant does not exist, the server is unreachable, or authentication failsNullPointerException- if any parameter isnull
-
authenticate
Authenticates using whatever credential the deployment configured, without being told which.This is the entry point a workload should use, and the reason the SDK can claim that a consumer authenticating through Auth0 - or Entra, Okta, Keycloak, Cognito, or any other OAuth 2.0 / OIDC authorization server - runs the same code as one using an ANKASecure client secret. There is no provider argument and no mechanism argument, because a provider is not something application code should know: it is a property of where the application is deployed, and it changes without the application changing.
The credential is read from this factory's properties, with the process environment as a fallback, and resolved by
TokenSourceChain- whose javadoc records the order and why it is that order. Nothing is discovered from the server: the SDK asks the deployment no questions about which identity provider a tenant uses, because the answer would disclose tenant configuration, would undo a timing control the platform deliberately built, and would let whoever owns that configuration name the endpoint a caller's credential is posted to.- Returns:
- an authenticated SDK instance; never
null - Throws:
AnkaSecureSdkException- if nothing is configured, if the configuration is ambiguous or incomplete, or if the platform refuses the credential
-
authenticate
Authenticates using a caller-supplied configuration view.For callers holding a credential that is not in their properties file - one decrypted at runtime, one handed over by a secret manager - so they can supply what they have without writing it down first, and without this class needing to know where it came from.
- Parameters:
settings- the configuration to resolve the credential from; must not benull- Returns:
- an authenticated SDK instance; never
null - Throws:
AnkaSecureSdkException- if nothing is configured, if the configuration is ambiguous or incomplete, or if the platform refuses the credentialNullPointerException- ifsettingsisnull
-
configuredCredential
Reports which credentialauthenticate()would use, without using it.Reads no file, starts no process and opens no connection, so an operator can ask what is configured - in a status command, in a health check - and get an answer that costs nothing and changes nothing.
- Returns:
- the link that would answer, or empty when no credential is configured
- Throws:
AnkaSecureSdkException- if two external token sources are configured at once
-