Skip to content

Analyze Operations

Purpose: Validating and assessing imported cryptographic material before conversion

Version: 3.0.0


Overview of Analyze Phase

The Analyze phase is the second step in the migration workflow: Import → Analyze → Convert. After importing cryptographic keys and certificates, you must validate their compatibility and security status before attempting conversion to post-quantum algorithms.

Purpose: Validate before conversion

Key Activities:

  • Certificate validation (X.509 structure, expiration, chain integrity)
  • Key compatibility analysis (algorithm identification, key sizes, usage constraints)
  • Security analysis (expiration assessment, weakness detection)
  • Status determination — the certificate's temporal and revocation state and the operation restrictions it implies (not the imported key's own lifecycle status, which is one of the nine published key-status tokens: active, pending_rotation, rotated, expired, revoked, archived, deleted_logical, suspended, pending_destruction)
  • PQC migration path mapping (which classical algorithm maps to which PQC algorithm)

Outcome: Clear understanding of which keys can be converted, which operations are allowed, and what PQC algorithms to use.


Certificate Validation Analysis

X.509 Structure Validation

Purpose: Ensure imported certificates conform to X.509 standard

Validation Checks:

  • Certificate version (v1, v2, v3)
  • Serial number uniqueness
  • Signature algorithm compatibility
  • Subject and Issuer DN (Distinguished Name) structure
  • Validity dates format (notBefore, notAfter)
  • Public key algorithm and parameters
  • Extensions structure (if present)

Common Issues:

  • Malformed certificates (encoding errors, truncated data)
  • Unsupported certificate versions
  • Invalid DN syntax
  • Missing required fields

Resolution: Reject import with detailed error message identifying structural issue.

Key Binding Validation

Purpose: Cryptographic proof that private key matches certificate's public key

Validation Process:

graph LR
    PrivKey["Private Key"] -->|1. Sign| TestData["Test Data<br/>Random bytes"]
    TestData -->|2. Signature| Sig["Digital Signature"]
    Cert["Certificate"] -->|3. Extract| PubKey["Public Key"]
    Sig -->|4. Verify| Verify{"Verification"}
    PubKey -->|4. Verify| Verify

    Verify -->|Success| Match["✅ Keys Match<br/>Import allowed"]
    Verify -->|Failure| Mismatch["❌ Keys Mismatch<br/>Reject import"]

    style Match fill:#51cf66,stroke:#333,stroke-width:2px
    style Mismatch fill:#ff6b6b,stroke:#333,stroke-width:2px

Why This Matters: Prevents importing mismatched key-certificate pairs that would fail in production.

Temporal Validation

Purpose: Check certificate validity period

Validation Rules:

Condition validationMode=STRICT validationMode=IMPORT_ONLY validationMode=SKIP
Not yet valid (now < notBefore) ❌ Reject ❌ Reject ❌ Reject
Valid (notBefore ≤ now ≤ notAfter) ✅ Accept, ACTIVE ✅ Accept, ACTIVE ✅ Accept, ACTIVE
Expired (now > notAfter) ❌ Reject ✅ Accept, EXPIRED, restricted=true ✅ Accept, ACTIVE

Key Insight: Expired certificates can still decrypt historical data, hence IMPORT_ONLY mode allows import with restrictions.

Chain Signature Verification

Purpose: Validate cryptographic integrity of certificate chain

Validation Process:

graph TD
    EndEntity["End-Entity Certificate<br/>CN=server.example.com"]
    Intermediate["Intermediate CA<br/>CN=Example Intermediate CA"]
    Root["Root CA<br/>CN=Example Root CA"]

    EndEntity -->|Signed by| Intermediate
    Intermediate -->|Signed by| Root
    Root -->|Self-signed| Root

    EndEntity -->|Verify signature| V1{"Valid?"}
    Intermediate -->|Verify signature| V2{"Valid?"}

    V1 -->|Yes| V2
    V2 -->|Yes| TrustChain["✅ Chain Valid"]
    V1 -->|No| ChainBroken["❌ Chain Broken"]
    V2 -->|No| ChainBroken

    style TrustChain fill:#51cf66,stroke:#333,stroke-width:2px
    style ChainBroken fill:#ff6b6b,stroke:#333,stroke-width:2px

Common Issues:

  • Intermediate CA missing from PKCS#12 file (incomplete chain)
  • Signature algorithm mismatch between certificates
  • Self-signed end-entity certificate (no chain validation possible)

PKIX Trust Anchor Validation

