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
- Sign then encrypt:
- Java SDK:
sdk.signThenEncrypt(signKid, encryptKid, data)returns aSignEncryptResult. For files,signThenEncryptFileCompact(signKid, encryptKid, input, output)orsignThenEncryptFileStream(signKid, encryptKid, input, output). - Crypto CLI:
AnkaSecureCLI sign-encrypt-file --sign-kid <kid> --encrypt-kid <kid> --input-file <file> --output-file <file>, orsign-encrypt-file-streamwith the same options for large files. - API:
POST /api/v3/crypto/sign-encryptwith{"signKid", "encryptKid", "data"}and, optionally,serializationandvalidityDaysfor the inner signature. For a stream,POST /api/v3/crypto/stream/sign-encryptwith amultipart/form-databody,metadatabeforefile.
- Java SDK:
- Decrypt then verify:
- Java SDK:
sdk.decryptThenVerify(jwe)returns aDecryptVerifyResult; readgetDecryptedData()andgetSignatureResults(). For files,decryptThenVerifyFileCompact(input, output)ordecryptThenVerifyFileStream(input, output). - Crypto CLI:
AnkaSecureCLI decrypt-verify-file --input-file <file> --output-file <file>, ordecrypt-verify-file-streamfor a streaming artifact. - API:
POST /api/v3/crypto/decrypt-verifywith{"jweToken": {...}}. For a stream,POST /api/v3/crypto/stream/decrypt-verifywith themultipart/mixedheader and ciphertext parts.
- Java SDK:
- If the outer JWE is addressed to several keys, name the one your actor decrypts with:
recipientKid,--recipient-kid, or therecipientKidargument of the SDK methods. - On a streaming decrypt then verify, wait for the verdict part. The plaintext arrives before it; the overall
verdictisVALIDonly when both the integrity check (gcmTagVerdict) and the inner signatures (signatureVerdict) areVALID. 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) |