hashicorp/packer · error

unsupported private key data

Error message

unsupported private key data

What it means

This error comes from loadPEMPrivateKeyAsPublic, which is a fallback path used when loading a verifier: the supplied PEM block failed to parse as a PKIX public key, a certificate, PKCS#8, PKCS#1, or SEC1/EC private key. The library only supports RSA (PKCS#1/PKCS#8) and EC (SEC1/PKCS#8) private keys here, so any other DER payload — malformed bytes, an Ed25519 PKCS#8 body that fails to decode, a DSA key, or a completely different PEM type (e.g. a CSR or encrypted key) — falls through to this error.

Source

Thrown at internal/attestation/sign_key.go:210

func loadPEMPrivateKeyAsPublic(contents []byte) (crypto.PublicKey, []byte, error) {
	block, _ := pem.Decode(contents)
	if block == nil {
		return nil, nil, fmt.Errorf("no PEM block found")
	}

	var signer crypto.Signer
	if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
		var ok bool
		signer, ok = key.(crypto.Signer)
		if !ok {
			return nil, nil, fmt.Errorf("private key does not implement crypto.Signer")
		}
	} else if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		signer = key
	} else if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
		signer = key
	} else {
		return nil, nil, fmt.Errorf("unsupported private key data")
	}

	publicKeyPEM, err := marshalPublicKeyPEM(signer.Public())
	if err != nil {
		return nil, nil, err
	}

	publicKey, _, err := loadPEMPublicKey(publicKeyPEM)
	if err != nil {
		return nil, nil, err
	}

	return publicKey, publicKeyPEM, nil
}

func marshalPublicKeyPEM(publicKey crypto.PublicKey) ([]byte, error) {
	encoded, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Convert the key to a supported format: for RSA use PKCS#1 or PKCS#8 PEM ('openssl rsa -in key.pem -traditional' or 'openssl pkcs8 -topk8 -nocrypt'), for EC use SEC1 or PKCS#8 ('openssl ec -in key.pem').
  2. If using an Ed25519 key from OpenSSH, convert it: 'ssh-keygen -p -m PKCS8 -f id_ed25519' so it parses as PKCS#8.
  3. If the file is meant to be a public key, export it as SubjectPublicKeyInfo: 'openssl pkey -pubin' / 'openssl pkey -in key.pem -pubout'.
  4. Decrypt the key first if it is passphrase-protected (encrypted PEM blocks never parse); supply the key unencrypted to the verifier path.
  5. Verify the file is not truncated or corrupted by checking it with 'openssl pkey -in file.pem -noout -check'.

Example fix

// before: verifier pointed at OpenSSH-format private key
// -----BEGIN OPENSSH PRIVATE KEY----- ...
// after: convert once, then reference the PKCS#8 file
//   ssh-keygen -p -m PKCS8 -f ~/.ssh/id_ed25519
// verifier_pem = "~/.ssh/id_ed25519"  // now parses via x509.ParsePKCS8PrivateKey
Defensive patterns

Strategy: validation

Validate before calling

// pre-check before handing PEM to LoadPEMVerifier
func pemKeySupported(path string) error {
	data, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	block, _ := pem.Decode(data)
	if block == nil {
		return fmt.Errorf("no PEM block")
	}
	switch {
	case x509.IsEncryptedPEMBlock(block): //nolint:staticcheck
		return fmt.Errorf("encrypted PEM not supported")
	}
	if _, err := x509.ParsePKIXPublicKey(block.Bytes); err == nil {
		return nil
	}
	if _, err := x509.ParseCertificate(block.Bytes); err == nil {
		return nil
	}
	if _, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
		return nil
	}
	if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		return nil
	}
	if _, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
		return nil
	}
	return fmt.Errorf("%s: unsupported PEM type %q; use PKCS#8/PKCS#1/SEC1 key or SPKI public key", path, block.Type)
}

Type guard

// narrow parsed PKCS#8 content to a supported signer key
func isSupportedSignerKey(key any) bool {
	switch key.(type) {
	case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
		return true
	default:
		return false
	}
}

Try / catch

verifier, err := attestation.LoadPEMVerifier(keyPath)
if err != nil {
	var unsupported = strings.Contains(err.Error(), "unsupported")
	switch {
	case unsupported:
		return fmt.Errorf("verifier key %s: convert to PKCS#8 PEM ('openssl pkcs8 -topk8 -nocrypt'); got: %w", keyPath, err)
	default:
		return fmt.Errorf("load verifier: %w", err)
	}
}

Prevention

When it happens

Trigger: Calling LoadPEMVerifier(path) or LoadPEMVerifierBytes(contents) with a PEM file whose block.Bytes does not parse as PKIX public key, x509 certificate, PKCS#8 private key, PKCS#1 RSA private key, or EC private key. Reached only via the loadPEMPublicKey fallback chain, so it is wrapped as "load verifier %q: ..." or surfaces after the earlier parse attempts fail.

Common situations: Pointing the verifier at a full signing key in an unusual format (e.g. OpenSSH "OPENSSH PRIVATE KEY" format instead of PKCS#8/PEM), passing a certificate request or encrypted ("ENCRYPTED PRIVATE KEY") PEM, a truncated or corrupt key file, or a DSA key. Note PKCS#8 keys that decode but do not implement crypto.Signer produce a different message, so this specific error means even DER-level parsing failed.

Related errors


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/ce9109e30744373a. Report an issue: GitHub.