Skip to content

Sign then encrypt, decrypt then verify

Sign data and encrypt the signed result in one operation, so the signature travels encrypted (a JWE whose plaintext is a JWS), and recover the data only when the signature verifies.

Who can do it

An application authenticated as a cryptographic actor of your tenant. Its token must carry the scope of the operation, and the actor must hold active Capability Grants on both keys:

Operation Scope Capability Grants
Sign then encrypt in one request secure.signAndEncrypt SIGN on the signing key, ENCRYPT on the encryption key
Decrypt then verify in one request secure.decryptAndVerify DECRYPT on the decryption key, VERIFY on every inner signer
Sign then encrypt a stream stream.signAndEncryptStream SIGN on the signing key, ENCRYPT on the encryption key
Decrypt then verify a stream stream.decryptAndVerifyStream DECRYPT on the decryption key, VERIFY on every inner signer

No console role performs these operations.

Before you start

  • Two different keys exist in the tenant: one that signs and one that encrypts. Each key must serve its own leg: a composite signature key can sign, a composite KEM key can encrypt. See Create a key.
  • The application can obtain a token. See Get an access token for an application.

Steps

  1. Sign then encrypt:
    • Java SDK: sdk.signThenEncrypt(signKid, encryptKid, data) returns a SignEncryptResult. For files, signThenEncryptFileCompact(signKid, encryptKid, input, output) or signThenEncryptFileStream(signKid, encryptKid, input, output).
    • Crypto CLI: AnkaSecureCLI sign-encrypt-file --sign-kid <kid> --encrypt-kid <kid> --input-file <file> --output-file <file>, or sign-encrypt-file-stream with the same options for large files.
    • API: POST /api/v3/crypto/sign-encrypt with {"signKid", "encryptKid", "data"} and, optionally, serialization and validityDays for the inner signature. For a stream, POST /api/v3/crypto/stream/sign-encrypt with a multipart/form-data body, metadata before file.
  2. Decrypt then verify:
    • Java SDK: sdk.decryptThenVerify(jwe) returns a DecryptVerifyResult; read getDecryptedData() and getSignatureResults(). For files, decryptThenVerifyFileCompact(input, output) or decryptThenVerifyFileStream(input, output).
    • Crypto CLI: AnkaSecureCLI decrypt-verify-file --input-file <file> --output-file <file>, or decrypt-verify-file-stream for a streaming artifact.
    • API: POST /api/v3/crypto/decrypt-verify with {"jweToken": {...}}. For a stream, POST /api/v3/crypto/stream/decrypt-verify with the multipart/mixed header and ciphertext parts.
  3. If the outer JWE is addressed to several keys, name the one your actor decrypts with: recipientKid, --recipient-kid, or the recipientKid argument of the SDK methods.
  4. On a streaming decrypt then verify, wait for the verdict part. The plaintext arrives before it; the overall verdict is VALID only when both the integrity check (gcmTagVerdict) and the inner signatures (signatureVerdict) are VALID. The SDK and the CLI write the output only then.

What you see when it worked

Sign then encrypt returns the nested JWE with the signing and the encryption metadata reported separately. Decrypt then verify returns the plaintext together with one signatureResults entry per inner signature, each with its key, algorithm, material version and verdict. The one-request operation releases no plaintext unless every inner signature verifies.

If it fails

What you see Cause What to do
400 .../errors/invalid-input The two keys are the same, a key does not serve its leg (a signing key used to encrypt, or the reverse), or the data is not valid Base64 Use two different keys, each of the right kind
400 .../errors/serialization-incompatible-with-timestamp Compact serialization was requested for the inner signature and the tenant requires a qualified timestamp Request JSON, or omit serialization
403 .../errors/forbidden The token lacks the scope, or the actor holds no grant on one of the keys Ask a tenant administrator to check the actor's roles and grants
404 .../errors/not-found A key does not exist Check the key identifiers
409 .../errors/invalid-key-state One of the keys is in a state that does not permit the operation, for example revoked Use active keys
422 on decrypt then verify An inner signature does not verify, the ciphertext fails its integrity check, or the token is malformed. No plaintext is returned Do not use the data
Streaming decrypt then verify ends with verdict INVALID The ciphertext was altered, an inner signature does not verify, or the actor may not verify with an inner signer Discard the output; the SDK raises DecryptVerifyIntegrityException with each signer's verdict and writes no file
413 .../errors/payload-too-large The data is above the one-request limit Use the streaming operation

Without the console

These operations have no console screen.

Surface How
Java SDK signThenEncrypt, signThenEncryptFileCompact, signThenEncryptFileStream, decryptThenVerify, decryptThenVerifyFileCompact, decryptThenVerifyFileStream on AuthenticatedSdk
Crypto CLI AnkaSecureCLI sign-encrypt-file, decrypt-verify-file, sign-encrypt-file-stream, decrypt-verify-file-stream
API POST /api/v3/crypto/sign-encrypt (signThenEncrypt), POST /api/v3/crypto/decrypt-verify (decryptThenVerify), POST /api/v3/crypto/stream/sign-encrypt (signEncryptStream), POST /api/v3/crypto/stream/decrypt-verify (decryptVerifyStream)