Skip to content

Decryption Failed

URI: https://docs.ankatech.co/errors/decryption-failed
HTTP Status: 422 Unprocessable Entity

When You See This

Your PKCS#7/CMS file is valid and supported and a matching private key was located, but the conversion engine could not decrypt the protected content. This is raised during PKCS#7 → JOSE conversion (POST /api/v3/migration/convert-pkcs7-to-jose and POST /api/v3/migration/stream/convert-pkcs7-to-jose) when the supplied recipient private key cannot decrypt the PKCS#7 EnvelopedData / SignedAndEnvelopedData content.

In other words: the key was found, but it does not actually unwrap this ciphertext — typically because the wrong recipient key was selected, or the PKCS#7 ciphertext is corrupted or truncated.

Common Causes

  • Wrong recipient key selected – The key of the decrypting recipient — the one decryptionKid names or, when it is absent, the first recipient in encoded order whose tenant key admits decryption and encryption — does not match the public key the file was encrypted to. There is no fallback to another recipient after a decryption failure

  • The content-encryption key cannot be unwrapped – The message's encrypted content-encryption key is malformed, or it was encrypted to a different recipient. For an RSA-OAEP recipient this is detected when the envelope is opened. For a PKCS #1 v1.5 (rsaEncryption) recipient it is not detected there: the key is replaced by a random one (RFC 3218 §2.3.2) and the failure surfaces at the content, like a wrong key or a corrupted ciphertext

  • Corrupted ciphertext – The PKCS#7 EnvelopedData content was altered or damaged in transit or storage

  • Truncated file – The PKCS#7 file is incomplete (cut off during upload, copy, or export)

  • An encrypted digest cannot be decrypted (SignedAndEnvelopedData only) – RFC 2315 §11.2 encrypts each outer signer's digest under the content-encryption key; a digest that does not decrypt is reported as this error, not as a signature failure

When the Streaming Endpoint Reports It

The streaming convert endpoint's 200 status line leaves the server with the first bytes of the conversion output that leave its response buffer — when the response is committed — so where the failure is detected decides how you receive it.

Before any output byte you receive this 422 with the problem document below, exactly as the compact endpoint returns it. That covers every decryption failure of a SignedAndEnvelopedData (its outer signers are verified under the content-encryption key before anything is written), the unwrap failure of an RSA-OAEP recipient of an EnvelopedData, and an EnvelopedData content failure reached while the response is still uncommitted.

After output has started — an EnvelopedData content failure reached once the response is committed, which includes a PKCS #1 v1.5 encrypted key that did not unwrap (it fails at the content, by design) as well as a corrupted or truncated ciphertext — there is no status left to change. The response is aborted: its framing is never completed, and no problem document is written into the body. A response that stops short is the failure signal.

Treat a short read as an error and discard the partial output. It will look valid: the emitted stream carries a single authentication tag at the very end rather than per-chunk authentication, so everything before the cut decrypts cleanly and only the missing tag reveals the truncation.

For the full rule, see PKCS#7 Conversion — the abort contract.

No Cryptographic Details Disclosed

Every decryption step — the unwrap of the encrypted content-encryption key, the content decryption, and for SignedAndEnvelopedData the decryption of each encrypted digest — answers the same status, the same errorCode and the same fixed detail. The response never reveals which step failed, the algorithm, padding state, or any key material. "Wrong key" and "corrupted ciphertext" therefore surface as the same message.

For a PKCS #1 v1.5 (rsaEncryption) decrypting recipient — the only recipient whose encrypted key is unwrapped — the conversion applies RFC 3218 §2.3.2, "Random Filling": when the encrypted key does not unwrap, unwraps to a block of the wrong length, or unwraps to a key the content cipher cannot take, it "substitute[s] a randomly generated CEK of appropriate length and continue[s]", the substitute being "cryptographically random". The failure then arises only where a wrong key would make it arise — at the content, or at an encrypted digest — so a caller cannot learn whether a crafted key-transport block unwrapped under the tenant's key, the oracle the million-message attack on PKCS #1 v1.5 is built from. This holds on every structure and on both endpoints.

