hashicorp/nomad · error
private key is not a valid format
Error message
private key is not a valid format
What it means
ParseSigner decoded a PEM PRIVATE KEY block and x509.ParsePKCS8PrivateKey succeeded, but the resulting key does not implement crypto.Signer (e.g. it parsed to an *encryptedPKCS8Container-like or non-key type such as x509 Certificate data mislabeled, or PKCS8 payload holding an unusable type). The key is syntactically valid PKCS#8 but not a signable private key.
Source
Thrown at helper/tlsutil/generate.go:335
if block == nil {
return nil, fmt.Errorf("no PEM-encoded data found")
}
switch block.Type {
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(block.Bytes)
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(block.Bytes)
case "PRIVATE KEY":
signer, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
pk, ok := signer.(crypto.Signer)
if !ok {
return nil, fmt.Errorf("private key is not a valid format")
}
return pk, nil
default:
return nil, fmt.Errorf("unknown PEM block type for signing key: %s", block.Type)
}
}
func Verify(caString, certString, dns string) error {
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(caString))
if !ok {
return fmt.Errorf("failed to parse root certificate")
}
cert, err := parseCert(certString)
if err != nil {View on GitHub (pinned to 482b49bf1a)
Solutions
- Regenerate the private key with a supported algorithm (ECDSA P-256 or RSA) using tlsutil.GeneratePrivateKey or standard tooling.
- Verify each PEM block's contents match its declared type; do not relabel blocks.
- Convert the key to a supported format: openssl pkcs8 -topk8 -nocrypt with EC/RSA, then retry ParseSigner.
- If a public key or cert was pasted by mistake, supply the actual private key.
Example fix
// before // certPEM bytes pasted under "-----BEGIN PRIVATE KEY-----" header signer, err := tlsutil.ParseSigner(mislabeledPEM) // after signer, err := tlsutil.ParseSigner(realKeyPEM) // header matches EC/RSA PRIVATE KEY contents
Defensive patterns
Strategy: type-guard
Validate before calling
func signerYieldsSigner(s string) error {
block, _ := pem.Decode([]byte(s))
if block == nil || block.Type != "PRIVATE KEY" { return nil }
k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil { return err }
if _, ok := k.(crypto.Signer); !ok {
return fmt.Errorf("PKCS8 payload %T is not a crypto.Signer", k)
}
return nil
} Type guard
func isUsablePKCS8Signer(block *pem.Block) bool {
if block == nil || block.Type != "PRIVATE KEY" { return false }
k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
return err == nil
}
// plus, after parse: _, ok := k.(crypto.Signer) Try / catch
signer, err := tlsutil.ParseSigner(keyPEM)
if err != nil {
if strings.Contains(err.Error(), "private key is not a valid format") {
return fmt.Errorf("PKCS8 key payload is not signable; regenerate as ECDSA/RSA: %w", err)
}
return err
} Prevention
- Regenerate keys with standard ECDSA P-256 or RSA; avoid exotic PKCS#8 algorithms.
- Never relabel PEM headers; keep block bytes and type declarations consistent.
- Round-trip keys through openssl to verify format before storing.
- After ParsePKCS8PrivateKey in custom code, always assert crypto.Signer.
When it happens
Trigger: A PKCS#8 PRIVATE KEY block whose DER payload decodes to a type not implementing crypto.Signer — typically because the block actually contains a certificate or public key mislabeled as PRIVATE KEY, or an exotic/unsupported PKCS8 algorithm payload.
Common situations: Mislabeled PEM blocks (certificate bytes under a PRIVATE KEY header); keys exported by tooling with unsupported PKCS#8 algorithm identifiers; hand-edited PEM bundles where block contents and types no longer match.
Related errors
- Failed to load cert/key pair: %v
- unknown PEM block type for signing key: %s
- no PEM-encoded data found
- common name value not provided
- country value not provided
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/70cb1705924d934a.
Report an issue: GitHub.