{"record":{"id":"0db93f892297d982","repo":"kubernetes/kops","slug":"could-not-parse-private-key","errorCode":null,"errorMessage":"could not parse private key","messagePattern":"could not parse private key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pki/publickey.go","lineNumber":47,"sourceCode":"\tk, err := parsePEMPublicKey(data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif k == nil {\n\t\treturn nil, nil\n\t}\n\treturn &PublicKey{Key: k}, nil\n}\n\ntype PublicKey struct {\n\tKey crypto.PublicKey\n}\n\nfunc parsePEMPublicKey(pemData []byte) (crypto.PublicKey, error) {\n\tfor {\n\t\tblock, rest := pem.Decode(pemData)\n\t\tif block == nil {\n\t\t\treturn nil, fmt.Errorf(\"could not parse private key\")\n\t\t}\n\n\t\tswitch block.Type {\n\t\tcase \"RSA PUBLIC KEY\":\n\t\t\tk, err := x509.ParsePKCS1PublicKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn k, nil\n\t\tcase \"PUBLIC KEY\":\n\t\t\tk, err := x509.ParsePKIXPublicKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn k.(crypto.PublicKey), nil\n\t\tdefault:\n\t\t\tklog.Infof(\"Ignoring unexpected PEM block: %q\", block.Type)\n\t\t}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/pki/publickey.go#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the input includes full PEM armor ('-----BEGIN ... KEY-----' through '-----END ... KEY-----')","If you only have raw base64/DER key bytes, wrap them in PEM headers with pem.EncodeToMemory before calling ParsePEMPublicKey","Check for truncated data at the source (config file, secret store) and re-export the key"],"exampleFix":"// before\nk, err := pki.ParsePEMPublicKey(base64KeyBytes)\n// after\npemData := pem.EncodeToMemory(&pem.Block{Type: \"RSA PUBLIC KEY\", Bytes: derBytes})\nk, err := pki.ParsePEMPublicKey(pemData)","handlingStrategy":"validation","validationCode":"func isPEM(data []byte) bool {\n\tblock, _ := pem.Decode(data)\n\treturn block != nil\n}\nif !isPEM(keyData) {\n\treturn fmt.Errorf(\"input is not PEM-encoded; missing BEGIN/END headers\")\n}\nkey, err := pki.ParsePEMPublicKey(keyData)","typeGuard":"func hasPEMHeader(s string) bool {\n\treturn strings.HasPrefix(strings.TrimSpace(s), \"-----BEGIN \")\n}","tryCatchPattern":null,"preventionTips":["Always store keys with full PEM armor, never stripped headers","Never pre-base64-decode PEM data before passing it to kops PKI functions","Validate with `openssl pkey -pubin -in key.pem -noout` before use"],"tags":["pki","pem","key-parsing"],"backgroundTag":"pem-parse-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}