What it does not guarantee:

  • An RSA-OAEP recipient is not random-filled: OAEP is itself one of the RFC 3218 §2.3 countermeasures (§2.3.3), so the §2.3.2 random filling is not applied to it, and an unwrap failure is refused at once, with the same body. On the streaming endpoint an EnvelopedData therefore refuses an OAEP unwrap failure when the envelope is opened, before any output byte, and a content failure while the output is being emitted — this same 422 while the response is still uncommitted, an aborted response once it is committed. The two can be told apart only when the content failure arrives after the response is committed.
  • A structural CMS failure raised before any key is used is refused at once, with the same body.
  • The timing of a refusal is not made constant.
  • A wrong or random content key decrypts to a correctly padded block with a probability of about 1/256. A SignedAndEnvelopedData then fails at its signer verification; an EnvelopedData, which carries no integrity check, converts content that is not the plaintext. Current limitation: that case is not detected for an EnvelopedData.

Response Example

{
  "type": "https://docs.ankatech.co/errors/decryption-failed",
  "title": "Decryption Failed",
  "status": 422,
  "detail": "PKCS#7 decryption failed. The content could not be decrypted with the private key of the decrypting recipient: either that key does not match the public key the file was encrypted to, or the file's encrypted key, encrypted content or content-encryption algorithm cannot be processed. Verify that the correct private key has been imported via POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore.",
  "instance": "/api/v3/migration/convert-pkcs7-to-jose",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": 1738117563,
  "extensions": {
    "errorCode": "DECRYPTION_FAILED",
    "possibleCauses": [
      "Private key does not match the public key used for encryption",
      "PKCS#7 data is corrupted or truncated",
      "Wrong recipient key selected in multi-recipient PKCS#7"
    ],
    "resolution": "Verify the correct private key is imported via POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore"
  }
}

Both endpoints answer this one body: the same type, title, status and detail, and the same extensions — errorCode DECRYPTION_FAILED, possibleCauses and resolution. Only the per-request members (instance, correlationId, timestamp) differ. Branch on type or on extensions.errorCode.

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.

How to Resolve

Step 1: Confirm the Recipient Metadata

Re-run the analyze endpoint to see which recipient the message was encrypted for:

POST /api/v3/migration/analyze-pkcs7
Content-Type: application/json

{
  "pkcs7Data": "<base64-encoded-pkcs7>"
}

The response shows the recipient's issuerDN and serialNumber — these identify the exact private key required for decryption.

Step 2: Verify the Correct Private Key Is Imported

The file names the recipient's certificate by issuer DN and serial number only; it does not carry the certificate. Export the public key of the tenant key matched to that recipient and compare it with the public key of the recipient's certificate:

GET /api/v3/admin/tenants/{tenantId}/keys/{kid}/export

The operation requires the admin.keys.read scope and returns public material only. Its publicKey (Base64 SubjectPublicKeyInfo) must equal the public key of the certificate identified in Step 1. If it does not, select (or import) the correct key. A key imported as non-exportable (exportable false) is refused 400 by this operation; compare it at the source system instead.

Step 3: Re-Run the Conversion

Retry with the decryptionKid that resolves to the matching recipient key (or omit it, and the first recipient in encoded order whose tenant key admits decryption and encryption decrypts):

POST /api/v3/migration/convert-pkcs7-to-jose
Content-Type: application/json

{
  "pkcs7Data": "<base64-encoded-pkcs7>",
  "decryptionKid": "my-recipient-key",
  "serialization": "AUTO",
  "targetFormat": "AUTO"
}

Step 4: Rule Out Corruption

If the correct key is confirmed and decryption still fails, the PKCS#7 content is likely damaged:

  • Re-export or re-download the PKCS#7 file from the source system
  • Confirm the file is complete and was not truncated during transfer
  • Compare the file size against the original to detect truncation

Need Help?

  • Use POST /api/v3/migration/analyze-pkcs7 to confirm the recipient metadata before retrying
  • If the correct key is imported and the file is intact but conversion still fails, contact support with the correlationId from the error response