kubernetes/kops · error

could not parse private key

Error message

could not parse private key

What it means

parsePEMPublicKey iterates pem.Decode over the input; when pem.Decode returns nil block it means the input contained no valid PEM-formatted block at all, so the function gives up. Despite the wording, it is raised for ANY key material (public or private) that is not PEM-encoded. kOps uses this when parsing key data for cluster PKI operations.

Source

Thrown at pkg/pki/publickey.go:47

	k, err := parsePEMPublicKey(data)
	if err != nil {
		return nil, err
	}
	if k == nil {
		return nil, nil
	}
	return &PublicKey{Key: k}, nil
}

type PublicKey struct {
	Key crypto.PublicKey
}

func parsePEMPublicKey(pemData []byte) (crypto.PublicKey, error) {
	for {
		block, rest := pem.Decode(pemData)
		if block == nil {
			return nil, fmt.Errorf("could not parse private key")
		}

		switch block.Type {
		case "RSA PUBLIC KEY":
			k, err := x509.ParsePKCS1PublicKey(block.Bytes)
			if err != nil {
				return nil, err
			}
			return k, nil
		case "PUBLIC KEY":
			k, err := x509.ParsePKIXPublicKey(block.Bytes)
			if err != nil {
				return nil, err
			}
			return k.(crypto.PublicKey), nil
		default:
			klog.Infof("Ignoring unexpected PEM block: %q", block.Type)
		}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Verify the input includes full PEM armor ('-----BEGIN ... KEY-----' through '-----END ... KEY-----')
  2. If you only have raw base64/DER key bytes, wrap them in PEM headers with pem.EncodeToMemory before calling ParsePEMPublicKey
  3. Check for truncated data at the source (config file, secret store) and re-export the key

Example fix

// before
k, err := pki.ParsePEMPublicKey(base64KeyBytes)
// after
pemData := pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: derBytes})
k, err := pki.ParsePEMPublicKey(pemData)
Defensive patterns

Strategy: validation

Validate before calling

func isPEM(data []byte) bool {
	block, _ := pem.Decode(data)
	return block != nil
}
if !isPEM(keyData) {
	return fmt.Errorf("input is not PEM-encoded; missing BEGIN/END headers")
}
key, err := pki.ParsePEMPublicKey(keyData)

Type guard

func hasPEMHeader(s string) bool {
	return strings.HasPrefix(strings.TrimSpace(s), "-----BEGIN ")
}

Prevention

When it happens

Trigger: Calling ParsePEMPublicKey with raw base64 key bytes, a DER-encoded key, a key with the BEGIN/END headers stripped, or an empty/whitespace-only string; also a corrupted file where the '-----BEGIN' header line is truncated.

Common situations: Storing keys in config without the PEM armor headers, copy-paste errors losing the first line, base64-decoding the key before passing it in, or reading a truncated secret from a YAML/etcd store.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/0db93f892297d982. Report an issue: GitHub.