Purpose: Verify chain terminates at a trusted root CA (RFC 5280)

When Performed: Only if the operator has configured trusted CA certificates on the deployment

Validation Logic:

graph TD
    Start{"Trust anchors<br/>configured?"}
    Start -->|No| SkipPKIX["Skip PKIX validation"]
    Start -->|Yes| CheckCert{"Certificate type?"}

    CheckCert -->|Self-signed| SkipPKIX
    CheckCert -->|Public CA| ValidatePublic["Validate against<br/>Mozilla CA bundle"]
    CheckCert -->|Private CA| ValidatePrivate["Validate against<br/>custom trust anchors"]

    ValidatePublic -->|Root found| PKIXPass["✅ PKIX validation passed"]
    ValidatePublic -->|Root NOT found| PKIXFail["❌ Untrusted root CA"]

    ValidatePrivate -->|Root found| PKIXPass
    ValidatePrivate -->|Root NOT found| PKIXFail

    PKIXPass -->|STRICT mode| AcceptImport["Accept import"]
    PKIXFail -->|STRICT mode| RejectImport["Reject import"]
    PKIXFail -->|IMPORT_ONLY| AcceptRestricted["Accept with<br/>origin=IMPORTED<br/>restricted=true<br/>decrypt/verify only"]

    style PKIXPass fill:#51cf66,stroke:#333,stroke-width:2px
    style PKIXFail fill:#ff6b6b,stroke:#333,stroke-width:2px
    style AcceptRestricted fill:#ffd43b,stroke:#333,stroke-width:2px

Supported Trust Anchors:

  • Public CAs: Mozilla CA bundle (144 trusted root CAs)
  • Corporate CAs: Custom root CAs added to trust-anchors directory
  • Self-signed: Automatically skipped (no trust anchor applicable)

Extension Validation

Purpose: Validate certificate extensions (keyUsage, extendedKeyUsage)

Key Extensions Analyzed:

keyUsage (RFC 5280):

  • digitalSignature
  • nonRepudiation
  • keyEncipherment
  • dataEncipherment
  • keyAgreement
  • keyCertSign
  • cRLSign

extendedKeyUsage (RFC 5280):

  • serverAuth (TLS server)
  • clientAuth (TLS client)
  • codeSigning
  • emailProtection
  • timeStamping

Validation Rules:

Extension Present Validation
keyUsage Yes Verify usage aligns with intended operations
keyUsage No ⚠️ Warning only (older certificates may lack this)
extendedKeyUsage Yes Informational (log usage types)
extendedKeyUsage No No action

Key Compatibility Analysis

Algorithm Identification

Purpose: Determine current cryptographic algorithm for migration path mapping

Supported Algorithms:

Algorithm Family Specific Algorithms PQC Equivalent
RSA Encryption RSA-1024, RSA-2048, RSA-3072, RSA-4096 ML-KEM-512/768/1024
ECC Encryption ECDH P-256, P-384, P-521 ML-KEM-768/1024
RSA Signing RSA-SHA256, RSA-SHA512 ML-DSA-65/87, FALCON-512/1024
ECC Signing ECDSA P-256, P-384, P-521 ML-DSA-65/87, FALCON-512/1024

Algorithm Detection:

graph TD
    Cert["Certificate"] -->|Extract| PubKey["Public Key"]
    PubKey -->|OID Analysis| OID{"Algorithm OID?"}

    OID -->|1.2.840.113549.1.1.*| RSA["RSA Family"]
    OID -->|1.2.840.10045.2.1| ECC["ECC Family"]
    OID -->|Other| Unsupported["⚠️ Unsupported Algorithm"]

    RSA -->|Key size| RSASize{"Size?"}
    RSASize -->|1024 bits| RSA1024["RSA-1024<br/>→ ML-KEM-512"]
    RSASize -->|2048 bits| RSA2048["RSA-2048<br/>→ ML-KEM-768"]
    RSASize -->|4096 bits| RSA4096["RSA-4096<br/>→ ML-KEM-1024"]

    ECC -->|Curve| ECCCurve{"Curve?"}
    ECCCurve -->|P-256| P256["P-256<br/>→ ML-KEM-768"]
    ECCCurve -->|P-384| P384["P-384<br/>→ ML-KEM-1024"]

    style RSA2048 fill:#4dabf7,stroke:#333,stroke-width:2px
    style RSA4096 fill:#4dabf7,stroke:#333,stroke-width:2px
    style P256 fill:#4dabf7,stroke:#333,stroke-width:2px

