Class AuthenticatedSdk

java.lang.Object
co.ankatech.ankasecure.sdk.AuthenticatedSdk
All Implemented Interfaces:
AutoCloseable

public final class AuthenticatedSdk extends Object implements AutoCloseable
Authenticated SDK instance with immutable JWT token.

This class represents an authenticated session with the ANKASecure© API. Each instance is bound to a specific JWT access token and provides all cryptographic operations available to that authenticated identity.

Thread Safety

Fully thread-safe. Instances are immutable and can be safely shared across thousands of concurrent threads. The underlying HTTP client uses connection pooling and HTTP/2 multiplexing for efficient resource usage.

Lifecycle Management

Instances are created by AnkaSecureSdk.authenticateApplication(String, co.ankatech.ankasecure.sdk.security.SecretChars) or AnkaSecureSdk.authenticateUser(String, co.ankatech.ankasecure.sdk.security.SecretChars, String). They remain valid until the token expires. Use isTokenExpiredLocally() to check validity.

Token Expiry Handling


 AuthenticatedSdk sdk = factory.authenticateApplication(clientId, clientSecret);

 // Use for multiple operations
 EncryptResult r1 = sdk.encrypt("key1", data1);
 EncryptResult r2 = sdk.encrypt("key2", data2);

 // Check expiry before long-running tasks
 if (sdk.isTokenExpiredLocally()) {
     sdk = factory.authenticateApplication(clientId, clientSecret);
 }

 // Continue with new token
 EncryptResult r3 = sdk.encrypt("key3", data3);
 

Concurrent Usage Pattern


 // Single AuthenticatedSdk shared across all threads
 AuthenticatedSdk sdk = factory.authenticateApplication(clientId, clientSecret);

 // Virtual threads (Java 21+) - 10,000+ concurrent operations
 try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
     List<CompletableFuture<EncryptResult>> futures = dataList.stream()
         .map(data -> CompletableFuture.supplyAsync(
             () -> sdk.encrypt("my-key", data),
             executor
         ))
         .toList();

     List<EncryptResult> results = futures.stream()
         .map(CompletableFuture::join)
         .toList();
 }
 

Memory Characteristics

  • Instance size: ~250 bytes (token string + references)
  • Shared HTTP client: single connection pool across all instances
  • 10,000 instances: ~2.5 MB total overhead

File-Output Overwrite Policy

Every file-output method (the *File, *FileCompact, *FileStream and combined/migration families) quarantines its result to a sidecar (<output>.part) and atomically renames it onto output. By default (OverwritePolicy.FAIL_IF_EXISTS) an operation fails if output already exists, throwing DestinationExistsException and leaving the pre-existing file untouched. To replace an existing destination, derive an overwrite-enabled instance with withOverwrite() (or withOverwritePolicy(OverwritePolicy.OVERWRITE)); the derived instance shares this instance's JWT and HTTP client with no re-authentication.

A failure of the caller's own files — an input that is missing or unreadable, a result or working file that cannot be written or moved into place — is reported by every file method as LocalFileException, naming the file and whether it had to be read or written. It is never classified as a network failure (IO / TIMEOUT).

Stateful Signing Keys

A stateful hash-based key (XMSS, XMSS^MT, LMS/HSS) holds a finite number of one-time signatures and the server spends one per signature. Every method that produces a signature — sign*, signThenEncrypt*, resign* (with the new key) and convertPkcs7ToJose* — can therefore be refused with an HTTP 409 carrying one of two error codes:

Either refusal is decided before any output exists: a file method writes no result. On a streaming route and on the PKCS#7 conversion the one-time signature is reserved before the request body is processed, so a request then refused for its body spends one index without producing a signature.

