{"record":{"id":"30bbf8e40af3287b","repo":"JuliusBrussee/caveman","slug":"githubapp-private-key-is-neither-pkcs-1-nor-pkcs","errorCode":null,"errorMessage":"githubapp: private key is neither PKCS#1 nor PKCS#8 RSA: %w","messagePattern":"githubapp: private key is neither PKCS#1 nor PKCS#8 RSA: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/githubapp/githubapp.go","lineNumber":407,"sourceCode":"\t\treturn resp.StatusCode, nil, fmt.Errorf(\"githubapp: read response: %w\", err)\n\t}\n\treturn resp.StatusCode, raw, nil\n}\n\n// parseRSAPrivateKey accepts a PKCS#1 (\"RSA PRIVATE KEY\") or PKCS#8\n// (\"PRIVATE KEY\") PEM — GitHub Apps download PKCS#1, but Cloud KMS / openssl\n// conversions emit PKCS#8, so we accept both.\nfunc parseRSAPrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode(pemBytes)\n\tif block == nil {\n\t\treturn nil, fmt.Errorf(\"githubapp: private key is not valid PEM\")\n\t}\n\tif key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {\n\t\treturn key, nil\n\t}\n\tparsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"githubapp: private key is neither PKCS#1 nor PKCS#8 RSA: %w\", err)\n\t}\n\tkey, ok := parsed.(*rsa.PrivateKey)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"githubapp: private key is not RSA\")\n\t}\n\treturn key, nil\n}\n\n// snippet trims an error body so we never echo a large/secret-bearing response.\nfunc snippet(b []byte) string {\n\tconst max = 256\n\ts := strings.TrimSpace(string(b))\n\tif len(s) > max {\n\t\treturn s[:max] + \"…\"\n\t}\n\treturn s\n}\n","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/githubapp/githubapp.go#L389-L425","documentation":"A PEM block was found, but neither x509.ParsePKCS1PrivateKey nor x509.ParsePKCS8PrivateKey could parse its DER payload. The key is PEM-armored yet its inner bytes are corrupt, truncated, or not a private key (e.g. a PUBLIC KEY or CERTIFICATE block).","triggerScenarios":"New() given a PEM whose block bytes are corrupted or truncated (file cut off mid-copy), or a PEM containing a PUBLIC KEY / CERTIFICATE block rather than a private key.","commonSituations":"Manually editing the .pem and dropping characters; pasting the key through a tool that mangled line breaks or whitespace; accidentally using the app's public key or webhook cert; a bad self-converted export.","solutions":["Re-download the private key from GitHub App settings and use it unmodified.","Validate locally: openssl rsa -in key.pem -check (or openssl pkey -in key.pem -text -noout); if OpenSSL rejects it, the file is corrupt.","Confirm the header says RSA PRIVATE KEY or PRIVATE KEY, not PUBLIC KEY or CERTIFICATE.","Regenerate via openssl genrsa / openssl pkcs8 -topk8 if you converted the key yourself."],"exampleFix":"// before\npem := \"-----BEGIN PUBLIC KEY-----\\n...\" // wrong block type\nclient, err := githubapp.New(pem)\n// after\npem := \"-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----\"\nclient, err := githubapp.New(pem)","handlingStrategy":"validation","validationCode":"func isParsableRSAPem(s string) bool {\n\tblock, _ := pem.Decode([]byte(s))\n\tif block == nil {\n\t\treturn false\n\t}\n\tif _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {\n\t\treturn true\n\t}\n\t_, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\treturn err == nil\n}","typeGuard":"func isParsableRSAPem(s string) bool {\n\tblock, _ := pem.Decode([]byte(s))\n\tif block == nil {\n\t\treturn false\n\t}\n\t_, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\treturn err == nil\n}","tryCatchPattern":"client, err := githubapp.New(pem)\nif err != nil {\n\tif strings.Contains(err.Error(), \"neither PKCS#1 nor PKCS#8\") {\n\t\treturn fmt.Errorf(\"private key payload corrupt or wrong type: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Never hand-edit PEM files; re-download from GitHub App settings if in doubt.","Verify with `openssl rsa -check -noout -in key.pem` before deploying.","Ensure file transfers preserve content (no truncation or CRLF mangling).","Confirm the block is a PRIVATE KEY, not PUBLIC KEY or CERTIFICATE."],"tags":["github-app","pem","pkcs8","x509"],"backgroundTag":"invalid-argument-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}