Key Size Verification

Purpose: Ensure key size is within acceptable security parameters

Security Thresholds:

Algorithm Minimum Size Recommended Size Status
RSA 1024 bits 2048+ bits 1024 deprecated
ECC 224 bits 256+ bits 224 deprecated
AES 128 bits 256 bits All sizes valid

Analysis Output:

{
  "algorithm": "RSA",
  "keySize": 2048,
  "securityLevel": "112-bit classical security",
  "pqcEquivalent": "ML-KEM-768",
  "migrationRecommendation": "Suitable for migration to ML-KEM-768"
}

Usage Constraints (keyOps)

Purpose: Determine which operations are allowed based on certificate validation results

keyOps Determination Logic:

stateDiagram-v2
    [*] --> ValidateCert: Import request

    ValidateCert --> CheckStatus: Validation complete

    CheckStatus --> ValidCert: Certificate valid
    CheckStatus --> ExpiredCert: Certificate expired
    CheckStatus --> RevokedCert: Certificate revoked

    ValidCert --> AllOps: keyOp [sign, verify, encrypt, decrypt]
    ValidCert --> SetActive: status ACTIVE
    ValidCert --> SetOrigin: origin IMPORTED, restricted false

    ExpiredCert --> CheckMode: Check validationMode

    CheckMode --> StrictMode: STRICT
    CheckMode --> ImportOnlyMode: IMPORT_ONLY

    StrictMode --> Reject: ❌ Reject import

    ImportOnlyMode --> RestrictedOps: keyOps [decrypt, verify]
    ImportOnlyMode --> SetExpired: status EXPIRED
    ImportOnlyMode --> SetRestricted: origin IMPORTED, restricted true

    RevokedCert --> AllowDecrypt: keyOps [decrypt, verify]
    RevokedCert --> SetRevoked: status REVOKED
    RevokedCert --> SetRestrictedRevoked: origin IMPORTED, restricted true

    AllOps --> [*]
    Reject --> [*]
    RestrictedOps --> [*]

keyOps Rules:

  • Valid certificate: All operations allowed (sign, verify, encrypt, decrypt)
  • Expired certificate (IMPORT_ONLY): Read-only operations (decrypt, verify) - cannot create new encrypted data or signatures
  • Revoked certificate: Read-only operations (decrypt, verify) - same as expired
  • Not yet valid: Rejected (no keyOps assigned)

PQC Migration Path Mapping

Purpose: Recommend post-quantum algorithm for migration

Mapping Matrix:

graph LR
    subgraph Classical["Classical Algorithms"]
        RSA2048["RSA-2048"]
        RSA4096["RSA-4096"]
        ECDSA256["ECDSA P-256"]
        ECDSA384["ECDSA P-384"]
    end

    subgraph PQC["Post-Quantum Recommendations"]
        MLKEM768["ML-KEM-768<br/>Encryption"]
        MLKEM1024["ML-KEM-1024<br/>Encryption"]
        MLDSA65["ML-DSA-65<br/>Signatures"]
        MLDSA87["ML-DSA-87<br/>Signatures"]
    end

    RSA2048 -->|Encryption| MLKEM768
    RSA4096 -->|Encryption| MLKEM1024
    RSA2048 -->|Signing| MLDSA65
    RSA4096 -->|Signing| MLDSA87
    ECDSA256 -->|Signing| MLDSA65
    ECDSA384 -->|Signing| MLDSA87

    style MLKEM768 fill:#51cf66,stroke:#333,stroke-width:2px
    style MLKEM1024 fill:#51cf66,stroke:#333,stroke-width:2px
    style MLDSA65 fill:#4dabf7,stroke:#333,stroke-width:2px
    style MLDSA87 fill:#4dabf7,stroke:#333,stroke-width:2px

Security Analysis

Expiration Assessment

Purpose: Determine urgency and risk of migration

Timeline Categories:

Certificate Status Days Until Expiry Risk Level Action Required
Expired < 0 (already expired) 🔴 Critical Immediate: Use IMPORT_ONLY to decrypt legacy data
Near expiry 0-30 days 🟠 High Urgent: Migrate within 1 week
Short-term valid 31-90 days 🟡 Medium Plan: Migrate within 1 month
Long-term valid 91-365 days 🟢 Low Schedule: Migrate within quarter
Very long-term > 365 days 🔵 Minimal Defer: Include in annual migration cycle

Weakness Detection

Purpose: Identify cryptographic weaknesses requiring immediate attention

