slackhq/nebula · error
bytes did not contain a proper Ed25519/ECDSA private key ban
Error message
bytes did not contain a proper Ed25519/ECDSA private key banner
What it means
pem.Decode produced a block, but its Type matches none of the accepted signing private key banners: encrypted Ed25519, encrypted ECDSA P256, plain Ed25519, or plain ECDSA P256. The input is valid PEM but is not a nebula signing private key — commonly it is a host (X25519/P256) key, a public key, or a certificate.
Source
Thrown at cert/pem.go:274
}
var curve Curve
switch k.Type {
case EncryptedEd25519PrivateKeyBanner:
return nil, nil, Curve_CURVE25519, ErrPrivateKeyEncrypted
case EncryptedECDSAP256PrivateKeyBanner:
return nil, nil, Curve_P256, ErrPrivateKeyEncrypted
case Ed25519PrivateKeyBanner:
curve = Curve_CURVE25519
if len(k.Bytes) != ed25519.PrivateKeySize {
return nil, r, 0, fmt.Errorf("key was not %d bytes, is invalid Ed25519 private key", ed25519.PrivateKeySize)
}
case ECDSAP256PrivateKeyBanner:
curve = Curve_P256
if len(k.Bytes) != 32 {
return nil, r, 0, fmt.Errorf("key was not 32 bytes, is invalid ECDSA P256 private key")
}
default:
return nil, r, 0, fmt.Errorf("bytes did not contain a proper Ed25519/ECDSA private key banner")
}
return k.Bytes, r, curve, nil
}
View on GitHub (pinned to dd8f660c0a)
Solutions
- Use the CA signing key file (banner 'NEBULA ED25519 SIGNING PRIVATE KEY' or 'NEBULA ECDSA P256 SIGNING PRIVATE KEY')
- Regenerate with nebula-cert ca / keygen to get a correctly bannered signing key
- Double-check config: signing_key_path must not point at the host key or certificate
Example fix
// before signing_key_path: ./host.key // X25519 host key // after signing_key_path: ./ca.key // Ed25519/ECDSA P256 signing private key banner
Defensive patterns
Strategy: validation
Validate before calling
blk, _ := pem.Decode(data)
valid := map[string]bool{
"NEBULA ED25519 SIGNING PRIVATE KEY": true,
"NEBULA ECDSA P256 SIGNING PRIVATE KEY": true,
"NEBULA ED25519 ENCRYPTED SIGNING PRIVATE KEY": true,
"NEBULA ECDSA P256 ENCRYPTED SIGNING PRIVATE KEY": true,
}
if blk != nil && !valid[blk.Type] {
return fmt.Errorf("not a nebula signing private key, PEM type is %q", blk.Type)
} Type guard
func isSigningPrivateKey(b []byte) bool {
blk, _ := pem.Decode(b)
if blk == nil { return false }
switch blk.Type {
case "NEBULA ED25519 SIGNING PRIVATE KEY", "NEBULA ECDSA P256 SIGNING PRIVATE KEY":
return true
}
return false
} Try / catch
key, _, curve, err := nebula.UnmarshalSigningPrivateKeyFromPEM(raw)
if err != nil {
return fmt.Errorf("%s is not a signing private key — check signing_key_path: %w", path, err)
} Prevention
- Label key files explicitly (ca-signing.key vs host.key) and never swap them
- Validate all key PEM banners once during config load
- Generate signing keys only via nebula-cert ca
When it happens
Trigger: Call UnmarshalSigningPrivateKeyFromPEM with any PEM block whose k.Type is not one of EncryptedEd25519PrivateKeyBanner, EncryptedECDSAP256PrivateKeyBanner, Ed25519PrivateKeyBanner, ECDSAP256PrivateKeyBanner.
Common situations: Swapping signing_key_path with key/cert paths in nebula config, passing an openssl-generated 'EC PRIVATE KEY' or 'PRIVATE KEY' block, or feeding a certificate PEM to the key parser.
Related errors
- bytes did not contain a proper private key banner
- key was not %d bytes, is invalid Ed25519 private key
- key was not 32 bytes, is invalid ECDSA P256 private key
- ErrInvalidPEMX25519PrivateKeyBanner
- ErrInvalidPEMEd25519PublicKeyBanner
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/88408da2736c12c0.
Report an issue: GitHub.