{"record":{"id":"a2b0515e7ceca333","repo":"kubernetes/kops","slug":"verifying-pkcs7-signature-w","errorCode":null,"errorMessage":"verifying PKCS7 signature: %w","messagePattern":"verifying PKCS7 signature: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"upup/pkg/fi/cloudup/azure/attest.go","lineNumber":252,"sourceCode":"\tif signature == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"empty PKCS7 signature\")\n\t}\n\n\tsigBytes, err := base64.StdEncoding.DecodeString(signature)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"decoding PKCS7 signature: %w\", err)\n\t}\n\tklog.V(4).Infof(\"Decoded PKCS7 signature (%d bytes)\", len(sigBytes))\n\n\tp7, err := pkcs7.Parse(sigBytes)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"parsing PKCS7 signature: %w\", err)\n\t}\n\tklog.V(8).Infof(\"Parsed PKCS7 structure with %d embedded certificate(s)\", len(p7.Certificates))\n\n\t// Verify the PKCS7 signature against the embedded leaf certificate.\n\tif err := p7.Verify(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"verifying PKCS7 signature: %w\", err)\n\t}\n\tklog.V(4).Infof(\"PKCS7 self-signature verified\")\n\n\tsigner := p7.GetOnlySigner()\n\tif signer == nil {\n\t\treturn nil, nil, fmt.Errorf(\"PKCS7 signer certificate not found\")\n\t}\n\tklog.V(8).Infof(\"PKCS7 signer certificate: subject=%q issuer=%q SANs=%v\", signer.Subject, signer.Issuer, signer.DNSNames)\n\tif err := validateAzureMetadataSignerSAN(signer); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"validating PKCS7 signer SAN: %w\", err)\n\t}\n\tklog.V(4).Infof(\"PKCS7 signer SAN validated as Azure metadata endpoint\")\n\n\treturn p7, signer, nil\n}\n\n// nonceForBody derives the IMDS attestation nonce from the request body; the shared\n// azuremetadata implementation keeps the authenticator and verifier sides identical.","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/azure/attest.go#L234-L270","documentation":"This wraps p7.Verify() failing after the PKCS#7 structure parsed successfully. Verify checks the message digest and the embedded certificates' signature chain against the trusted root; failure means the signature over the attested payload is cryptographically invalid or the embedded cert does not chain to the expected Azure root. It is returned by parseAndValidatePKCS7Signer to callers in verifyAttestedDocumentWithRootAndFetcher.","triggerScenarios":"p7.Verify() is invoked on a parsed PKCS7 blob whose SignedData digest does not match the message content, whose signer cert is expired, or whose certificate chain does not validate against the root passed to verifyAttestedDocumentWithRootAndFetcher.","commonSituations":"Clock skew making the embedded certificate appear expired/not-yet-valid; the attested document was tampered with or replayed from another VM; custom root/fetcher wired with the wrong intermediate (missing Azure intermediate certs); MITM proxy re-signing responses.","solutions":["Check VM system clock (chrony/ntpd) for skew; expired signer certs are the most common cause","Confirm the trusted root and any intermediates supplied to verifyAttestedDocumentWithRootAndFetcher match the current Azure IMDS signing hierarchy","Re-fetch the attested document directly from IMDS (169.254.169.254) bypassing proxies to rule out tampering/truncation","Compare signature verification on a known-good VM to isolate whether the certificate chain or the payload is at fault"],"exampleFix":"// before\nrootPool := x509.NewCertPool() // missing Azure intermediates\nif err := p7.Verify(); err != nil { return err } // \"verifying PKCS7 signature: x509: certificate signed by unknown authority\"\n// after\nrootPool := x509.NewCertPool()\nrootPool.AddCert(azureRoot)\nfor _, c := range azureIntermediates {\n    rootPool.AddCert(c)\n}\nif err := p7.Verify(); err != nil { return err }","handlingStrategy":"try-catch","validationCode":"// Pre-check: ensure the trust root/intermediates are loaded before calling verifyAttestedDocumentWithRootAndFetcher\nif rootPool == nil {\n    return fmt.Errorf(\"azure attestation root not initialized\")\n}","typeGuard":"func certChainLoaded(pool *x509.CertPool, roots ...*x509.Certificate) bool {\n    if pool == nil {\n        return false\n    }\n    for _, r := range roots {\n        if r == nil {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"if err := p7.Verify(); err != nil {\n    var certErr x509.CertificateInvalidError\n    if errors.As(err, &certErr) && errors.Is(certErr.Err, x509.Expired) {\n        klog.Warningf(\"PKCS7 signer cert expired at %v; check VM clock skew (now=%v)\", certErr.NotAfter, time.Now().UTC())\n    }\n    return nil, nil, fmt.Errorf(\"verifying PKCS7 signature: %w\", err)\n}","preventionTips":["Keep VM clocks synced (chrony/ntpd) — expired-cert failures are usually clock skew","Pin and refresh the Azure IMDS root/intermediate certificates as part of routine upgrades","Fetch attested documents only from 169.254.169.254 inside the VM, never via proxies","Treat verification failures as security-relevant; log full x509 chain details before failing"],"tags":["azure","pkcs7","signature-verification","x509","attestation","security"],"backgroundTag":"signature-verification-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}