hyperledger/fabric · error

failed to decode PEM block from %s

Error message

failed to decode PEM block from %s

What it means

loadPrivateKey reads the key file and calls pem.Decode; if the file content is not a valid PEM block (nil result), it throws this error including the file path. The Signer requires the private key in PEM form to parse it via parsePrivateKey.

Source

Thrown at cmd/common/signer/signer.go:116

	// Fabric only supports ECDSA and ed25519 at the moment.
	case *ecdsa.PrivateKey:
		digest := util.ComputeSHA256(msg)
		return signECDSA(si.key.(*ecdsa.PrivateKey), digest)
	case ed25519.PrivateKey:
		return ed25519.Sign(si.key.(ed25519.PrivateKey), msg), nil
	default:
		return nil, errors.Errorf("found unknown private key type (%T) in msg signing", key)
	}
}

func loadPrivateKey(file string) (crypto.PrivateKey, error) {
	b, err := os.ReadFile(file)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	bl, _ := pem.Decode(b)
	if bl == nil {
		return nil, errors.Errorf("failed to decode PEM block from %s", file)
	}
	key, err := parsePrivateKey(bl.Bytes)
	if err != nil {
		return nil, err
	}
	return key, nil
}

// Based on crypto/tls/tls.go but modified for Fabric:
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	// OpenSSL 1.0.0 generates PKCS#8 keys.
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		// Fabric only supports ECDSA at the moment.
		case *ecdsa.PrivateKey:
			return key, nil
		case ed25519.PrivateKey:
			return key, nil

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Convert the key to PEM: openssl pkey -inform DER -in key.der -out key.pem (or openssl ec -in key.sec1.der ... )
  2. Verify the file contains '-----BEGIN ... PRIVATE KEY-----' at the top
  3. Fix SignerConfig.KeyPath in config to point at the actual PEM private key
  4. Re-download/re-export the key from the MSP directory

Example fix

// before
openssl pkcs8 -topk8 -inform DER -in key.der -out key.pem -nocrypt
// after: config signer.key: /path/to/key.pem (PEM-encoded)
Defensive patterns

Strategy: validation

Validate before calling

b, _ := os.ReadFile(keyPath)
if blk, _ := pem.Decode(b); blk == nil {
    return fmt.Errorf("%s is not PEM-encoded", keyPath)
}

Type guard

func isPEMPrivateKey(b []byte) bool {
    blk, _ := pem.Decode(b)
    return blk != nil && strings.Contains(blk.Type, "PRIVATE KEY")
}

Try / catch

s, err := signer.NewSigner(keyPath, idPath)
if err != nil {
    if strings.Contains(err.Error(), "failed to decode PEM block") {
        return fmt.Errorf("key %s not PEM; convert: openssl pkey -inform DER", keyPath)
    }
    return err
}

Prevention

When it happens

Trigger: NewSigner with a key file that is raw DER, empty, an error page, base64 without headers, or corrupted/truncated PEM; wrong path passed as SignerConfig.KeyPath so the file read succeeds but content isn't PEM.

Common situations: Key exported in DER (PKCS#8/SEC1 binary) form; key file truncated by failed download; pointing KeyPath at the certificate or some other non-PEM file; trailing whitespace-only file.

Understand the failure class

Related errors


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