{"record":{"id":"d9a588a45c4678a9","repo":"hyperledger/fabric","slug":"failed-unmarshaling-certificate","errorCode":null,"errorMessage":"failed unmarshaling certificate","messagePattern":"failed unmarshaling certificate","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/crypto/sanitize.go","lineNumber":70,"sourceCode":"\tr, s, err := utils.UnmarshalECDSASignature(cert.Signature)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed unmarshaling ECDSA signature on identity: %s\", string(initialPEM))\n\t}\n\n\t// We assume that the consenter and the CA use the same signature scheme.\n\tcurveOrderUsedByCryptoGen := cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().N\n\thalfOrder := new(big.Int).Rsh(curveOrderUsedByCryptoGen, 1)\n\t// Low S, nothing to do here!\n\tif s.Cmp(halfOrder) != 1 {\n\t\treturn initialPEM, nil\n\t}\n\t// Else it's high-S, so shift it below half the order.\n\ts.Sub(curveOrderUsedByCryptoGen, s)\n\n\tvar newCert certificate\n\t_, err = asn1.Unmarshal(cert.Raw, &newCert)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed unmarshaling certificate\")\n\t}\n\n\tnewSig, err := utils.MarshalECDSASignature(r, s)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed marshaling ECDSA signature\")\n\t}\n\tnewCert.SignatureValue = asn1.BitString{Bytes: newSig, BitLength: len(newSig) * 8}\n\n\tnewCert.Raw = nil\n\tnewRaw, err := asn1.Marshal(newCert)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed marshaling new certificate\")\n\t}\n\n\tfinalPEM := pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: newRaw})\n\treturn finalPEM, nil\n}\n","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/crypto/sanitize.go#L52-L88","documentation":"When a certificate has a high-S ECDSA signature, the sanitizer re-encodes the whole certificate with the normalized low-S signature. It does this by ASN.1-unmarshaling cert.Raw into a local certificate struct; if that fails, 'failed unmarshaling certificate' is wrapped and returned.","triggerScenarios":"A high-S certificate whose raw DER cannot be re-parsed by encoding/asn1 into the sanitizer's certificate struct — e.g. non-standard extensions/encodings that break strict ASN.1 parsing.","commonSituations":"Certificates from CAs emitting non-canonical DER (e.g. weird OPTIONAL fields, zero-length encodings); intermediate tooling re-encoding certificates non-strictly; very old/uncommon CA software.","solutions":["Re-issue the certificate so the CA signs it with low-S directly (fix CA's RFC6979/low-S configuration)","Compare the failing cert's DER with openssl asn1parse to find the non-standard encoding","Convert the cert to canonical DER before use (re-encode via Go's x509 or openssl)","Upgrade the fabric version, as asn1 handling of exotic encodings has improved over releases"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"func hasHighS(cert *x509.Certificate) (bool, error) {\n    r, s, err := utils.UnmarshalECDSASignature(cert.Signature)\n    if err != nil { return false, err }\n    half := new(big.Int).Rsh(cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().N, 1)\n    return s.Cmp(half) == 1, nil\n}","typeGuard":null,"tryCatchPattern":"out, err := crypto.SanitizeX509Cert(certPEM)\nif err != nil && strings.Contains(err.Error(), \"failed unmarshaling certificate\") {\n    // cert is high-S and non-canonical DER; re-issue from CA\n}","preventionTips":["Configure CAs to emit low-S signatures (RFC6979 deterministic k) so sanitization is a no-op","Check unusual certs with openssl asn1parse before deploying","Prefer recent fabric releases with robust ASN.1 handling"],"tags":["asn1","ecdsa","low-s","x509"],"backgroundTag":"asn1-unmarshal-failed","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}