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

  1. Regenerate the private key with a supported algorithm (ECDSA P-256 or RSA) using tlsutil.GeneratePrivateKey or standard tooling.
  2. Verify each PEM block's contents match its declared type; do not relabel blocks.
  3. Convert the key to a supported format: openssl pkcs8 -topk8 -nocrypt with EC/RSA, then retry ParseSigner.
  4. 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

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/70cb1705924d934a. Report an issue: GitHub.