See Also:
  • Method Details

    • close

      public void close()
      Clears the bearer token from the underlying AnkaSecureOpenApiClient's HttpBearerAuth. After this call:
      • Subsequent operations on this instance throw IllegalStateException.
      • The bearer token is no longer reachable via the OkHttp request chain.

      Idempotent. Use try-with-resources for deterministic cleanup:

      
         try (AuthenticatedSdk sdk = factory.authenticateApplication(clientId, clientSecret)) {
             sdk.encrypt(kid, data);
         } // close() runs here, clearing the bearer token
       

      Note: the accessToken field is a String (immutable, interned-eligible) and cannot be reliably zeroized — a known JVM-level limitation. close() reduces the in-memory lifetime by clearing the OkHttp interceptor reference, but the String itself remains until GC.

      Wither ownership

      Instances derived via withOverwritePolicy(OverwritePolicy) / withOverwrite() SHARE the root's single bearer token and HTTP client. Only the factory-created ROOT owns the token: closing a DERIVED instance marks that instance closed but MUST NOT clear the token still in use by the root (and by any sibling derived instance), preventing a use-after-free of a zeroized secret. Closing the ROOT clears the shared token, ending the session for all views.

      Specified by:
      close in interface AutoCloseable
    • overwritePolicy

      public OverwritePolicy overwritePolicy()
      Returns the file-output overwrite policy in effect for this instance.
      Returns:
      the OverwritePolicy (default OverwritePolicy.FAIL_IF_EXISTS)
      See Also:
    • withOverwritePolicy

      public AuthenticatedSdk withOverwritePolicy(OverwritePolicy overwritePolicy)
      Returns a NEW immutable AuthenticatedSdk that applies overwritePolicy to every file-output operation, sharing this instance's JWT and HTTP client.

      This is a pure, local, immutable derivation: it performs NO network round-trip and NO re-authentication. The derived instance shares the SAME JWT reference and the SAME HTTP client (there is no plaintext copy of the token). Per the wither ownership model, only the original factory-created instance owns the token — closing the derived instance never clears the token still in use by the original.

      Parameters:
      overwritePolicy - the overwrite policy for the derived instance; must not be null
      Returns:
      a distinct immutable instance with the requested policy; never null
      Throws:
      NullPointerException - if overwritePolicy is null
      See Also:
    • withOverwrite

      public AuthenticatedSdk withOverwrite()
      Convenience wither equivalent to withOverwritePolicy(OverwritePolicy.OVERWRITE): returns a NEW immutable AuthenticatedSdk whose file-output operations atomically replace an existing destination.

      Like withOverwritePolicy(OverwritePolicy), this performs NO network round-trip and NO re-authentication and shares the same JWT and HTTP client (see the wither ownership model).

      Returns:
      a distinct immutable instance with OverwritePolicy.OVERWRITE; never null
      See Also:
    • isClosed

      public boolean isClosed()
      Returns:
      true if close() has been called; never throws
    • isTokenExpiredLocally

      public boolean isTokenExpiredLocally()
      Checks whether the access token has expired, or is about to, according to its local exp claim only.

      This is a local, offline check. It extracts the exp claim from the JWT payload and compares it with the current time; it does NOT contact the server and therefore does NOT detect server-side revocation — a token that was revoked (logout / blacklist) or superseded by the subject's revocation epoch (bulk revoke, e.g. an admin force-reset, a suspend/delete, or a role scope shrink) is still unexpired locally and returns false here while the server rejects it. To detect that case, perform an operation and handle TokenRevokedException (thrown on a 401), then re-authenticate.

      Usage Pattern

      
       AuthenticatedSdk sdk = factory.authenticateApplication(id, clientSecret);
      
       // Before an expensive batch operation, cheaply skip a definitely-expired token
       if (sdk.isTokenExpiredLocally()) {
           sdk = factory.authenticateApplication(id, clientSecret);
       }
      
       try {
           processBatch(sdk, largeDataset);
       } catch (TokenRevokedException revoked) {
           // The server rejected the still-unexpired token — re-authenticate and retry.
           sdk = factory.authenticateApplication(id, clientSecret);
       }
       

      Clock skew

      A token expiring within 60 seconds is already reported as expired. The comparison is deliberately not exact: the caller's clock and the server's clock are not the same clock, and a token still has to survive the flight time of the operation this check was performed for. Reporting a token with a second left as usable would produce a 401 at the server that is indistinguishable from a revocation, which is the harder failure to diagnose. The window matches the platform's own token refresh window and the clock-skew tolerance it admits on a client assertion.

      Returns:
      true if the token is malformed, already past its exp, or expiring within the 60-second skew window; false if it is still comfortably valid by its local exp claim (which does not imply the server will accept it)
    • getTokenExpiration

      public Instant getTokenExpiration()
      Extracts the expiration time from the JWT token.

      Decodes the JWT payload (Base64URL) and reads the exp claim (seconds since Unix epoch). Returns null if the token is malformed.

      Returns:
      the token expiration as Instant, or null if malformed
    • encrypt

      public EncryptResult encrypt(String kid, byte[] plaintext) throws AnkaSecureSdkException
      Encrypts raw bytes using the key identified by kid.

      The key type determines the algorithm automatically: asymmetric keys (ML-KEM, RSA, ECC) perform key encapsulation + symmetric encryption; symmetric keys (AES) encrypt directly. The result is a JWE (RFC 7516) string containing the ciphertext and all metadata needed for decryption, in the serialization that carries every recipient the server returned:

      • Single-recipient key (every non-composite key): Compact Serialization (§7.1), five dot-separated base64url segments.
      • Composite KEM key: General JSON Serialization (§7.2), one recipient per component. Compact Serialization holds exactly one recipient, so it cannot represent this result.

      decrypt(String) accepts either form.

      This method is suitable for in-memory payloads up to ~100 MB. For larger files, use encryptFileStream(String, Path, Path) to avoid loading the entire payload into memory.

      Example

      
       byte[] plaintext = "Sensitive data".getBytes(StandardCharsets.UTF_8);
       EncryptResult result = sdk.encrypt("my-ml-kem-768-key", plaintext);
      
       // The JWE token is self-contained and can be stored or transmitted
       String jweToken = result.getJweToken();
      
       // Decrypt later
       DecryptResult decrypted = sdk.decrypt(jweToken);
       byte[] recovered = decrypted.getDecryptedData();
       
      Parameters:
      kid - key identifier referencing a key stored in ANKASecure©; must not be null
      plaintext - the raw bytes to encrypt; must not be null
      Returns:
      encryption result containing the JWE token (Compact for a single-recipient key, General JSON for a composite KEM key) and operation metadata; never null
      Throws:
      AnkaSecureSdkException - if the key identified by kid does not exist, does not support encryption, the authenticated client lacks permission, or the server returns an error
      NullPointerException - if kid or plaintext is null
      See Also:
    • encryptFileCompact

      public EncryptResult encryptFileCompact(String kid, Path input, Path output) throws AnkaSecureSdkException
      Encrypts a file with forced compact format, i.e. in one non-streaming request. Use when you need a single-line string for API/JSON transmission. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: JSON responses, HTTP headers, query parameters
      • Storage: Single database field (text/varchar)
      • Interoperability: JWE standard format (RFC 7516)

      Output Format

       eyJhbGc...header...XVCJ9.eyJkYXRh...encrypted-payload...9.dBjftJ...tag...
       

      Single-line base64url string, suitable for text-based transmission. That is the RFC 7516 §7.1 Compact Serialization, written for every single-recipient key. A composite KEM key yields one recipient per component, which Compact Serialization cannot hold, so the file then carries the single-line RFC 7516 §7.2 General JSON Serialization instead. decryptFile(Path, Path) and decryptFileCompact(Path, Path) read either form.

      Alternative Methods

      Example

      
       // Encrypt small document for API transmission
       EncryptResult result = sdk.encryptFileCompact(
           "my-ml-kem-key",
           Path.of("contract.pdf"),
           Path.of("contract.jwe")
       );
      
       System.out.println("Algorithm: " + result.getAlgorithmUsed());
       
      Parameters:
      kid - key identifier for encryption; must not be null
      input - input file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file (.jwe recommended); receives the Compact JWE, or the General JSON JWE for a composite KEM key; must not be null
      Returns:
      encryption result with metadata; its jweToken is exactly what was written; never null
      Throws:
      AnkaSecureSdkException - if the input exceeds the server's configured compact limit (HTTP 413) or operation fails
      See Also:
    • encryptFile

      public EncryptResult encryptFile(String kid, Path input, Path output) throws AnkaSecureSdkException
      Encrypts a file. Recommended for most use cases. Automatically selects the optimal format based on the server-discovered compact payload limit (below the limit = compact, at or above = streaming).

      This method automatically selects the optimal format:

      • Files below the server's maxPlaintextBytes: a single-line JWE string
      • Files at or above that limit: Streaming JWE format (binary chunks)

      Output Format Below the Limit

      The file holds the JWE the server returned, in the serialization that carries all of it:

      • Single-recipient key (every non-composite key): RFC 7516 §7.1 Compact Serialization, five dot-separated base64url segments.
      • Composite KEM key: RFC 7516 §7.2 General JSON Serialization, a JSON object with one entry in recipients per component. Compact Serialization holds exactly one recipient and cannot represent it.

      decryptFile(Path, Path) reads either form, telling them apart by content.

      Why Use This Method?

      You don't need to worry about file sizes or server limits. The threshold is discovered from the server (not hardcoded) and cached briefly; if a compact request is still rejected as too large (HTTP 413) the SDK transparently retries via streaming, ensuring your encryption always succeeds regardless of file size.

      When to Use Explicit Formats

      Use encryptFileCompact(String, Path, Path) or encryptFileStream(String, Path, Path) only when you specifically need a particular output format (e.g., compact JWE for API transmission).

      Example

      
       // Encrypt any file - works for both small and large files
       EncryptResult result = sdk.encryptFile(
           "my-ml-kem-key",
           Path.of("data.bin"),
           Path.of("data.jwe")
       );
      
       System.out.println("Encrypted with: " + result.getAlgorithmUsed());
       
      Parameters:
      kid - key identifier for encryption; must not be null
      input - input file (any size); must not be null
      output - output file (.jwe or .jwet): Compact JWE, General JSON JWE (composite KEM key) or streaming JWET, as described above; must not be null. By default fails if output exists; call withOverwrite() to overwrite.
      Returns:
      encryption result with metadata; never null
      Throws:
      AnkaSecureSdkException - if operation fails
      LocalFileException - if the input cannot be read or the output cannot be written
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default)
      See Also:
    • encryptFileStream

      public EncryptResult encryptFileStream(String kid, Path input, Path output) throws AnkaSecureSdkException
      Encrypts a file with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary output is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When base64 encoding overhead is undesirable
      • Progressive processing: Start processing before full upload

      Output Format

      Binary streaming format (.jwet file) with multipart chunks. Not suitable for text-based transmission (API/JSON).

      Alternative Methods

      Example

      
       // Encrypt large video file
       EncryptResult result = sdk.encryptFileStream(
           "my-ml-kem-key",
           Path.of("movie.mp4"),
           Path.of("movie.jwet")
       );
       
      Parameters:
      kid - key identifier for encryption; must not be null
      input - input file (any size, up to 100MB recommended); must not be null
      output - output file (.jwet recommended); must not be null
      Returns:
      encryption result with metadata; never null
      Throws:
      AnkaSecureSdkException - if operation fails
      See Also:
    • decrypt

      public DecryptResult decrypt(String ciphertextJwe) throws AnkaSecureSdkException
      Decrypts a JWE (RFC 7516) token to recover the original plaintext.

      Accepts both serializations the SDK produces, detected from the content: Compact (§7.1, five dot-separated segments) and General JSON (§7.2, a JSON object, as encrypt(String, byte[]) returns for a composite KEM key).

      The JWE token is self-contained and includes all metadata required for decryption (algorithm, encrypted key, IV, ciphertext, authentication tag). The server resolves the correct decryption key based on the kid header in the JWE.

      This method is suitable for in-memory payloads up to ~100 MB. For larger files, use decryptFileStream(Path, Path) to avoid loading the entire payload into memory.

      Example

      
       // Decrypt a previously encrypted message
       String jweToken = "eyJhbGciOiJNTC1LRU0tNzY4IiwiZW5jIjoiQTI1NkdDTSIsImtpZCI6Im15LWtleSJ9...";
       DecryptResult result = sdk.decrypt(jweToken);
      
       byte[] plaintext = result.getDecryptedData();
       String message = new String(plaintext, StandardCharsets.UTF_8);
      
       System.out.println("Decrypted using key: " + result.getMeta().getKeyRequested());
       System.out.println("Algorithm: " + result.getMeta().getAlgorithmUsed());
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decrypt(String, String).

      Parameters:
      ciphertextJwe - the Compact or General JSON JWE string to decrypt; must not be null
      Returns:
      decryption result containing the plaintext bytes and operation metadata; never null
      Throws:
      AnkaSecureSdkException - if the JWE is neither a well-formed Compact nor a General JSON JWE (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), the decryption key is not accessible to the authenticated client, decryption fails (authentication tag mismatch), or the server returns an error. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if ciphertextJwe is null
      See Also:
    • decrypt

      public DecryptResult decrypt(String recipientKid, String ciphertextJwe) throws AnkaSecureSdkException
      Decrypts a JWE token string with the recipient the caller names.

      Same operation as decrypt(String); recipientKid selects the recipient of a JWE addressed to several keys (RFC 7516 §7.2.1).

      Choosing the recipient

      A JWE addressed to several keys (several recipients, each naming its key in its own header, and no kid in protected) is decrypted with exactly the recipient recipientKid names: one grant and one key-lifecycle decision are taken, on that key only, and no other recipient is ever tried. For a JWE that names one key (a single recipient, or a composite key's JWE) recipientKid may be null; when present it must equal that key.

      Example

      
       // A board-minutes JWE addressed to three custodians; this caller holds "custodian-b"
       DecryptResult result = sdk.decrypt("custodian-b", boardMinutesJwe);
       byte[] plaintext = result.getDecryptedData();
       System.out.println("Decrypted with: " + result.getMeta().getKeyRequested()
               + " v" + result.getMeta().getMaterialVersion());
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      ciphertextJwe - the Compact or General JSON JWE string to decrypt; must not be null
      Returns:
      decryption result containing the plaintext bytes and the metadata of the key used; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for a JWE addressed to several keys or names no recipient of the JWE; HTTP 403 when the authenticated client holds no grant on that key; or when the operation otherwise fails. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if ciphertextJwe is null
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • decryptFileCompact

      public DecryptResultMetadata decryptFileCompact(Path input, Path output) throws AnkaSecureSdkException
      Decrypts a file with forced compact format. Use when you need to decrypt a single-line JWE string. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: Decrypting JWE from JSON responses or HTTP headers
      • Storage: Decrypting from single database field (text/varchar)
      • Interoperability: JWE standard format (RFC 7516)

      Input Format

      Expects a single-line JWE, as written by encryptFileCompact(String, Path, Path): RFC 7516 §7.1 Compact Serialization, or §7.2 General JSON Serialization for a composite KEM key. The form is detected from the content.

      Alternative Methods

      Example

      
       // Decrypt compact JWE file
       DecryptResultMetadata result = sdk.decryptFileCompact(
           Path.of("contract.jwe"),
           Path.of("contract.pdf")
       );
      
       System.out.println("Decrypted using key: " + result.getKeyRequested());
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptFileCompact(String, Path, Path).

      Parameters:
      input - Compact or General JSON JWE file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file for decrypted plaintext; must not be null
      Returns:
      decryption result metadata; never null
      Throws:
      AnkaSecureSdkException - if the input is neither a well-formed Compact nor a General JSON JWE (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), exceeds the server's configured compact limit (HTTP 413), or the operation fails
      See Also:
    • decryptFileCompact

      public DecryptResultMetadata decryptFileCompact(String recipientKid, Path input, Path output) throws AnkaSecureSdkException
      Decrypts a Compact or General JSON JWE file with the recipient the caller names.

      Same operation as decryptFileCompact(Path, Path); recipientKid selects the recipient of a JWE addressed to several keys.

      Choosing the recipient

      A JWE addressed to several keys (several recipients, each naming its key in its own header, and no kid in protected) is decrypted with exactly the recipient recipientKid names: one grant and one key-lifecycle decision are taken, on that key only, and no other recipient is ever tried. For a JWE that names one key (a single recipient, or a composite key's JWE) recipientKid may be null; when present it must equal that key.

      Example

      
       DecryptResultMetadata meta = sdk.decryptFileCompact("custodian-b",
               Path.of("board-minutes.jwe"), Path.of("board-minutes.pdf"));
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      input - Compact or General JSON JWE file (up to the server's configured compact limit); must not be null
      output - output file for the decrypted plaintext; must not be null
      Returns:
      metadata of the key the JWE was decrypted with; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for a JWE addressed to several keys or names no recipient of the JWE; HTTP 403 when the authenticated client holds no grant on that key; or when the operation otherwise fails
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • decryptFile

      public DecryptResultMetadata decryptFile(Path input, Path output) throws AnkaSecureSdkException
      Decrypts a file. Recommended for most use cases. Automatically selects the decryption path from the input envelope's actual format, not its size.

      This method inspects the input file's structure and routes accordingly:

      Why Format, Not Size?

      The input here is an envelope, not plaintext. A compact JWE is roughly twice the size of the plaintext it wraps, so its size may exceed the server's compact plaintext ceiling even though it is a compact envelope that must be decrypted via the compact path. Selecting by format guarantees that any file produced by encryptFile/encryptFileCompact is always decryptable, and that a genuinely detached/streaming artifact is always decrypted via streaming.

      When to Use Explicit Formats

      Use decryptFileCompact(Path, Path) or decryptFileStream(Path, Path) only when you specifically need to force a particular input format.

      Example

      
       // Decrypt any file - works for both small and large files
       DecryptResultMetadata result = sdk.decryptFile(
           Path.of("data.jwe"),
           Path.of("data.bin")
       );
      
       System.out.println("Decrypted using key: " + result.getKeyRequested());
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptFile(String, Path, Path).

      Parameters:
      input - input JWE file (any size): Compact, General JSON or streaming JWET; must not be null
      output - output file for decrypted plaintext; must not be null. By default fails if output exists; call withOverwrite() to overwrite.
      Returns:
      decryption result metadata; never null
      Throws:
      AnkaSecureSdkException - if the input is not a streaming JWET artifact (it does not begin with the multipart delimiter) and is not a well-formed Compact or General JSON JWE (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), or the operation fails. A General JSON input naming a header parameter both in protected and in a recipient header is refused with HTTP 400 (RFC 7516 §7.2.1: the header locations are disjoint); a streaming JWET artifact is refused as decryptFileStream(Path, Path) describes.
      LocalFileException - if the input cannot be read or the output cannot be written
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default)
      See Also:
    • decryptFile

      public DecryptResultMetadata decryptFile(String recipientKid, Path input, Path output) throws AnkaSecureSdkException
      Decrypts a file of any format with the recipient the caller names, selecting the path from the input's actual format exactly as decryptFile(Path, Path) does.

      Choosing the recipient

      A JWE addressed to several keys (several recipients, each naming its key in its own header, and no kid in protected) is decrypted with exactly the recipient recipientKid names: one grant and one key-lifecycle decision are taken, on that key only, and no other recipient is ever tried. For a JWE that names one key (a single recipient, or a composite key's JWE) recipientKid may be null; when present it must equal that key.

      Example

      
       DecryptResultMetadata meta = sdk.decryptFile("custodian-b",
               Path.of("board-minutes.jwe"), Path.of("board-minutes.pdf"));
       System.out.println("Decrypted with: " + meta.getKeyRequested());
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      input - input JWE file (any size): Compact, General JSON or streaming JWET; must not be null
      output - output file for the decrypted plaintext; must not be null
      Returns:
      metadata of the key the JWE was decrypted with; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for a JWE addressed to several keys or names no recipient of the JWE; HTTP 403 when the authenticated client holds no grant on that key; or when the operation otherwise fails. A General JSON input naming a header parameter both in protected and in a recipient header is refused with HTTP 400 (RFC 7516 §7.2.1: the header locations are disjoint); a streaming JWET artifact is refused as decryptFileStream(Path, Path) describes.
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • decryptFileStream

      public DecryptResultMetadata decryptFileStream(Path input, Path output) throws AnkaSecureSdkException
      Decrypts a file with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary input is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When decrypting streaming JWE format (.jwet files)
      • Progressive processing: Start processing before full download

      Input Format

      Binary streaming format (.jwet file) with multipart chunks. Typically created by encryptFileStream(String, Path, Path).

      Alternative Methods

      Example

      
       // Decrypt large video file
       DecryptResultMetadata result = sdk.decryptFileStream(
           Path.of("movie.jwet"),
           Path.of("movie.mp4")
       );
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptFileStream(String, Path, Path).

      Parameters:
      input - streaming JWE file (any size, up to 100MB recommended); must not be null
      output - output file for decrypted plaintext; must not be null
      Returns:
      decryption result metadata; never null
      Throws:
      AnkaSecureSdkException - if operation fails. The JWET header is refused with HTTP 422 and no payload byte, before any key is looked up, when it carries a root member besides protected, recipients, iv, ciphertext, tag and, on a composite key's header, anka_composite_mode (JWE_ROOT_MEMBER_FORBIDDEN; a JSON aad member and a shared unprotected header included) or a recipient member besides header and encrypted_key (JWE_RECIPIENT_MEMBER_FORBIDDEN). A header parameter name present both in protected and in a recipient header (RFC 7516 §7.2.1) is refused with HTTP 422 for a single-recipient JWE and HTTP 400 for a composite JWE or one addressed to several keys.
      See Also:
    • decryptFileStream

      public DecryptResultMetadata decryptFileStream(String recipientKid, Path input, Path output) throws AnkaSecureSdkException
      Decrypts a streaming JWET file with the recipient the caller names; sent as the recipientKid query parameter of /crypto/stream/decrypt.

      Choosing the recipient

      A JWE addressed to several keys (several recipients, each naming its key in its own header, and no kid in protected) is decrypted with exactly the recipient recipientKid names: one grant and one key-lifecycle decision are taken, on that key only, and no other recipient is ever tried. For a JWE that names one key (a single recipient, or a composite key's JWE) recipientKid may be null; when present it must equal that key.

      Example

      
       DecryptResultMetadata meta = sdk.decryptFileStream("custodian-b",
               Path.of("archive.jwet"), Path.of("archive.tar"));
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      input - streaming JWE file; must not be null
      output - output file for the decrypted plaintext; must not be null
      Returns:
      metadata of the key the JWE was decrypted with; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for a JWE addressed to several keys or names no recipient of the JWE; HTTP 403 when the authenticated client holds no grant on that key; or when the operation otherwise fails. The JWET header is refused with HTTP 422 and no payload byte, before any key is looked up, when it carries a root member besides protected, recipients, iv, ciphertext, tag and, on a composite key's header, anka_composite_mode (JWE_ROOT_MEMBER_FORBIDDEN; a JSON aad member and a shared unprotected header included) or a recipient member besides header and encrypted_key (JWE_RECIPIENT_MEMBER_FORBIDDEN). A header parameter name present both in protected and in a recipient header (RFC 7516 §7.2.1) is refused with HTTP 422 for a single-recipient JWE and HTTP 400 for a composite JWE or one addressed to several keys.
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • sign

      public SignResult sign(String kid, byte[] data) throws AnkaSecureSdkException
      Signs raw bytes using the key identified by kid.

      Creates a compact JWS (RFC 7515) containing the signature and all metadata needed for verification. The signing algorithm is determined by the key type: ML-DSA, ECDSA, or RSA-PSS.

      This method is suitable for in-memory payloads up to ~100 MB. For larger files, use signFileStream(String, Path, Path) to avoid loading the entire payload into memory.

      Example

      
       byte[] document = Files.readAllBytes(Path.of("/contracts/agreement.pdf"));
       SignResult result = sdk.sign("legal-ml-dsa-key", document);
      
       // The JWS token contains the signature and can be verified later
       String jwsToken = result.getJwsToken();
      
       // Verify later
       VerifySignatureResult verified = sdk.verifySignature(jwsToken);
       if (verified.isValid()) {
           System.out.println("Signature verified using key: " + verified.getSignatureResults().get(0).getKid());
       }
       
      Parameters:
      kid - key identifier referencing a signing key stored in ANKASecure©; must not be null
      data - the raw bytes to sign; must not be null
      Returns:
      signature result containing the JWS token and operation metadata; never null
      Throws:
      AnkaSecureSdkException - if the key identified by kid does not exist, does not support signing, the authenticated client lacks permission, or the server returns an error
      NullPointerException - if kid or data is null
      See Also:
    • sign

      public SignResult sign(String kid, byte[] data, JwsSerialization serialization) throws AnkaSecureSdkException
      Signs raw bytes, explicitly selecting the JWS serialization.

      Ergonomic overload of sign(String, byte[]). Pass JwsSerialization.COMPACT or JwsSerialization.JSON to request that representation; passing null is equivalent to sign(String, byte[]) (the server chooses the standards-correct representation). This axis never adds or drops a qualified timestamp.

      Parameters:
      kid - key identifier referencing a signing key; must not be null
      data - the raw bytes to sign; must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      signature result; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 when COMPACT is requested but the deployment mandates a qualified timestamp
      NullPointerException - if kid or data is null
      See Also:
    • signFileCompact

      public SignResult signFileCompact(String kid, Path input, Path output) throws AnkaSecureSdkException
      Signs a file with forced compact format. Use when you need a single-line string for API/JSON transmission. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: JSON responses, HTTP headers, query parameters
      • Storage: Single database field (text/varchar)
      • Interoperability: JWS standard format (RFC 7515)

      Output Format

       eyJhbGc...header...XVCJ9.eyJkYXRh...payload...9.dBjftJ...signature...
       

      Single-line base64url string with embedded payload, suitable for text-based transmission.

      Alternative Methods

      Example

      
       // Sign small document for API transmission
       SignResult result = sdk.signFileCompact(
           "my-ml-dsa-key",
           Path.of("contract.pdf"),
           Path.of("contract.jws")
       );
      
       System.out.println("Algorithm: " + result.getAlgorithmUsed());
       
      Parameters:
      kid - key identifier for signing; must not be null
      input - input file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file (.jws recommended); must not be null
      Returns:
      signature result with metadata; never null
      Throws:
      AnkaSecureSdkException - if the input exceeds the server's configured compact limit (HTTP 413) or operation fails
      See Also:
    • signFileCompact

      public SignResult signFileCompact(String kid, Path input, Path output, JwsSerialization serialization) throws AnkaSecureSdkException
      Signs a file (attached), explicitly selecting the JWS serialization.

      Ergonomic overload of signFileCompact(String, Path, Path). Passing null defers to the server. When the deployment mandates a qualified timestamp, the written output is a JWS-JSON artifact carrying the JAdES sigTst (it is never collapsed to a compact string that would drop the stamp).

      Parameters:
      kid - key identifier for signing; must not be null
      input - input file (max 5MB); must not be null
      output - output file; must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      signature result; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 when COMPACT is requested but the deployment mandates a qualified timestamp
      See Also:
    • signFile

      public SignResult signFile(String kid, Path input, Path output) throws AnkaSecureSdkException
      Signs a file. Recommended for most use cases. Automatically selects optimal format based on file size (<5MB = compact, ≥5MB = streaming).

      This method automatically selects the optimal format:

      • Files below the server's maxPlaintextBytes: Compact JWS format (single-line string)
      • Files at or above that limit: Streaming JWS format (detached signature)

      Why Use This Method?

      You don't need to worry about file sizes or server limits. The threshold is discovered from the server (not hardcoded) and cached briefly; if a compact request is still rejected as too large (HTTP 413) the SDK transparently retries via streaming. This method handles everything automatically, ensuring your signing operation always succeeds regardless of file size.

      When to Use Explicit Formats

      Use signFileCompact(String, Path, Path) or signFileStream(String, Path, Path) only when you specifically need a particular output format (e.g., compact JWS for API transmission).

      Example

      
       // Sign any file - works for both small and large files
       SignResult result = sdk.signFile(
           "my-ml-dsa-key",
           Path.of("document.pdf"),
           Path.of("document.jws")
       );
      
       System.out.println("Signed with: " + result.getAlgorithmUsed());
       
      Parameters:
      kid - key identifier for signing; must not be null
      input - input file (any size); must not be null
      output - output file (.jws or .jwst); must not be null
      Returns:
      signature result with metadata; never null

      By default fails if output exists; call withOverwrite() to overwrite.

      Throws:
      AnkaSecureSdkException - if operation fails
      LocalFileException - if the input cannot be read or the output cannot be written
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default)
      See Also:
    • signFileStream

      public SignResult signFileStream(String kid, Path input, Path output) throws AnkaSecureSdkException
      Signs a file with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary output is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When detached signature format is acceptable
      • Progressive processing: Start processing before full upload

      Output Format

      Detached JWS signature (.jwst file) where payload is stored separately. Suitable for large files where embedding the payload in the signature would be impractical.

      Alternative Methods

      Example

      
       // Sign large archive file
       SignResult result = sdk.signFileStream(
           "my-ml-dsa-key",
           Path.of("backup.tar.gz"),
           Path.of("backup.tar.gz.jwst")
       );
       
      Parameters:
      kid - key identifier for signing; must not be null
      input - input file (any size, up to 100MB recommended); must not be null
      output - output file (.jwst recommended); must not be null
      Returns:
      signature result with metadata; never null
      Throws:
      AnkaSecureSdkException - if operation fails
      See Also:
    • verifySignature

      public VerifySignatureResult verifySignature(Path jwsFile) throws AnkaSecureSdkException
      Verifies a JWS signature from a file, accepting either the compact form or the JWS-JSON form (serialization-agnostic).

      The file may contain an RFC 7515 §7.1 compact string OR an RFC 7515 §7.2 JWS-JSON object (which MAY carry a JAdES qualified timestamp in its unprotected header). The correct representation is detected automatically. Fails if the file is ≥5MB (server limit).

      Alternative Methods

      Note: No auto-detection method available because streaming verification requires separate data and signature file parameters.

      Example

      
       // Verify a compact or JWS-JSON signature file
       VerifySignatureResult result = sdk.verifySignature(
           Path.of("document.jws")
       );
      
       if (result.isValid()) {
           result.getSignatureResults().forEach(signer ->
               System.out.println("Signed by: " + signer.getKid() + " v" + signer.getMaterialVersion()));
       }
       // Informational: never affects isValid()
       if (result.getQualifiedTimestamp() != null) {
           System.out.println("Qualified timestamp trust: "
                   + result.getQualifiedTimestamp().getTrustStatus());
       }
       
      Parameters:
      jwsFile - compact or JWS-JSON signature file; must not be null
      Returns:
      verification result; never null
      Throws:
      AnkaSecureSdkException - if the input exceeds the server's configured compact limit (HTTP 413) or operation fails
      See Also:
    • verifySignature

      public VerifySignatureResult verifySignature(String jws) throws AnkaSecureSdkException
      Verifies an attached JWS (RFC 7515) signature held in memory.

      The input may be the RFC 7515 §7.1 Compact Serialization or the §7.2 JWS JSON Serialization — which carries a JAdES qualified timestamp — detected from the content. The JWS is self-contained and includes all metadata required for verification (algorithm, signature, protected header). The server resolves the verification key from the kid it carries.

      Example

      
       String jwsToken = "eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJteS1rZXkifQ...";
       VerifySignatureResult result = sdk.verifySignature(jwsToken);
      
       if (result.isValid()) {
           System.out.println("Signature verified");
           result.getSignatureResults().forEach(signer ->
               System.out.println("Signed by key: " + signer.getKid() + " (" + signer.getAlgorithm() + ")"));
       } else {
           System.out.println("Signature verification failed");
       }
       
      Parameters:
      jws - the Compact or JWS JSON Serialization string to verify; must not be null
      Returns:
      verification result containing validity status and metadata; never null
      Throws:
      AnkaSecureSdkException - if the JWS is not a well-formed Compact or JWS JSON Serialization (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), the verification key is not accessible, or the server returns an error
      NullPointerException - if jws is null
      See Also:
    • verifySignatureStream

      public VerifySignatureResult verifySignatureStream(Path dataFile, Path signatureFile) throws AnkaSecureSdkException
      Verifies a detached JWS signature with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary output is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When verifying detached JWS signature format (.jwst files)
      • Progressive processing: Start verification before full upload

      Input Format

      Detached JWS signature (.jwst file) + separate data file. Typically created by signFileStream(String, Path, Path).

      Alternative Methods

      Note: No auto-detection method available because streaming verification requires separate data and signature file parameters.

      Example

      
       // Verify detached signature for large file
       VerifySignatureResult result = sdk.verifySignatureStream(
           Path.of("backup.tar.gz"),        // Original data
           Path.of("backup.tar.gz.jwst")    // Detached signature
       );
      
       if (result.isValid()) {
           System.out.println("Signature valid!");
       }
       
      Parameters:
      dataFile - original data file (any size, up to 100MB recommended); must not be null
      signatureFile - detached JWS signature file; must not be null
      Returns:
      verification result; never null
      Throws:
      AnkaSecureSdkException - if operation fails
      See Also:
    • listKeys

      public KeyPage listKeys(KeyFilters filters, int page, int size) throws AnkaSecureSdkException
      Lists ONE PAGE of the keys accessible to the authenticated client.

      Filters apply server-side BEFORE pagination, so the returned totals are filtered, tenant-scoped counts. Use listAllKeys(KeyFilters) when the complete set is needed (client-side auto-pagination).

      Example

      
       KeyPage page = sdk.listKeys(new KeyFilters(null, null, "ML-KEM", "active"), 0, 50);
       System.out.println("Page 1/" + page.totalPages() + " - " + page.totalElements() + " keys");
       page.content().forEach(key ->
           System.out.println(key.getKid() + " (" + key.getAlg() + ")")
       );
       
      Parameters:
      filters - server-side filters; KeyFilters.none() for every key the tenant still holds, in any lifecycle state (destroyed keys are returned only when the status filter is deleted_logical)
      page - zero-based page index
      size - page size (1..200; values above 200 are rejected by the server with 400)
      Returns:
      the page of key metadata with totals; never null
      Throws:
      AnkaSecureSdkException - if the server returns an error
      See Also:
    • listAllKeys

      public List<KeyMetadata> listAllKeys(KeyFilters filters) throws AnkaSecureSdkException
      Lists ALL keys matching the filters by auto-paginating through the server's paginated listing (the AWS-CLI pattern: the API pages, the client iterates).

      Bounded by the first page's totalPages snapshot with a defensive hard cap; exceeding it THROWS PaginationCeilingExceededException rather than silently truncating. Offset-pagination drift between page fetches is accepted best-effort.

      Example

      
       List<KeyMetadata> allKeys = sdk.listAllKeys(KeyFilters.none());
       List<KeyMetadata> activeRsa =
           sdk.listAllKeys(new KeyFilters(null, null, "RSA", "active"));
       
      Parameters:
      filters - server-side filters; KeyFilters.none() for every key the tenant still holds, in any lifecycle state (destroyed keys are returned only when the status filter is deleted_logical)
      Returns:
      all matching key metadata; never null, may be empty
      Throws:
      AnkaSecureSdkException - if the server returns an error
      See Also:
    • getKeyMetadata

      public KeyMetadata getKeyMetadata(String kid) throws AnkaSecureSdkException
      Retrieves metadata for a specific key.

      Example

      
       KeyMetadata key = sdk.getKeyMetadata("my-ml-kem-768-key");
      
       System.out.println("Key: " + key.getKid());
       System.out.println("Algorithm: " + key.getAlg());
       System.out.println("Status: " + key.getStatus());
       System.out.println("Created: " + key.getCreatedAt());
       
      Parameters:
      kid - key identifier; must not be null
      Returns:
      key metadata; never null
      Throws:
      AnkaSecureSdkException - if the key does not exist, the authenticated client lacks permission, or the server returns an error
      NullPointerException - if kid is null
      See Also:
    • reencrypt

      public ReencryptResult reencrypt(String newKid, String jweToken) throws AnkaSecureSdkException
      Re-encrypts data with a different key without decrypting to plaintext.

      Decrypts the JWE using the original key, then immediately re-encrypts with the new key. The plaintext never leaves the server, providing better security than decrypt + encrypt in the client.

      This method is suitable for in-memory JWE tokens up to ~100 MB. For larger files, use reencryptFileStream(String, String, Path, Path).

      Example

      
       // Original encryption
       EncryptResult encrypted = sdk.encrypt("old-aes-key", data);
       String jweToken = encrypted.getJweToken();
      
       // Migrate to post-quantum encryption
       ReencryptResult reencrypted = sdk.reencrypt("new-ml-kem-key", jweToken);
      
       System.out.println("Migrated from: " + reencrypted.getOldKeyAlgorithmUsed());
       System.out.println("Migrated to: " + reencrypted.getNewKeyAlgorithmUsed());
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWE addressed to several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with reencrypt(String, String, String).

      Parameters:
      newKid - identifier of the new encryption key; must not be null
      jweToken - the JWE token to re-encrypt, Compact or General JSON (detected from the content); must not be null
      Returns:
      re-encryption result containing the new JWE (Compact when newKid is a single-recipient key, General JSON when it is a composite KEM key) and migration metadata; never null
      Throws:
      AnkaSecureSdkException - if either key does not exist, the JWE is malformed, the authenticated client lacks permission, the JWE's one key and newKid are of different classes (HTTP 400 key-operation-incompatible: one symmetric, the other asymmetric; a composite key is asymmetric), or the server returns an error. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if newKid or jweToken is null
      See Also:
    • reencrypt

      public ReencryptResult reencrypt(String newKid, String sourceKid, String jweToken) throws AnkaSecureSdkException
      Re-encrypts a JWE to newKid, replacing the recipient the caller names.

      Same operation as reencrypt(String, String); the plaintext never leaves the server.

      Choosing the recipient to replace

      For a JWE addressed to several keys, sourceKid names the ONE recipient whose encrypted_key is unwrapped; newKid takes its position and every other recipient is kept, re-wrapped at its current PRIMARY. ReencryptResult.getRecipients() reports every recipient of the produced JWE. For a JWE that names one key sourceKid may be null (when present it must equal that key); for a JWE that names none it is required and names the key the JWE was produced for.

      Example

      
       // Rotate custodian A of a three-custodian JWE to the 2026 archive key
       ReencryptResult result = sdk.reencrypt("archive-2026", "custodian-a", boardMinutesJwe);
       for (ReencryptedRecipient r : result.getRecipients()) {
           System.out.println(r.getKid() + " (" + r.getAlgorithm() + ") v" + r.getMaterialVersion());
       }
       
      Parameters:
      newKid - identifier of the key that takes the replaced recipient's position; must not be null
      sourceKid - the recipient this re-encryption replaces; null when the JWE names one key. Required when the JWE is addressed to several keys or names none
      jweToken - the JWE to re-encrypt, Compact or General JSON; must not be null
      Returns:
      re-encryption result with the new JWE and every recipient of it; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no recipient of the JWE, HTTP 400 naming newKid when it is one of the recipients the operation keeps, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWE addressed to several keys, the HTTP 422 refusals of a symmetric or composite recipient or newKid (JOSE_SYMMETRIC_ACTOR, JWE_COMPOSITE_RECIPIENT) are decided before that HTTP 400. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if newKid or jweToken is null
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • reencryptFileCompact

      public ReencryptResult reencryptFileCompact(String newKid, Path input, Path output) throws AnkaSecureSdkException
      Re-encrypts a file with forced compact format. Use when you need a single-line string for API/JSON transmission. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: Re-encrypting for JSON responses or HTTP headers
      • Storage: Re-encrypting for single database field (text/varchar)
      • Interoperability: JWE standard format (RFC 7516)

      Input and Output Format

      The input may be RFC 7516 §7.1 Compact or §7.2 General JSON, detected from the content. The output is written in the serialization that carries every recipient of the new JWE: Compact when newKid is a single-recipient key, General JSON when it is a composite KEM key (one recipient per component, which Compact Serialization cannot hold).

      Alternative Methods

      Example

      
       // Re-encrypt small file with new key
       ReencryptResult result = sdk.reencryptFileCompact(
           "new-ml-kem-key",
           Path.of("old-data.jwe"),
           Path.of("new-data.jwe")
       );
      
       System.out.println("Migrated from: " + result.getOldKeyAlgorithmUsed());
       System.out.println("Migrated to: " + result.getNewKeyAlgorithmUsed());
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWE addressed to several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with reencryptFileCompact(String, String, Path, Path).

      Parameters:
      newKid - identifier of the new encryption key; must not be null
      input - Compact or General JSON JWE file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file (.jwe recommended); receives the Compact JWE, or the General JSON JWE when newKid is a composite KEM key; must not be null
      Returns:
      re-encryption result with migration metadata; its jweToken is exactly what was written; never null
      Throws:
      AnkaSecureSdkException - if the input is neither a well-formed Compact nor a General JSON JWE (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), exceeds the server's configured compact limit (HTTP 413), is refused with HTTP 400 key-operation-incompatible when the JWE's one key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), or the operation fails
      See Also:
    • reencryptFileCompact

      public ReencryptResult reencryptFileCompact(String newKid, String sourceKid, Path input, Path output) throws AnkaSecureSdkException
      Re-encrypts a Compact or General JSON JWE file to newKid, replacing the recipient the caller names.

      Choosing the recipient to replace

      For a JWE addressed to several keys, sourceKid names the ONE recipient whose encrypted_key is unwrapped; newKid takes its position and every other recipient is kept, re-wrapped at its current PRIMARY. ReencryptResult.getRecipients() reports every recipient of the produced JWE. For a JWE that names one key sourceKid may be null (when present it must equal that key); for a JWE that names none it is required and names the key the JWE was produced for.

      Example

      
       ReencryptResult result = sdk.reencryptFileCompact("archive-2026", "custodian-a",
               Path.of("board-minutes.jwe"), Path.of("board-minutes.rotated.jwe"));
       
      Parameters:
      newKid - identifier of the key that takes the replaced recipient's position; must not be null
      sourceKid - the recipient this re-encryption replaces; null when the JWE names one key. Required when the JWE is addressed to several keys or names none
      input - Compact or General JSON JWE file; must not be null
      output - output file receiving the re-encrypted JWE; must not be null
      Returns:
      re-encryption result with every recipient of the produced JWE; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no recipient of the JWE, HTTP 400 naming newKid when it is one of the recipients the operation keeps, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWE addressed to several keys, the HTTP 422 refusals of a symmetric or composite recipient or newKid (JOSE_SYMMETRIC_ACTOR, JWE_COMPOSITE_RECIPIENT) are decided before that HTTP 400
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • reencryptFile

      public ReencryptResult reencryptFile(String newKid, Path input, Path output) throws AnkaSecureSdkException
      Re-encrypts a file. Recommended for most use cases. Automatically selects the re-encryption path from the input envelope's actual format, not its size.

      This method inspects the input file's structure and routes accordingly:

      Why Format, Not Size?

      The input here is an envelope, not plaintext. A compact JWE is roughly twice the size of the plaintext it wraps, so its size may exceed the server's compact plaintext ceiling even though it is a compact envelope that must be re-encrypted via the compact path. Selecting by format guarantees that any file produced by encryptFile/encryptFileCompact is always re-encryptable, and that a genuinely detached/streaming artifact is always re-encrypted via streaming.

      When to Use Explicit Formats

      Use reencryptFileCompact(String, Path, Path) or reencryptFileStream(String, String, Path, Path) only when you specifically need to force a particular format.

      Example

      
       // Re-encrypt any file - works for both small and large files
       ReencryptResult result = sdk.reencryptFile(
           "new-ml-kem-key",
           Path.of("old-data.jwe"),
           Path.of("new-data.jwe")
       );
      
       System.out.println("Migrated from: " + result.getOldKeyAlgorithmUsed());
       System.out.println("Migrated to: " + result.getNewKeyAlgorithmUsed());
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWE addressed to several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with reencryptFile(String, String, Path, Path).

      Parameters:
      newKid - identifier of the new encryption key; must not be null
      input - input JWE file (any size): Compact, General JSON or streaming JWET; must not be null
      output - output file (.jwe or .jwet); must not be null. By default fails if output exists; call withOverwrite() to overwrite.
      Returns:
      re-encryption result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - if the input is not a streaming JWET artifact (it does not begin with the multipart delimiter) and is not a well-formed Compact or General JSON JWE (refused before any request is sent, with SdkErrorCode.MALFORMED_JOSE_INPUT), is refused with HTTP 400 key-operation-incompatible when the JWE's one key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), or the operation fails. A General JSON input naming a header parameter both in protected and in a recipient header is refused with HTTP 400 (RFC 7516 §7.2.1: the header locations are disjoint); a streaming JWET artifact is refused as reencryptFileStream(String, String, Path, Path) describes.
      LocalFileException - if the input cannot be read or the output cannot be written
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default)
      See Also:
    • reencryptFile

      public ReencryptResult reencryptFile(String newKid, String sourceKid, Path input, Path output) throws AnkaSecureSdkException
      Re-encrypts a file of any format to newKid, replacing the recipient the caller names, and selecting the path from the input's actual format exactly as reencryptFile(String, Path, Path) does.

      Choosing the recipient to replace

      For a JWE addressed to several keys, sourceKid names the ONE recipient whose encrypted_key is unwrapped; newKid takes its position and every other recipient is kept, re-wrapped at its current PRIMARY. For a JWE that names one key sourceKid may be null (when present it must equal that key); for a JWE that names none it is required and names the key the JWE was produced for.

      A Compact or General JSON input takes the compact path, and ReencryptResult.getRecipients() reports every recipient of the produced JWE. A streaming JWET input takes reencryptFileStream(String, String, Path, Path), whose response carries no per-recipient detail: getRecipients() is null there.

      Example

      
       ReencryptResult result = sdk.reencryptFile("archive-2026", "custodian-a",
               Path.of("board-minutes.jwe"), Path.of("board-minutes.rotated.jwe"));
       
      Parameters:
      newKid - identifier of the key that takes the replaced recipient's position; must not be null
      sourceKid - the recipient this re-encryption replaces; null when the JWE names one key. Required when the JWE is addressed to several keys or names none
      input - input JWE file (any size): Compact, General JSON or streaming JWET; must not be null
      output - output file; must not be null
      Returns:
      re-encryption result; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no recipient of the JWE, HTTP 400 naming newKid when it is one of the recipients the operation keeps, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWE addressed to several keys, the HTTP 422 refusals of a symmetric or composite recipient or newKid (JOSE_SYMMETRIC_ACTOR, JWE_COMPOSITE_RECIPIENT) are decided before that HTTP 400. A General JSON input naming a header parameter both in protected and in a recipient header is refused with HTTP 400 (RFC 7516 §7.2.1: the header locations are disjoint); a streaming JWET artifact is refused as reencryptFileStream(String, String, Path, Path) describes.
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • reencryptFileStream

      public ReencryptResult reencryptFileStream(String newKid, String sourceKid, Path input, Path output) throws AnkaSecureSdkException
      Re-encrypts a file with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary output is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When re-encrypting to streaming JWE format (.jwet files)
      • Progressive processing: Start processing before full upload
      • Key rotation: Migrating archived data to new encryption keys

      Alternative Methods

      Example

      
       // Re-encrypt large file with new key
       ReencryptResult result = sdk.reencryptFileStream(
           "new-ml-kem-key",
           null,  // the JWE names one key
           Path.of("backup.jwet"),
           Path.of("backup-new.jwet")
       );
       
      Parameters:
      newKid - identifier of the new encryption key; must not be null
      sourceKid - the recipient this re-encryption replaces; null when the JWE names one key. Required when the JWE is addressed to several keys or names none; sent as the sourceKid query parameter
      input - streaming JWE file (any size, up to 100MB recommended); must not be null
      output - output file (.jwet recommended); must not be null
      Returns:
      re-encryption result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no recipient of the JWE, HTTP 400 naming newKid when it is one of the recipients the operation keeps, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric, the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWE addressed to several keys, the HTTP 422 refusals of a symmetric or composite recipient or newKid (JOSE_SYMMETRIC_ACTOR, JWE_COMPOSITE_RECIPIENT) are decided before that HTTP 400. The JWET header is refused with HTTP 422 and no payload byte, before any key is looked up, when it carries a root member besides protected, recipients, iv, ciphertext, tag and, on a composite key's header, anka_composite_mode (JWE_ROOT_MEMBER_FORBIDDEN; a JSON aad member and a shared unprotected header included) or a recipient member besides header and encrypted_key (JWE_RECIPIENT_MEMBER_FORBIDDEN). A header parameter name present both in protected and in a recipient header (RFC 7516 §7.2.1) is refused with HTTP 422 for a single-recipient JWE and HTTP 400 for a composite JWE or one addressed to several keys.
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • resign

      public ResignResult resign(String newKid, String jws) throws AnkaSecureSdkException
      Re-signs data with a different key without verifying the original signature.

      Strips the old signature and creates a new JWS with the new signing key. This is useful for key rotation or migrating signatures to post-quantum algorithms.

      This method is suitable for in-memory JWS tokens up to ~100 MB. For larger files, use resignFileStream(Path, String, Path, Path).

      Example

      
       // Original signature
       SignResult signed = sdk.sign("old-ecdsa-key", document);
       String jwsToken = signed.getJwsToken();
      
       // Migrate to post-quantum signature
       ResignResult resigned = sdk.resign("new-ml-dsa-key", jwsToken);
      
       System.out.println("Migrated from: " + resigned.getOldKeyAlgorithmUsed());
       System.out.println("Migrated to: " + resigned.getNewKeyAlgorithmUsed());
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWS signed by several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with resign(String, String, String).

      Parameters:
      newKid - identifier of the new signing key; must not be null
      jws - the JWS token to re-sign; must not be null
      Returns:
      re-sign result containing new JWS and migration metadata; never null
      Throws:
      AnkaSecureSdkException - if either key does not exist, the JWS is malformed, the authenticated client lacks permission, the JWS's one key and newKid are of different classes (HTTP 400 key-operation-incompatible: one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), or the server returns an error
      NullPointerException - if newKid or jws is null
      See Also:
    • resign

      public ResignResult resign(String newKid, String sourceKid, String jws) throws AnkaSecureSdkException
      Re-signs a JWS with newKid, replacing the signature of the signer the caller names.

      Choosing the signature to replace

      For a JWS signed by several keys, sourceKid names the ONE signer whose signature is replaced; every signature of the input is verified under its own key first, and ResignResult.getSignatureResults() reports each of them. When the protected header names a kid, sourceKid may be null (when present it must equal it); when the header names none it is required and names the key that signed.

      Example

      
       // Replace the 2019 legal signature of a two-signer contract JWS
       ResignResult result = sdk.resign("legal-mldsa-2026", "legal-rsa-2019", contractJws);
       result.getSignatureResults().forEach(s ->
           System.out.println("input signer " + s.getKid() + " v" + s.getMaterialVersion()));
       
      Parameters:
      newKid - identifier of the new signing key; must not be null
      sourceKid - the signer whose signature is replaced; null when the protected header names one key. Required for a JWS signed by several keys or naming none
      jws - the JWS to re-sign (Compact or JWS JSON); must not be null
      Returns:
      re-sign result with the new JWS and every signature of the input; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no signer, HTTP 400 when any input signature does not verify, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWS signed by several keys, the HTTP 422 JOSE_SYMMETRIC_ACTOR refusal of a symmetric signer or newKid is decided before that HTTP 400
      NullPointerException - if newKid or jws is null
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • resign

      public ResignResult resign(String newKid, String jws, JwsSerialization serialization) throws AnkaSecureSdkException
      Re-signs a JWS, explicitly selecting the JWS serialization.

      Ergonomic overload of resign(String, String). Passing null defers to the server. The input JWS may be compact or JWS-JSON.

      A literal null third argument, resign(newKid, jws, null), also matches resign(String, String, String) (whose second argument is sourceKid), so the compiler refuses the call as ambiguous. Pass a typed (JwsSerialization) null, or call resign(String, String), which is the same request.

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWS signed by several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with resign(String, String, String, JwsSerialization).

      Parameters:
      newKid - identifier of the new signing key; must not be null
      jws - the JWS to re-sign (compact or JWS-JSON); must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      re-sign result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 when COMPACT is requested but the deployment mandates a qualified timestamp, and HTTP 400 key-operation-incompatible when the JWS's one key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric)
      NullPointerException - if newKid or jws is null
      See Also:
    • resign

      public ResignResult resign(String newKid, String sourceKid, String jws, JwsSerialization serialization) throws AnkaSecureSdkException
      Re-signs a JWS with newKid, replacing the signature of the signer the caller names and selecting the JWS serialization.

      Choosing the signature to replace

      For a JWS signed by several keys, sourceKid names the ONE signer whose signature is replaced; every signature of the input is verified under its own key first, and ResignResult.getSignatureResults() reports each of them. When the protected header names a kid, sourceKid may be null (when present it must equal it); when the header names none it is required and names the key that signed.

      Example

      
       ResignResult result = sdk.resign("legal-mldsa-2026", "legal-rsa-2019", contractJws,
               JwsSerialization.JSON);
       
      Parameters:
      newKid - identifier of the new signing key; must not be null
      sourceKid - the signer whose signature is replaced; null when the protected header names one key. Required for a JWS signed by several keys or naming none
      jws - the JWS to re-sign (Compact or JWS JSON); must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      re-sign result with the new JWS and every signature of the input; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no signer, HTTP 400 when any input signature does not verify, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWS signed by several keys, the HTTP 422 JOSE_SYMMETRIC_ACTOR refusal of a symmetric signer or newKid is decided before that HTTP 400
      NullPointerException - if newKid or jws is null
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • resignFileCompact

      public ResignResult resignFileCompact(String newKid, Path input, Path output) throws AnkaSecureSdkException
      Re-signs a compact JWS file with forced compact format. Use when you need a single-line string for API/JSON transmission. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: Re-signing for JSON responses or HTTP headers
      • Storage: Re-signing for single database field (text/varchar)
      • Interoperability: JWS standard format (RFC 7515)

      Alternative Methods

      Note: No auto-detection method available because streaming re-signing requires separate data and signature file parameters.

      Example

      
       // Re-sign compact JWS with new key
       ResignResult result = sdk.resignFileCompact(
           "new-ml-dsa-key",
           Path.of("old-signature.jws"),
           Path.of("new-signature.jws")
       );
      
       System.out.println("Migrated from: " + result.getOldKeyAlgorithmUsed());
       System.out.println("Migrated to: " + result.getNewKeyAlgorithmUsed());
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWS signed by several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with resignFileCompact(String, String, Path, Path).

      Parameters:
      newKid - identifier of the new signing key; must not be null
      input - compact JWS file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file (.jws recommended); must not be null
      Returns:
      re-sign result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - with HTTP 413 when the input exceeds the server's configured compact limit, HTTP 400 key-operation-incompatible when the JWS's one key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), or when the operation otherwise fails
      See Also:
    • resignFileCompact

      public ResignResult resignFileCompact(String newKid, String sourceKid, Path input, Path output) throws AnkaSecureSdkException
      Re-signs a JWS file with newKid, replacing the signature of the signer the caller names.

      Choosing the signature to replace

      For a JWS signed by several keys, sourceKid names the ONE signer whose signature is replaced; every signature of the input is verified under its own key first, and ResignResult.getSignatureResults() reports each of them. When the protected header names a kid, sourceKid may be null (when present it must equal it); when the header names none it is required and names the key that signed.

      Example

      
       ResignResult result = sdk.resignFileCompact("legal-mldsa-2026", "legal-rsa-2019",
               Path.of("contract.jws"), Path.of("contract.resigned.jws"));
       
      Parameters:
      newKid - identifier of the new signing key; must not be null
      sourceKid - the signer whose signature is replaced; null when the protected header names one key. Required for a JWS signed by several keys or naming none
      input - input JWS file (Compact or JWS JSON); must not be null
      output - output file; must not be null
      Returns:
      re-sign result with every signature of the input; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no signer, HTTP 400 when any input signature does not verify, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWS signed by several keys, the HTTP 422 JOSE_SYMMETRIC_ACTOR refusal of a symmetric signer or newKid is decided before that HTTP 400
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • resignFileCompact

      public ResignResult resignFileCompact(String newKid, Path input, Path output, JwsSerialization serialization) throws AnkaSecureSdkException
      Re-signs a JWS file (attached), explicitly selecting the JWS serialization.

      Ergonomic overload of resignFileCompact(String, Path, Path). Passing null defers to the server. When the deployment mandates a qualified timestamp, the written output is a JWS-JSON artifact carrying the JAdES sigTst.

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWS signed by several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with resignFileCompact(String, String, Path, Path, JwsSerialization).

      Parameters:
      newKid - identifier of the new signing key; must not be null
      input - input JWS file (compact or JWS-JSON, max 5MB); must not be null
      output - output file; must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      re-sign result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 when COMPACT is requested but the deployment mandates a qualified timestamp, and HTTP 400 key-operation-incompatible when the JWS's one key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric)
      See Also:
    • resignFileCompact

      public ResignResult resignFileCompact(String newKid, String sourceKid, Path input, Path output, JwsSerialization serialization) throws AnkaSecureSdkException
      Re-signs a JWS file with newKid, replacing the signature of the signer the caller names and selecting the JWS serialization.

      Choosing the signature to replace

      For a JWS signed by several keys, sourceKid names the ONE signer whose signature is replaced; every signature of the input is verified under its own key first, and ResignResult.getSignatureResults() reports each of them. When the protected header names a kid, sourceKid may be null (when present it must equal it); when the header names none it is required and names the key that signed.

      Example

      
       ResignResult result = sdk.resignFileCompact("legal-mldsa-2026", "legal-rsa-2019",
               Path.of("contract.jws"), Path.of("contract.resigned.jws"), JwsSerialization.JSON);
       
      Parameters:
      newKid - identifier of the new signing key; must not be null
      sourceKid - the signer whose signature is replaced; null when the protected header names one key. Required for a JWS signed by several keys or naming none
      input - input JWS file (Compact or JWS JSON); must not be null
      output - output file; must not be null
      serialization - requested serialization, or null to let the server choose
      Returns:
      re-sign result with every signature of the input; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no signer, HTTP 400 when any input signature does not verify, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWS signed by several keys, the HTTP 422 JOSE_SYMMETRIC_ACTOR refusal of a symmetric signer or newKid is decided before that HTTP 400
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • resignFileStream

      public ResignResult resignFileStream(Path oldJwsHeader, String newKid, Path input, Path output) throws AnkaSecureSdkException
      Re-signs a detached JWS signature with forced streaming format. Use for large files (100MB+), low-memory environments, or when binary output is acceptable.

      When to Use Streaming Format

      • Large files: Files above the server's configured compact limit (required), optimal for 100MB+ (recommended)
      • Memory constraints: Low-memory environments or embedded systems
      • Binary output: When re-signing detached JWS signature format (.jwst files)
      • Progressive processing: Start processing before full upload
      • Key rotation: Migrating archived signatures to new signing keys

      Input Format

      Detached JWS signature (.jwst file) + separate data file. Typically created by signFileStream(String, Path, Path).

      Alternative Methods

      Note: No auto-detection method available because streaming re-signing requires separate data and signature file parameters.

      Example

      
       // Re-sign detached signature for large file
       ResignResult result = sdk.resignFileStream(
           Path.of("backup.tar.gz.jwst"),    // Old JWS header
           "new-ml-dsa-key",
           Path.of("backup.tar.gz"),          // Original data
           Path.of("backup-new.tar.gz.jwst") // New signature
       );
       

      Multi-key tokens: this form sends no sourceKid, which the server reads as "the token names one key". For a JWS signed by several keys, or naming none, it refuses the request with HTTP 400 naming sourceKid; name the key with resignFileStream(Path, String, String, Path, Path).

      Parameters:
      oldJwsHeader - old JWS header file (for metadata extraction); must not be null
      newKid - identifier of the new signing key; must not be null
      input - original data file (any size, up to 100MB recommended); must not be null
      output - output file (.jwst recommended); must not be null
      Returns:
      re-sign result with migration metadata; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 key-operation-incompatible when the JWS's one key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), or when the operation otherwise fails
      See Also:
    • resignFileStream

      public ResignResult resignFileStream(Path oldJwsHeader, String newKid, String sourceKid, Path input, Path output) throws AnkaSecureSdkException
      Re-signs a detached JWS in streaming mode, naming the signer whose signature is replaced; sent as the sourceKid member of the /crypto/stream/resign metadata part.

      Choosing the signature to replace

      For a JWS signed by several keys, sourceKid names the ONE signer whose signature is replaced; every signature of the input is verified under its own key first. When the protected header names a kid, sourceKid may be null (when present it must equal it); when the header names none it is required and names the key that signed.

      The streaming response is the new detached JWS and an end-of-stream verdict; it carries no per-signature detail, so ResignResult.getSignatureResults() is null on this path (use resignFileCompact(String, String, Path, Path) to read every input signer). The new signature is written only when the verdict is VALID and the result crosses keys.

      Example

      
       ResignResult result = sdk.resignFileStream(Path.of("backup.tar.gz.jwst"), "archive-mldsa-2026",
               null, Path.of("backup.tar.gz"), Path.of("backup-new.tar.gz.jwst"));
       
      Parameters:
      oldJwsHeader - the detached JWS being replaced; must not be null
      newKid - identifier of the new signing key; must not be null
      sourceKid - the signer whose signature is replaced; null when the protected header names one key. Required for a JWS signed by several keys or naming none
      input - original data file; must not be null
      output - output file for the new detached JWS; must not be null
      Returns:
      re-sign result naming the replaced and the replacing key (getSignatureResults() is null on this path); never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming sourceKid when it is absent where required or names no signer, HTTP 400 when any input signature does not verify, HTTP 400 key-operation-incompatible when the source key and newKid are of different classes (one symmetric (HMAC), the other asymmetric; a composite key is asymmetric), HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when the operation otherwise fails. For a JWS signed by several keys, the HTTP 422 JOSE_SYMMETRIC_ACTOR refusal of a symmetric signer or newKid is decided before that HTTP 400
      StreamIntegrityException - if the end-of-stream verdict is INVALID or absent (no output is written)
      IllegalArgumentException - if sourceKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • signThenEncrypt

      public SignEncryptResult signThenEncrypt(String signKid, String encryptKid, byte[] data) throws AnkaSecureSdkException
      Signs then encrypts data in a single operation (nested JWE containing JWS).

      Creates a JWS signature, then encrypts the entire JWS as a JWE. This provides both authenticity (signature) and confidentiality (encryption). The recipient must decrypt first, then verify the signature.

      This method is suitable for in-memory payloads up to ~100 MB. For larger files, use signThenEncryptFileCompact(String, String, Path, Path).

      Example

      
       byte[] sensitiveDoc = Files.readAllBytes(Path.of("/contracts/agreement.pdf"));
      
       SignEncryptResult result = sdk.signThenEncrypt(
           "my-ml-dsa-key",    // signing key
           "my-ml-kem-key",    // encryption key
           sensitiveDoc
       );
      
       // Later: decrypt then verify
       DecryptVerifyResult verified = sdk.decryptThenVerify(result.getJweToken());
       if (verified.isSignatureValid()) {
           System.out.println("Signature valid, data authentic");
       }
       
      Parameters:
      signKid - identifier of the signing key; must not be null
      encryptKid - identifier of the encryption key; must not be null
      data - the raw bytes to sign and encrypt; must not be null
      Returns:
      result containing nested JWE token and operation metadata; never null
      Throws:
      AnkaSecureSdkException - if either key does not exist, the authenticated client lacks permission, or the server returns an error
      NullPointerException - if any parameter is null
      See Also:
    • signThenEncrypt

      public SignEncryptResult signThenEncrypt(String signKid, String encryptKid, byte[] data, JwsSerialization serialization) throws AnkaSecureSdkException
      Signs then encrypts, explicitly selecting the inner-JWS serialization.

      Ergonomic overload of signThenEncrypt(String, String, byte[]). Passing null defers to the server. Governs only the inner JWS representation and never changes the outer JWE.

      Parameters:
      signKid - private key identifier for signing; must not be null
      encryptKid - public key identifier for encryption; must not be null
      data - plaintext bytes to sign and encrypt; must not be null
      serialization - requested inner-JWS serialization, or null to defer
      Returns:
      combined sign-encrypt result; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 (before any signing or encryption) when COMPACT is requested but the deployment mandates a qualified timestamp
      NullPointerException - if any of signKid, encryptKid, or data is null
      See Also:
    • decryptThenVerify

      public DecryptVerifyResult decryptThenVerify(String jweToken) throws AnkaSecureSdkException
      Decrypts a JWE then verifies the inner JWS signature.

      Reverses signThenEncrypt(String, String, byte[]) by decrypting the outer JWE, then verifying the inner JWS signature. Both operations must succeed for the overall operation to succeed.

      Example

      
       // Received encrypted and signed document
       String jweToken = "eyJhbGciOiJNTC1LRU0tNzY4IiwiZW5jIjoiQTI1NkdDTSJ9...";
      
       DecryptVerifyResult result = sdk.decryptThenVerify(jweToken);
      
       if (result.isSignatureValid()) {
           byte[] authenticData = result.getDecryptedData();
           System.out.println("Decrypted and verified successfully");
       } else {
           System.out.println("Signature verification failed");
       }
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptThenVerify(String, String).

      Parameters:
      jweToken - the nested JWE token to decrypt and verify, Compact or General JSON (as returned for a composite KEM encryption key; detected from the content); must not be null
      Returns:
      result containing decrypted data and verification status; never null
      Throws:
      AnkaSecureSdkException - if the JWE is malformed, decryption fails, the JWS is malformed, or the server returns an error. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if jweToken is null
      See Also:
    • decryptThenVerify

      public DecryptVerifyResult decryptThenVerify(String recipientKid, String jweToken) throws AnkaSecureSdkException
      Decrypts a nested JWE(JWS) token with the recipient the caller names, then verifies every signature of the inner JWS.

      Choosing the recipient and reading every signer

      recipientKid names the recipient of the outer JWE to decrypt with, exactly as for decrypt(String, String); it must be none of the inner JWS's signers. Every signature of the inner JWS is verified under its own key, and DecryptVerifyResult.getSignatureResults() reports each of them in token order.

      Example

      
       DecryptVerifyResult result = sdk.decryptThenVerify("custodian-b", signedMinutesJwe);
       for (SignatureResult signer : result.getSignatureResults()) {
           System.out.println(signer.getKid() + " " + signer.getAlgorithm()
                   + " v" + signer.getMaterialVersion() + " verified=" + signer.getVerified());
       }
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      jweToken - the nested JWE token, Compact or General JSON; must not be null
      Returns:
      result with the plaintext, the decryption key's metadata and one entry per inner signature; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for an outer JWE addressed to several keys or names no recipient, HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when decryption or verification otherwise fails. A header parameter name present both in protected and in a recipient header is refused with HTTP 400 whatever the two values (RFC 7516 §7.2.1: the header locations are disjoint).
      NullPointerException - if jweToken is null
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • signThenEncryptFileCompact

      public SignEncryptResult signThenEncryptFileCompact(String signKid, String encryptKid, Path input, Path output) throws AnkaSecureSdkException
      Signs then encrypts a file with forced compact format. Use when you need a single-line string for API/JSON transmission. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: Signed and encrypted data for JSON responses or HTTP headers
      • Storage: Single database field (text/varchar) with both authenticity and confidentiality
      • Interoperability: Nested JWE(JWS) standard format

      Output Format

       JWE(
         JWS(payload)
       )
       

      Alternative Methods

      Note: No streaming variant available for combined operations. For large files, split into separate sign and encrypt operations.

      Example

      
       // Sign and encrypt sensitive document
       SignEncryptResult result = sdk.signThenEncryptFileCompact(
           "my-ml-dsa-key",   // Signing key
           "my-ml-kem-key",   // Encryption key
           Path.of("contract.pdf"),
           Path.of("contract-secure.jwe")
       );
      
       System.out.println("Signed with: " + result.getSignKeyRequested());
       System.out.println("Encrypted with: " + result.getEncryptKeyRequested());
       
      Parameters:
      signKid - signing key identifier; must not be null
      encryptKid - encryption key identifier; must not be null
      input - input file (up to the server's configured compact limit, default 4 MB); must not be null
      output - output file (.jwe); must not be null
      Returns:
      operation result with metadata; never null
      Throws:
      AnkaSecureSdkException - if the input exceeds the server's configured compact limit (HTTP 413) or operation fails
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default); call withOverwrite() to overwrite
    • signThenEncryptFileCompact

      public SignEncryptResult signThenEncryptFileCompact(String signKid, String encryptKid, Path input, Path output, JwsSerialization serialization) throws AnkaSecureSdkException
      Signs a file then encrypts it, explicitly selecting the inner-JWS serialization.

      Ergonomic overload of signThenEncryptFileCompact(String, String, Path, Path). Passing null defers to the server.

      Parameters:
      signKid - private key identifier for signing; must not be null
      encryptKid - public key identifier for encryption; must not be null
      input - input file; must not be null
      output - output file; must not be null
      serialization - requested inner-JWS serialization, or null to defer
      Returns:
      combined sign-encrypt result; never null
      Throws:
      AnkaSecureSdkException - on error, including HTTP 400 (before any signing or encryption) when COMPACT is requested but the deployment mandates a qualified timestamp
      See Also:
    • decryptThenVerifyFileCompact

      public DecryptVerifyResult decryptThenVerifyFileCompact(Path input, Path output) throws AnkaSecureSdkException
      Decrypts and verifies a nested JWE(JWS) file with forced compact format. Use when you need to decrypt and verify a single-line string. Fails if the input exceeds the server's configured compact limit (maxPlaintextBytes, default 4 MB).

      When to Use Compact Format

      • API transmission: Decrypting and verifying from JSON responses or HTTP headers
      • Storage: Decrypting and verifying from single database field (text/varchar)
      • Interoperability: Nested JWE(JWS) standard format

      Alternative Methods

      Note: No streaming variant available for combined operations. For large files, split into separate decrypt and verify operations.

      Example

      
       // Decrypt and verify secured document
       DecryptVerifyResult result = sdk.decryptThenVerifyFileCompact(
           Path.of("contract-secure.jwe"),
           Path.of("contract-recovered.pdf")
       );
      
       if (result.isSignatureValid()) {
           System.out.println("Signature valid!");
           result.getSignatureResults().forEach(signer ->
               System.out.println("Signed by: " + signer.getKid()));
       }
       

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptThenVerifyFileCompact(String, Path, Path).

      Parameters:
      input - nested JWE file (up to the server's configured compact limit, default 4 MB); must not be null
      output - recovered plaintext file; must not be null
      Returns:
      operation result with verification status; never null
      Throws:
      AnkaSecureSdkException - if the input exceeds the server's configured compact limit (HTTP 413) or operation fails
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default); call withOverwrite() to overwrite
    • decryptThenVerifyFileCompact

      public DecryptVerifyResult decryptThenVerifyFileCompact(String recipientKid, Path input, Path output) throws AnkaSecureSdkException
      Decrypts a nested JWE(JWS) file with the recipient the caller names, then verifies every signature of the inner JWS.

      Choosing the recipient and reading every signer

      recipientKid names the recipient of the outer JWE to decrypt with, exactly as for decrypt(String, String); it must be none of the inner JWS's signers. Every signature of the inner JWS is verified under its own key, and DecryptVerifyResult.getSignatureResults() reports each of them in token order.

      Example

      
       DecryptVerifyResult result = sdk.decryptThenVerifyFileCompact("custodian-b",
               Path.of("signed-minutes.jwe"), Path.of("signed-minutes.pdf"));
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      input - nested JWE file; must not be null
      output - recovered plaintext file; must not be null
      Returns:
      result with one entry per inner signature; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for an outer JWE addressed to several keys or names no recipient, HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when decryption or verification otherwise fails
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • signThenEncryptFileStream

      public SignEncryptResult signThenEncryptFileStream(String signKid, String encryptKid, Path input, Path output) throws AnkaSecureSdkException
      Signs a file then encrypts it in streaming mode (nested JWE(JWS), bounded memory) — the large-file counterpart of signThenEncryptFileCompact(String, String, Path, Path).

      The payload is signed with signKid (inner JWS) and the resulting JWS is encrypted with encryptKid (outer JWE) in a single incremental pass, so the signature is carried ENCRYPTED. The nested ciphertext artifact is written to output via the SDK's sidecar write contract (quarantine to <output>.part, then atomic promotion). This is a PRODUCER operation — its result is deterministic (complete-or-abort), not verdict-framed. Because the artifact is streamed to disk, SignEncryptResult.getJweToken() is null for this variant; the artifact is the output file.

      Parameters:
      signKid - private key identifier for signing (inner JWS); must not be null
      encryptKid - public key identifier for encryption (outer JWE); must not be null
      input - input file to stream; must not be null
      output - output file for the nested JWE(JWS) artifact; must not be null
      Returns:
      sign-encrypt result with the outer-JWE metadata; never null
      Throws:
      AnkaSecureSdkException - if signing, encryption, or streaming fails
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default); call withOverwrite() to overwrite
      See Also:
    • decryptThenVerifyFileStream

      public DecryptVerifyResult decryptThenVerifyFileStream(Path input, Path output) throws AnkaSecureSdkException
      Decrypts a nested JWE(JWS) file then verifies the inner signature in streaming mode — the large-file counterpart of decryptThenVerifyFileCompact(Path, Path), and the inverse of signThenEncryptFileStream(String, String, Path, Path).

      The server returns a TWO-verdict result carrying BOTH the outer AES-GCM tag outcome and the inner JWS signature outcome. The SDK quarantines the recovered plaintext to <output>.part and promotes it to output ONLY when the overall verdict is VALID (both dimensions passed). An INVALID or absent verdict is FAIL-CLOSED: the sidecar is deleted, no file is written, and a typed StreamIntegrityException is thrown — success is never inferred from a closed socket. An INVALID verdict is its subclass DecryptVerifyIntegrityException, which names every inner signer with its verified outcome.

      On success, DecryptVerifyResult.isSignatureValid() is true and DecryptVerifyResult.getQualifiedTimestamp() surfaces the informational timestamp when present (it never affects validity).

      Multi-key tokens: this form sends no recipientKid, which the server reads as "the token names one key". For a JWE addressed to several keys it refuses the request with HTTP 400 naming recipientKid; name the key with decryptThenVerifyFileStream(String, Path, Path).

      Parameters:
      input - nested JWE(JWS) streaming artifact (the output of signThenEncryptFileStream(String, String, Path, Path)); must not be null
      output - destination for the recovered plaintext; must not be null
      Returns:
      decrypt-verify result with verification status and metadata; never null
      Throws:
      AnkaSecureSdkException - if decryption or I/O fails. The JWET header is refused with HTTP 422 and no payload byte, before any key is looked up, when it carries a root member besides protected, recipients, iv, ciphertext, tag and, on a composite key's header, anka_composite_mode (JWE_ROOT_MEMBER_FORBIDDEN; a JSON aad member and a shared unprotected header included) or a recipient member besides header and encrypted_key (JWE_RECIPIENT_MEMBER_FORBIDDEN). A header parameter name present both in protected and in a recipient header (RFC 7516 §7.2.1) is refused with HTTP 422 for a single-recipient JWE and HTTP 400 for a composite JWE or one addressed to several keys.
      StreamIntegrityException - if the integrity verdict is INVALID or absent
      DestinationExistsException - if output already exists and this instance uses OverwritePolicy.FAIL_IF_EXISTS (the default); call withOverwrite() to overwrite
      See Also:
    • decryptThenVerifyFileStream

      public DecryptVerifyResult decryptThenVerifyFileStream(String recipientKid, Path input, Path output) throws AnkaSecureSdkException
      Decrypts a nested JWE(JWS) streaming artifact with the recipient the caller names, then verifies every signature of the inner JWS; recipientKid is sent as the query parameter of /crypto/stream/decrypt-verify. The fail-closed two-verdict contract of decryptThenVerifyFileStream(Path, Path) applies unchanged.

      Choosing the recipient and reading every signer

      recipientKid names the recipient of the outer JWE to decrypt with, exactly as for decrypt(String, String); it must be none of the inner JWS's signers. Every signature of the inner JWS is verified under its own key, and DecryptVerifyResult.getSignatureResults() reports each of them in token order.

      The streaming route commits its response before the inner signers are known, so a signer that does not verify, or that the authenticated client holds no grant on, yields an INVALID verdict rather than the 400 or 403 of decryptThenVerifyFileCompact(String, Path, Path). That outcome is a DecryptVerifyIntegrityException whose getSignatureResults() names every signer with its verified outcome.

      Example

      
       DecryptVerifyResult result = sdk.decryptThenVerifyFileStream("custodian-b",
               Path.of("signed-archive.jwet"), Path.of("signed-archive.tar"));
       
      Parameters:
      recipientKid - the key of the recipient to decrypt with; null when the JWE names one key. Required when the JWE is addressed to several keys
      input - nested JWE(JWS) streaming artifact; must not be null
      output - destination for the recovered plaintext; must not be null
      Returns:
      result with one entry per inner signature; never null
      Throws:
      AnkaSecureSdkException - with HTTP 400 naming recipientKid when it is absent for an outer JWE addressed to several keys or names no recipient, HTTP 403 when the authenticated client holds no grant on a key the operation uses, or when decryption or verification otherwise fails. The JWET header is refused with HTTP 422 and no payload byte, before any key is looked up, when it carries a root member besides protected, recipients, iv, ciphertext, tag and, on a composite key's header, anka_composite_mode (JWE_ROOT_MEMBER_FORBIDDEN; a JSON aad member and a shared unprotected header included) or a recipient member besides header and encrypted_key (JWE_RECIPIENT_MEMBER_FORBIDDEN). A header parameter name present both in protected and in a recipient header (RFC 7516 §7.2.1) is refused with HTTP 422 for a single-recipient JWE and HTTP 400 for a composite JWE or one addressed to several keys.
      DecryptVerifyIntegrityException - if the two-verdict result is INVALID, carrying the server's per-signer verdicts (no output is written)
      StreamIntegrityException - if the integrity verdict is absent
      IllegalArgumentException - if recipientKid is empty or blank (pass null when the token names one key); nothing is sent
      See Also:
    • analyzePkcs7

      public co.ankatech.secure.client.model.Pkcs7AnalysisResponse analyzePkcs7(co.ankatech.secure.client.model.Pkcs7AnalysisRequest request) throws AnkaSecureSdkException
      Analyzes a PKCS#7 file to extract metadata and certificate information.

      Parses a PKCS#7/CMS structure and returns its detected format, its signers and recipients, the support status, the conversion recommendations and the analysis limits, without verifying any signature or decrypting any content.

      Example

      
       byte[] der = java.nio.file.Files.readAllBytes(
           java.nio.file.Path.of("/legacy/contract-signed.p7s"));
      
       co.ankatech.secure.client.model.Pkcs7AnalysisRequest request =
           new co.ankatech.secure.client.model.Pkcs7AnalysisRequest()
               .pkcs7Data(java.util.Base64.getEncoder().encodeToString(der));
      
       co.ankatech.secure.client.model.Pkcs7AnalysisResponse analysis =
           sdk.analyzePkcs7(request);
      
       System.out.println("Structure: " + analysis.getDetectedFormat().getStructure());
       System.out.println("Actor limit: " + analysis.getLimits().getMaxActors());
       if (analysis.getSigners() != null) {
           analysis.getSigners().forEach(s ->
               System.out.println("Signer: " + s.getIssuerDN() + " (" + s.getSignatureAlgorithm() + ")"));
       }
       if (analysis.getRecipients() != null) {
           System.out.println("Recipients: " + analysis.getRecipients().size());
       }
       if (analysis.getRecommendations() != null) {
           System.out.println("Migration path: " + analysis.getRecommendations().getMigrationPath());
       }
       
      Parameters:
      request - PKCS#7 analysis request; must not be null
      Returns:
      analysis result containing PKCS#7 metadata; never null
      Throws:
      AnkaSecureSdkException - if the file cannot be read, is not valid PKCS#7, or the server returns an error
      NullPointerException - if request is null
    • analyzePkcs7Stream

      public co.ankatech.secure.client.model.Pkcs7AnalysisResponse analyzePkcs7Stream(File file) throws AnkaSecureSdkException
      Analyzes a PKCS#7 file using streaming to avoid loading the entire file into memory.

      Example

      
       java.io.File file = new java.io.File("/legacy/large-signed-archive.p7s");
      
       co.ankatech.secure.client.model.Pkcs7AnalysisResponse analysis =
           sdk.analyzePkcs7Stream(file);
       

      Analysis is structural: it decrypts nothing and validates no certificate path or timestamp, so it takes no key and no option. The request still carries a metadata part, the empty JSON object, before file: the server reads the body single-pass and cannot rewind, so a file-first body, or one omitting metadata, is refused with 400 .../errors/multipart-part-order before any payload byte is read. The SDK always emits that part; the caller supplies only the file.

      Parameters:
      file - PKCS#7 file to analyze; must not be null
      Returns:
      analysis result containing PKCS#7 metadata; never null
      Throws:
      AnkaSecureSdkException - if the file cannot be read or is not valid PKCS#7, or the server returns an error
      NullPointerException - if file is null
    • convertPkcs7ToJose

      public co.ankatech.secure.client.model.Pkcs7ConversionResponse convertPkcs7ToJose(co.ankatech.secure.client.model.Pkcs7ConversionRequest request) throws AnkaSecureSdkException
      Converts a PKCS#7 signature to JOSE (JWS) format.

      Extracts the signature from a PKCS#7/CMS file and converts it to compact JWS (RFC 7515) format. This enables legacy PKCS#7 signatures to be verified using modern JOSE tooling.

      Authorization. Besides the migration.convertPkcs7 scope, the calling Cryptographic Actor needs a Capability Grant per leg, decided with the re-operation capabilities: REENCRYPT over [opener, recipient] for every other recipient and over the opener's own in-place pair (the opener being decryptionKid, or the first recipient whose key can decrypt), and RESIGN over each signer's in-place pair. A REENCRYPT/RESIGN grant is requested with exactly two assetKids: an in-place pair names the kid twice ([kid, kid], stored as [kid]), a cross pair is [opener, recipient], and there is no wildcard re-operation grant. A grant admits any request whose kids it contains, so the [opener, recipient] grant also covers the opener's in-place leg. ENCRYPT, DECRYPT and SIGN grants admit no conversion; a missing grant is refused with HTTP 403. Grants are created on the control plane.

      Example

      
       co.ankatech.secure.client.model.Pkcs7ConversionRequest request =
           new co.ankatech.secure.client.model.Pkcs7ConversionRequest()
               .pkcs7Data(base64Pkcs7);
      
       co.ankatech.secure.client.model.Pkcs7ConversionResponse conversion =
           sdk.convertPkcs7ToJose(request);
      
       String jws = conversion.getJoseToken();
       System.out.println("Converted to JWS: " + jws);
       
      Parameters:
      request - PKCS#7 conversion request; must not be null
      Returns:
      conversion result containing JWS token; never null
      Throws:
      AnkaSecureSdkException - if the file cannot be read, is not valid PKCS#7, conversion fails, or the server returns an error
      NullPointerException - if request is null
    • convertPkcs7ToJoseStream

      public File convertPkcs7ToJoseStream(co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest metadata, File inputFile, File outputFile) throws AnkaSecureSdkException
      Converts a PKCS#7 signature to JOSE using streaming.

      Authorization. Besides the migration.convertPkcs7 scope, the calling Cryptographic Actor needs a Capability Grant per leg, decided with the re-operation capabilities: REENCRYPT over [opener, recipient] for every other recipient and over the opener's own in-place pair (the opener being decryptionKid, or the first recipient whose key can decrypt), and RESIGN over each signer's in-place pair. A REENCRYPT/RESIGN grant is requested with exactly two assetKids: an in-place pair names the kid twice ([kid, kid], stored as [kid]), a cross pair is [opener, recipient], and there is no wildcard re-operation grant. A grant admits any request whose kids it contains, so the [opener, recipient] grant also covers the opener's in-place leg. ENCRYPT, DECRYPT and SIGN grants admit no conversion; a missing grant is refused with HTTP 403. Grants are created on the control plane.

      Example

      
       co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest metadata =
           new co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest();
       java.io.File inputFile = new java.io.File("/legacy/large-signed-archive.p7s");
       java.io.File outputFile = new java.io.File("/converted/signature.jws");
      
       java.io.File result = sdk.convertPkcs7ToJoseStream(metadata, inputFile, outputFile);
       
      Parameters:
      metadata - conversion metadata; must not be null
      inputFile - path to PKCS#7 file; must not be null and file must exist
      outputFile - path where JWS will be written; must not be null
      Returns:
      the output file; never null
      Throws:
      AnkaSecureSdkException - if the file cannot be read, is not valid PKCS#7, conversion fails, or the server returns an error
      NullPointerException - if any parameter is null
    • performMigrationWorkflow

      public MigrationWorkflowResult performMigrationWorkflow(File pkcs7File, co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest conversionRequest, File outputFile) throws AnkaSecureSdkException
      Performs a complete PKCS#7-to-JOSE migration workflow with analysis and conversion.

      Analyzes a PKCS#7 file, converts it to JOSE format, and writes the result. This is a complete end-to-end workflow for migrating legacy PKCS#7 signatures to modern JOSE format.

      Authorization. The conversion step is authorized as convertPkcs7ToJoseStream(co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest, java.io.File, java.io.File): besides the migration.convertPkcs7 scope, the calling Cryptographic Actor needs a Capability Grant per leg, decided with the re-operation capabilities: REENCRYPT over [opener, recipient] for every other recipient and over the opener's own in-place pair (the opener being decryptionKid, or the first recipient whose key can decrypt), and RESIGN over each signer's in-place pair. A REENCRYPT/RESIGN grant is requested with exactly two assetKids: an in-place pair names the kid twice ([kid, kid], stored as [kid]), a cross pair is [opener, recipient], and there is no wildcard re-operation grant. A grant admits any request whose kids it contains, so the [opener, recipient] grant also covers the opener's in-place leg. ENCRYPT, DECRYPT and SIGN grants admit no conversion; a missing grant is refused with HTTP 403. Grants are created on the control plane.

      Example

      
       java.io.File pkcs7 = new java.io.File("/legacy/signed.p7s");
       java.io.File output = new java.io.File("/converted/signed.jws");
      
       co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest request =
           new co.ankatech.secure.client.model.Pkcs7ConversionStreamRequest();
      
       MigrationWorkflowResult result = sdk.performMigrationWorkflow(pkcs7, request, output);
      
       System.out.println("Migration complete");
       
      Parameters:
      pkcs7File - the PKCS#7 file to convert; must not be null
      conversionRequest - the conversion parameters; must not be null
      outputFile - where to write the JOSE output; must not be null
      Returns:
      migration result with details; never null
      Throws:
      AnkaSecureSdkException - if the workflow fails or the server returns an error
      NullPointerException - if any parameter is null
      See Also:
    • encryptFileUtilityStream

      public EncryptResult encryptFileUtilityStream(String kty, String alg, String publicKeyB64, Path input, Path output) throws AnkaSecureSdkException
      Encrypts a file using a provided public key (without requiring key in ANKASecure).

      This utility method allows encryption using an externally-managed public key, bypassing the normal key management workflow. Useful for one-off encryptions or interoperability with external systems.

      Example

      
       String publicKeyBase64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...";
      
       EncryptResult result = sdk.encryptFileUtilityStream(
           "RSA",
           "RSA-OAEP",
           publicKeyBase64,
           Path.of("/data/document.pdf"),
           Path.of("/data/document.pdf.jwe")
       );
       
      Parameters:
      kty - key type (e.g., "RSA", "EC", "ML-KEM"); must not be null
      alg - algorithm (e.g., "RSA-OAEP", "ML-KEM-768"); must not be null
      publicKeyB64 - base64-encoded public key; must not be null
      input - path to plaintext file; must not be null
      output - path where JWE will be written; must not be null
      Returns:
      encryption result containing operation metadata; never null
      Throws:
      AnkaSecureSdkException - if the key is invalid, algorithm is unsupported, files cannot be read/written, or the server returns an error
      NullPointerException - if any parameter is null
    • verifySignatureUtilityStream

      public VerifySignatureResult verifySignatureUtilityStream(String kty, String alg, String publicKeyB64, String signatureB64, Path dataFile) throws AnkaSecureSdkException
      Verifies a signature using a provided public key (without requiring key in ANKASecure).

      This utility method allows signature verification using an externally-managed public key, bypassing the normal key management workflow. Useful for verifying signatures from external systems.

      Example

      
       String publicKeyBase64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...";
       String signatureBase64 = "...";
      
       VerifySignatureResult result = sdk.verifySignatureUtilityStream(
           "RSA",
           "RS256",
           publicKeyBase64,
           signatureBase64,
           Path.of("/data/document.pdf")
       );
      
       if (result.isValid()) {
           System.out.println("Signature verified");
       }
       
      Parameters:
      kty - key type (e.g., "RSA", "EC", "ML-DSA"); must not be null
      alg - algorithm (e.g., "RS256", "ML-DSA-65"); must not be null
      publicKeyB64 - base64-encoded public key; must not be null
      signatureB64 - base64-encoded signature; must not be null
      dataFile - path to original data file; must not be null
      Returns:
      verification result containing validity status; never null
      Throws:
      AnkaSecureSdkException - if the key is invalid, algorithm is unsupported, files cannot be read, or the server returns an error
      NullPointerException - if any parameter is null
    • getSupportedAlgorithms

      public List<AlgorithmInfo> getSupportedAlgorithms() throws AnkaSecureSdkException
      Retrieves the complete list of cryptographic algorithms supported by the server.

      Returns all algorithms available for key generation, encryption, signing, and other cryptographic operations. Each AlgorithmInfo includes detailed metadata about the algorithm's characteristics, security level, and supported operations.

      Example

      
       List<AlgorithmInfo> allAlgorithms = sdk.getSupportedAlgorithms();
      
       // Find all post-quantum algorithms
       allAlgorithms.stream()
           .filter(alg -> alg.getCategory().equals("POST_QUANTUM"))
           .forEach(alg ->
               System.out.println(alg.getKty() + " / " + alg.getAlg() +
                   " (Level " + alg.getSecurityLevel() + ")")
           );
       
      Returns:
      list of all supported algorithm information; never null, never empty
      Throws:
      AnkaSecureSdkException - if the server returns an error or the request fails
      See Also:
    • getSupportedAlgorithms

      public List<AlgorithmInfo> getSupportedAlgorithms(AlgorithmFilter filter) throws AnkaSecureSdkException
      Retrieves cryptographic algorithms matching the specified filter criteria.

      Filters the algorithm catalog by category (classical, post-quantum, hybrid), key operations (encrypt, sign, etc.), security level, and other characteristics. Multiple filter criteria are combined with AND logic.

      Example

      
       // Find all NIST Level 5 post-quantum signing algorithms
       AlgorithmFilter filter = AlgorithmFilter.builder()
           .withCategory(AlgorithmInfo.Category.POST_QUANTUM)
           .withKeyOps("sign", "verify")
           .withSecurityLevel(5)
           .build();
      
       List<AlgorithmInfo> pqcSigning = sdk.getSupportedAlgorithms(filter);
      
       pqcSigning.forEach(alg ->
           System.out.println(alg.getKty() + " / " + alg.getAlg() +
               " (Level " + alg.getSecurityLevel() + ")")
       );
      
       // Use the first result to drive a data-plane operation
       if (!pqcSigning.isEmpty()) {
           AlgorithmInfo chosen = pqcSigning.get(0);
           System.out.println("Selected algorithm: " + chosen.getKty() + " / " + chosen.getAlg());
       }
       
      Parameters:
      filter - the algorithm filter specifying criteria such as category, key operations, and security level; must not be null
      Returns:
      list of algorithm information matching all specified filter criteria; never null, may be empty if no algorithms match
      Throws:
      AnkaSecureSdkException - if the server returns an error or the request fails
      NullPointerException - if filter is null
      See Also: