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), nil

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure cert1 is PEM-encoded (-----BEGIN CERTIFICATE----- included)
  2. If you hold DER bytes, wrap them: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
  3. Check that the right variable/file is bound to cert1 (the error message echoes the raw input, inspect it)
  4. 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

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

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/21c214cac7fafba3. Report an issue: GitHub.