hyperledger/fabric · error
node identity certificate %s is not a valid PEM
Error message
node identity certificate %s is not a valid PEM
What it means
CompareCertPublicKeys decodes the first certificate (cert1) as PEM to extract its public key. If pem.Decode yields no block, cert1 is not valid PEM and the error reports the raw string content of the input for diagnosis.
Source
Thrown at orderer/common/cluster/util.go:845
binary.LittleEndian.PutUint64(b, uint64(t.Seconds))
return b
}
// ExtractPublicKeyFromCert extracts the public key from an X.509 certificate
func ExtractPublicKeyFromCert(der []byte) ([]byte, error) {
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, errors.Wrap(err, "failed to parse certificate")
}
return x509.MarshalPKIXPublicKey(cert.PublicKey)
}
func CompareCertPublicKeys(cert1, cert2 []byte) (bool, error) {
// Extract public key using the same approach as IsConsenterOfChannel
bl, _ := pem.Decode(cert1)
if bl == nil {
return false, errors.Errorf("node identity certificate %s is not a valid PEM", string(cert1))
}
publicKey1, err := ExtractPublicKeyFromCert(bl.Bytes)
if err != nil {
return false, err
}
bl, _ = pem.Decode(cert2)
if bl == nil {
return false, errors.Errorf("node identity certificate %s is not a valid PEM", string(cert2))
}
publicKey2, err := ExtractPublicKeyFromCert(bl.Bytes)
if err != nil {
return false, err
}
return bytes.Equal(publicKey1, publicKey2), nilView on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure cert1 is PEM-encoded (-----BEGIN CERTIFICATE----- included)
- If you hold DER bytes, wrap them: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
- Check that the right variable/file is bound to cert1 (the error message echoes the raw input, inspect it)
- Both arguments must use the same encoding — normalize both before comparing
Example fix
// before
same, err := CompareCertPublicKeys(derCert1, pemCert2) // derCert1 not PEM
// after
pem1 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derCert1})
same, err := CompareCertPublicKeys(pem1, pemCert2) Defensive patterns
Strategy: validation
Validate before calling
if blk, _ := pem.Decode(cert1); blk == nil {
return errors.New("cert1 must be a PEM-encoded certificate")
} Type guard
func isPEM(data []byte) bool {
blk, _ := pem.Decode(data)
return blk != nil
} Try / catch
same, err := cluster.CompareCertPublicKeys(cert1, cert2)
if err != nil && strings.Contains(err.Error(), "not a valid PEM") {
return fmt.Errorf("normalize both certs to PEM before comparing: %w", err)
} Prevention
- Normalize both certificate inputs to PEM before comparison
- Never pass file paths or DER bytes where PEM is expected
- Check error message echo of raw input to spot wrong-variable bugs
- Store all node identity certs in PEM format consistently
When it happens
Trigger: Calling CompareCertPublicKeys with cert1 as raw DER bytes, an empty byte slice, a path string, or text with the PEM armor stripped — the first argument specifically.
Common situations: Mixing DER and PEM inputs for the two arguments; loading identity material from a source that strips headers; passing the wrong variable (e.g. node ID string) as cert1.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- enrollment certificate isn't a valid PEM block
- enrollment certificate should be a certificate, got a %s ins
- failed to add ca-file PEM to cert pool
- failed to PEM decode identity bytes: %s
- %s: wrong PEM encoding
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/21c214cac7fafba3.
Report an issue: GitHub.