Analyzed Weaknesses:

  1. Deprecated Algorithms:

    • RSA < 2048 bits (NIST deprecated 2013)
    • ECC < 224 bits (NIST deprecated 2015)
    • SHA-1 signatures (NIST deprecated 2017)
    • 3DES encryption (NIST deprecated 2023)
  2. Weak Key Generation:

    • Low-entropy keys (e.g., generated on embedded devices)
    • Small prime factors (RSA-specific)
    • Predictable random number generators
  3. Implementation Vulnerabilities:

    • Padding oracle vulnerabilities (PKCS#1 v1.5)
    • Timing attacks (RSA decryption without blinding)
    • Side-channel attack susceptibility

Detection Output:

{
  "weaknesses": [
    {
      "type": "DEPRECATED_ALGORITHM",
      "severity": "HIGH",
      "message": "RSA-1024 deprecated since 2013",
      "recommendation": "Migrate to ML-KEM-768 immediately"
    },
    {
      "type": "WEAK_HASH",
      "severity": "MEDIUM",
      "message": "Certificate uses SHA-1 signature (deprecated)",
      "recommendation": "Re-issue certificate with SHA-256+"
    }
  ]
}

Compliance Checking

Purpose: Verify alignment with regulatory requirements

Compliance Frameworks Checked:

  • NIST SP 800-57: Key management guidelines
  • NIST SP 800-131A: Transitioning to stronger cryptography
  • FIPS 140-3: Cryptographic module validation
  • PCI-DSS 4.0: Payment card industry requirements
  • HIPAA: Healthcare data encryption requirements

Compliance Rules:

Framework RSA Minimum ECC Minimum Hash Minimum Verdict
NIST 2024 2048 bits 256 bits SHA-256 ✅/❌
PCI-DSS 4.0 2048 bits 256 bits SHA-256 ✅/❌
FIPS 140-3 2048 bits 256 bits SHA-256 ✅/❌

Analysis Results Interpretation

Status Codes

Keys can be in one of the following states after import and analysis:

Status Meaning Operations Allowed Migration Priority
ACTIVE Valid certificate, all operations allowed All (sign, verify, encrypt, decrypt) Medium (scheduled migration)
EXPIRED Certificate expired, read-only operations decrypt, verify only High (migrate soon to restore functionality)
REVOKED Certificate revoked, read-only operations decrypt, verify only High (security risk if used for new operations)
PENDING_ROTATION Scheduled for PQC migration All (during transition period) In progress
ROTATED Successfully migrated to PQC Legacy: decrypt/verify, PQC: all ops Complete (cleanup legacy key)

Key Origin and Restriction Status

The origin and restricted fields indicate special handling requirements:

origin: "IMPORTED", restricted: false (Valid Imported Key):

  • Imported with valid certificate
  • All operations allowed
  • Behaves identically to native ANKASecure key
  • Can be rotated to PQC successor
  • Subject to standard lifecycle policies

origin: "IMPORTED", restricted: true (Restricted Key):

  • Imported with expired/revoked certificate or untrusted CA
  • Operations restricted to decrypt/verify only
  • Cannot create new encrypted data or signatures
  • Cannot be rotated (migration requires new key generation)
  • Intended for legacy data recovery

Allowed Operations Matrix

graph TD
    Key["Imported Key"] -->|Analyze| Status{"Status?"}
    Status -->|ACTIVE| RestrictedCheck{"restricted?"}
    Status -->|EXPIRED| RestrictedOps["decrypt, verify only"]
    Status -->|REVOKED| RestrictedOps

    RestrictedCheck -->|false| AllOps["All operations:<br/>sign, verify, encrypt, decrypt"]
    RestrictedCheck -->|true| RestrictedOps

    AllOps -->|Can perform| NewData["Create new encrypted data<br/>Create new signatures"]
    RestrictedOps -->|Can perform| OldData["Decrypt restricted data<br/>Verify restricted signatures"]

    AllOps -.->|Cannot perform| RestrictedOnly["Restricted data recovery<br/>if future cert expires"]
    RestrictedOps -.->|Cannot perform| NewData

    style AllOps fill:#51cf66,stroke:#333,stroke-width:2px
    style RestrictedOps fill:#ffd43b,stroke:#333,stroke-width:2px

Example: Analyzing Multi-Key PKCS#12

Step-by-Step Analysis Workflow

Scenario: Import PKCS#12 file with 3 keys, analyze each for migration planning.

PKCS#12 Contents:

  • Key 1: RSA-2048, certificate expires 2026-12-31 (valid)
  • Key 2: RSA-4096, certificate expired 2023-06-15
  • Key 3: ECDSA P-256, certificate expires 2027-03-31 (valid)

Analysis Process:

sequenceDiagram
    participant Admin
    participant Import as Import API
    participant Analyze as Analysis Engine
    participant Output as Analysis Report

    Admin->>Import: POST /keystores<br/>{validationMode: IMPORT_ONLY}
    Import->>Analyze: Analyze Key 1 (RSA-2048, valid)
    Analyze->>Output: ✅ ACTIVE, origin: IMPORTED, restricted: false<br/>keyOps: all → Migrate to ML-KEM-768

    Import->>Analyze: Analyze Key 2 (RSA-4096, expired)
    Analyze->>Output: ⚠️ EXPIRED, origin: IMPORTED, restricted: true<br/>keyOps: [decrypt, verify]<br/>→ Generate new ML-KEM-1024

    Import->>Analyze: Analyze Key 3 (ECDSA P-256, valid)
    Analyze->>Output: ✅ ACTIVE, keyOps: all<br/>→ Migrate to ML-DSA-65

    Output-->>Admin: Analysis Complete<br/>Migration plan generated

Analysis Report:

{
  "totalKeys": 3,
  "activeKeys": 2,
  "expiredKeys": 1,
  "migrationPlan": [
    {
      "kid": "key-1-rsa2048",
      "status": "active",
      "algorithm": "RSA-2048",
      "expiresAt": "2026-12-31T23:59:59Z",
      "pqcTarget": "ML-KEM-768",
      "migrationStrategy": "Rotate to ML-KEM-768",
      "priority": "MEDIUM"
    },
    {
      "kid": "key-2-rsa4096",
      "status": "expired",
      "origin": "IMPORTED",
      "restricted": true,
      "algorithm": "RSA-4096",
      "expiresAt": "2023-06-15T23:59:59Z",
      "pqcTarget": "ML-KEM-1024",
      "migrationStrategy": "Generate new ML-KEM-1024, decrypt restricted data, retire RSA key",
      "priority": "HIGH"
    },
    {
      "kid": "key-3-ecdsa256",
      "status": "active",
      "algorithm": "ECDSA P-256",
      "expiresAt": "2027-03-31T23:59:59Z",
      "pqcTarget": "ML-DSA-65",
      "migrationStrategy": "Rotate to ML-DSA-65",
      "priority": "MEDIUM"
    }
  ]
}

Decision Making Based on Results

For Key 1 (ACTIVE, RSA-2048):

  • ✅ All operations allowed
  • Migration path: RSA-2048 → ML-KEM-768
  • Timeline: Before 2026-12-31
  • Strategy: Standard rotation workflow

For Key 2 (EXPIRED, RSA-4096, restricted=true):

  • ⚠️ Read-only operations (decrypt, verify)
  • Cannot use for new encryptions
  • Migration path: Generate new ML-KEM-1024 key
  • Timeline: Immediate (already expired)
  • Strategy:
  • Keep RSA-4096 for decrypting historical data
  • Generate new ML-KEM-1024 key for new operations
  • Eventually retire RSA-4096 when restricted data no longer needed

For Key 3 (ACTIVE, ECDSA P-256):

  • ✅ All operations allowed
  • Migration path: ECDSA P-256 → ML-DSA-65 (signatures)
  • Timeline: Before 2027-03-31
  • Strategy: Standard rotation workflow with hybrid signatures during transition

PKCS#7 File Analysis

For organizations migrating from legacy PKCS#7/CMS systems (S/MIME and other legacy systems), ANKASecure provides specialized analysis endpoints.

Purpose: Inspect PKCS#7 structure before conversion to understand:

  • Number of signers and recipients
  • Algorithms used (signature, encryption)
  • Certificate metadata
  • Conversion feasibility (supported / unsupported)

Endpoints:

  • POST /api/v3/migration/analyze-pkcs7 (compact; the decoded PKCS#7 data is bounded by the key-import bound, 5 MB by default, which the operator can change)
  • POST /api/v3/migration/stream/analyze-pkcs7 (streaming; the file is parsed incrementally as it is received)

Both carry the scope migration.analyzePkcs7.

The streaming arm declares a 413 for the 2 MiB cap on the metadata part; it never refers to the file part. See the declared error contract below.

The streaming request is multipart/form-data with metadata first and file second, both required; see PKCS#7 Conversion for the part-ordering contract and the 400 .../errors/multipart-part-order it produces.

Declared error contract

The streaming arm's documented status set is 200, 400, 401, 403, 408, 413, 422, 429, 500, 503. The 408 is the streaming read budget while the metadata part is read, and the 503 the effective-status check, which fails closed when the calling application's or actor's status cannot be verified.

About that set:

  • 400 reports a malformed CMS structure.

  • 413 is the metadata part cap. The streaming arm answers 413 with the type multipart-part-too-large when the metadata part exceeds 2 MiB. Branch on the type, never on the status alone: the compact arm's 413 is payload-too-large, answered when the decoded data exceeds the key-import bound.

  • 429 limits concurrency. It bounds how many streaming requests may be in flight at once, globally and per tenant. The refusal is immediate and carries Retry-After; requests are not queued.

  • 404 is not returned. Analysis performs no decryption and no key resolution, so no not-found condition can arise.

  • 422 reports an unsupported PKCS#7 structure.

The two arms differ only where their behaviour differs

POST /api/v3/migration/analyze-pkcs7 requires the same scope as its streaming sibling, so a token without migration.analyzePkcs7 is refused with a 403. The compact arm returns that 403, and the same effective-status 503, so its documented set is 200, 400, 401, 403, 413, 422, 500, 503. It differs from the streaming arm's in three places: the compact arm has no 429 (the concurrency bound covers the streaming routes only) and no 408 (it reads no multipart body), and its 413 is payload-too-large, answered when the decoded data exceeds the key-import bound.

Example request:

{
  "pkcs7Data": "MIIGHgYJKoZIhvcNAQcCoIIGDzCCBgsCAQAxggE..."
}

Analysis takes no key and no option. It is purely structural — nothing is decrypted, no certificate path and no timestamp is validated — so the compact request carries pkcs7Data only, and the streaming request's metadata part is an empty JSON object, {}, still required and still first. A member either request does not define is refused 400 invalid-input with a detail naming the member (never its value): a client that still sends decryptionKid, validateCertificates or validateTimestamp is refused. decryptionKid belongs to the convert operations, where it names the decrypting recipient.

Every actor is reported. signers[] and recipients[] list each signer and each recipient in encoded order, and the number of either never makes a structure unsupported. For SignedAndEnvelopedData the signers are the outer signerInfos of RFC 2315 §11. A serialNumber is 0x followed by the upper-case hexadecimal of the serial's DER INTEGER content octets, as the certificate carries them (a serial whose first octet is 80-FF keeps its leading 00, for example 0x009F8E7D6C5B4A3B2C); matching uses the serial number itself, never this text.

What recommendations says:

Member Meaning
migrationPath The JOSE form a conversion under serialization AUTO produces, per structure: CONVERT_TO_JWS_* (SignedData), CONVERT_TO_JWE_* (EnvelopedData), CONVERT_TO_JWE_JWS_* (SignedAndEnvelopedData — a JWE whose plaintext is a JWS). The suffix is _COMPACT when the structure carries at most one signer and at most one recipient, _JSON (General JSON serialization, RFC 7515 §7.2.1 / RFC 7516 §7.2.1) otherwise.
requiredKeys[] One entry per actor: every signer (purpose signing), then every recipient (purpose encryption), each named by the issuerDN and serialNumber the tenant key's certificate must carry. Every one needs a key imported with its private key: a signer a key admissible to sign; the recipient that decrypts a key admissible to decrypt and encrypt; every other recipient a key admissible to encrypt (a keystore import with its private key, or the encryption key of a DUAL import). Current limitation: a certificate imported alone is a verify-only trust anchor and is admissible neither as a signer nor as a recipient.
decryptionRequirement Present for an encrypted structure, always ONE_RECIPIENT_PRIVATE_KEY: exactly one recipient's key opens the content — the one decryptionKid names, or else the first recipient in encoded order whose tenant key admits decryption and encryption.
estimatedComplexity LOW, MEDIUM or HIGH; HIGH whenever there is more than one signer or more than one recipient.

What supportStatus says. Analysis runs every check a conversion runs without a key and reports each refusal with the conversion's own code, so supported is false exactly when the conversion would refuse the structure before any key is used. The reasons are listed in the order the conversion decides them, so the first ERROR reason is the refusal a conversion of the same bytes answers:

  1. ERROR — the structure refusals. For a SignedData: INVALID_PKCS7, what the conversion answers 400 invalid-pkcs7 (no signer, or no encapsulated content). For a SignedAndEnvelopedData: UNSUPPORTED_CONTENT_ENCRYPTION (a content cipher other than AES-CBC or 3DES), then SIGNED_AND_ENVELOPED_WITHOUT_SIGNER, UNSUPPORTED_SIGNED_AND_ENVELOPED_SIGNER_KEY and SIGNED_AND_ENVELOPED_DIGEST_NOT_ENCRYPTED.
  2. ERROR — ACTOR_LIMIT_EXCEEDED: more signers and recipients together than one conversion accepts. The bound is 16, and every analysis response states it in limits.maxActors, the same value the conversion enforces — the way a client discovers the bound rather than hard-coding it.
  3. WARNING — LEGACY_ALGORITHM_REJECTED: a signer on SHA-1 or MD5, which the conversion refuses unless algorithmPolicy.allowLegacyHash is true.
  4. INFO — TIMESTAMP_DETECTED: an RFC 3161 timestamp in a signer, reported and not validated.

Content-encryption parameters that encode a key size their RFC does not define are not a reason either: RC2/CAST5 parameters outside RFC 2268 §6 / RFC 2984 §2.1 (undefined effective key size, malformed CAST5 parameters) refuse the analysis itself 422 unsupported-pkcs7-format with errorCode UNSUPPORTED_CONTENT_ENCRYPTION, on every structure, EnvelopedData included, as they refuse every conversion parse. On the streaming analysis, a declared ASN.1 length above the structure budget (1 MiB) is refused 400 invalid-pkcs7 before it is allocated, with the body a malformed file receives.

A signer identified by SubjectKeyIdentifier rather than issuer and serial number is refused 422 unsupported-pkcs7-format with errorCode UNSUPPORTED_SIGNER_IDENTIFIER, and a recipient that is not RSA key transport named by issuer and serial number with UNSUPPORTED_RECIPIENT_TYPE — the analysis itself is refused, since those actors cannot be reported. The detail names the signer's index. For a recipient it names the index — "PKCS#7 recipient at index N is not a key-transport recipient named by issuer and serial number. Only KeyTransRecipientInfo (RSA) is supported." — for a key-transport recipient identified by SubjectKeyIdentifier on any structure and for every recipient refusal of a SignedAndEnvelopedData; for any other recipient of an EnvelopedData it names the index and the recipient's RecipientInfo alternative as RFC 5652 §6.2 names it (KeyAgreeRecipientInfo, KEKRecipientInfo, PasswordRecipientInfo; KEMRecipientInfo per RFC 9629; OtherRecipientInfo otherwise) — "PKCS#7 recipient at index 0 is a KeyAgreeRecipientInfo. Only KeyTransRecipientInfo (RSA) is supported."

Both responses below are complete: they carry every member the service returns for that case, including the null ones. Only certificatePEM is shortened, after its first base64 line.

Example response (complete — three signers):

{
  "detectedFormat": {
    "standard": "PKCS#7",
    "structure": "SignedData",
    "contentTypeOID": "1.2.840.113549.1.7.2",
    "encoding": "BER",
    "payloadEmbedded": true
  },
  "signers": [
    {"index": 0, "issuerDN": "CN=Legacy Audit Signer,O=Example Corp,C=US", "serialNumber": "0x7E6D5C4B3A291807", "signatureAlgorithmOID": "1.2.840.10045.4.3.2", "signatureAlgorithm": "SHA256withECDSA", "digestAlgorithmOID": "2.16.840.1.101.3.4.2.1", "digestAlgorithm": "SHA-256", "certificatePresent": true, "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIIBizCCATKgAwIBAgIIfm1cSzopGAcwCgYIKoZIzj0EAwIwQjELMAkGA1UEBhMC\n...\n-----END CERTIFICATE-----", "certificateChainLength": 1, "keyUsage": ["digitalSignature", "nonRepudiation"], "timestampPresent": false, "timestampTSA": null, "timestampGenTime": null},
    {"index": 1, "issuerDN": "CN=Legacy Finance Signer,O=Example Corp,C=US", "serialNumber": "0x4A3F2E1D5B6C7A8B", "signatureAlgorithmOID": "1.2.840.113549.1.1.11", "signatureAlgorithm": "SHA256withRSA", "digestAlgorithmOID": "2.16.840.1.101.3.4.2.1", "digestAlgorithm": "SHA-256", "certificatePresent": true, "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIISj8uHVtseoswDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE\n...\n-----END CERTIFICATE-----", "certificateChainLength": 1, "keyUsage": ["digitalSignature", "nonRepudiation"], "timestampPresent": false, "timestampTSA": null, "timestampGenTime": null},
    {"index": 2, "issuerDN": "CN=Legacy Legal Signer,O=Example Corp,C=US", "serialNumber": "0x1B2C3D4E5F607182", "signatureAlgorithmOID": "1.2.840.113549.1.1.10", "signatureAlgorithm": "RSASSA-PSS", "digestAlgorithmOID": "2.16.840.1.101.3.4.2.1", "digestAlgorithm": "SHA-256", "certificatePresent": true, "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIIDGDCCAgCgAwIBAgIIGyw9Tl9gcYIwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UE\n...\n-----END CERTIFICATE-----", "certificateChainLength": 1, "keyUsage": ["digitalSignature", "nonRepudiation"], "timestampPresent": false, "timestampTSA": null, "timestampGenTime": null}
  ],
  "recipients": [],
  "supportStatus": { "supported": true, "reasons": [] },
  "recommendations": {
    "migrationPath": "CONVERT_TO_JWS_JSON",
    "requiredKeys": [
      {"purpose": "signing", "issuerDN": "CN=Legacy Audit Signer,O=Example Corp,C=US", "serialNumber": "0x7E6D5C4B3A291807", "algorithm": "SHA256withECDSA", "importHint": "Use POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore to import the private key and certificate of this signer"},
      {"purpose": "signing", "issuerDN": "CN=Legacy Finance Signer,O=Example Corp,C=US", "serialNumber": "0x4A3F2E1D5B6C7A8B", "algorithm": "SHA256withRSA", "importHint": "Use POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore to import the private key and certificate of this signer"},
      {"purpose": "signing", "issuerDN": "CN=Legacy Legal Signer,O=Example Corp,C=US", "serialNumber": "0x1B2C3D4E5F607182", "algorithm": "RSASSA-PSS", "importHint": "Use POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore to import the private key and certificate of this signer"}
    ],
    "estimatedComplexity": "HIGH"
  },
  "limits": { "maxActors": 16 }
}

Example response (complete — EnvelopedData, two recipients):

{
  "detectedFormat": {
    "standard": "PKCS#7",
    "structure": "EnvelopedData",
    "contentTypeOID": "1.2.840.113549.1.7.3",
    "encoding": "BER",
    "payloadEmbedded": true
  },
  "signers": [],
  "recipients": [
    {"index": 0, "issuerDN": "CN=Records Office,O=Example Corp,C=US", "serialNumber": "0x5A6B7C8D9E0F1021", "keyEncryptionAlgorithmOID": "1.2.840.113549.1.1.1", "keyEncryptionAlgorithm": "RSA", "certificatePresent": false, "certificatePEM": null},
    {"index": 1, "issuerDN": "CN=Treasury Archive,O=Example Corp,C=US", "serialNumber": "0x2F4E6D8C0B1A3928", "keyEncryptionAlgorithmOID": "1.2.840.113549.1.1.1", "keyEncryptionAlgorithm": "RSA", "certificatePresent": false, "certificatePEM": null}
  ],
  "encryption": { "contentEncryptionAlgorithmOID": "2.16.840.1.101.3.4.1.42", "contentEncryptionAlgorithm": "AES-256-CBC", "ivLength": 16 },
  "supportStatus": { "supported": true, "reasons": [] },
  "recommendations": {
    "migrationPath": "CONVERT_TO_JWE_JSON",
    "requiredKeys": [
      {"purpose": "encryption", "issuerDN": "CN=Records Office,O=Example Corp,C=US", "serialNumber": "0x5A6B7C8D9E0F1021", "algorithm": "RSA", "importHint": "Use POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore to import the private key and certificate of this recipient (a certificate imported alone only verifies and is not admissible as a recipient)"},
      {"purpose": "encryption", "issuerDN": "CN=Treasury Archive,O=Example Corp,C=US", "serialNumber": "0x2F4E6D8C0B1A3928", "algorithm": "RSA", "importHint": "Use POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore to import the private key and certificate of this recipient (a certificate imported alone only verifies and is not admissible as a recipient)"}
    ],
    "decryptionRequirement": "ONE_RECIPIENT_PRIVATE_KEY",
    "estimatedComplexity": "HIGH"
  },
  "limits": { "maxActors": 16 }
}

See: PKCS#7 Conversion for complete workflow and conversion process.



Version: 3.0.0

Last Updated: 2025-12-24

Status: Production Ready