{"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":"error","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/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/githubapp/githubapp.go#L389-L425","documentation":"Thrown by parseRSAPrivateKey (githubapp.go:407): the input was valid PEM, but its payload parsed as neither PKCS#1 RSA (x509.ParsePKCS1PrivateKey failed) nor PKCS#8 (x509.ParsePKCS8PrivateKey failed). The wrapped error is the PKCS#8 failure, which is usually the more informative of the two. So the block decoded fine; its DER content is some other format.","triggerScenarios":"A PEM block whose type is not a private key at all (e.g. -----BEGIN CERTIFICATE----- or -----BEGIN PUBLIC KEY----- passed to the key parser), an encrypted PKCS#8 key (PBES2 envelope the parser will not decrypt), or a corrupted body where headers survive but base64/DER is damaged.","commonSituations":"Passing the certificate file where the key file belongs (tls.crt vs tls.key swap); a key generated encrypted (openssl genrsa -aes256) whose PEM header says 'ENCRYPTED PRIVATE KEY'; hand-edited base64; a block truncated internally though the END line exists.","solutions":["Confirm the block header: it must be 'RSA PRIVATE KEY' (PKCS#1) or 'PRIVATE KEY' (PKCS#8), unencrypted.","If it says 'ENCRYPTED PRIVATE KEY', decrypt once: openssl pkcs8 -in enc.pem -out plain.pem -nocrypt (or re-generate unencrypted).","If a certificate was supplied by mistake, point the config at the private key file instead.","Regenerate the key pair and re-upload to the GitHub App if the body is corrupt."],"exampleFix":"# before: encrypted key (BEGIN ENCRYPTED PRIVATE KEY)\n\n# after: strip the passphrase\nopenssl pkcs8 -in encrypted.pem -nocrypt -out plain.pem\n# use plain.pem contents as the private key","handlingStrategy":"validation","validationCode":"block, _ := pem.Decode(keyBytes)\nif block == nil { return errors.New(\"not PEM\") }\nif _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {\n    if _, err8 := x509.ParsePKCS8PrivateKey(block.Bytes); err8 != nil {\n        return fmt.Errorf(\"key is neither PKCS#1 nor PKCS#8: %v/%v\", err, err8)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := parseKey(pemBytes); err != nil {\n    return fmt.Errorf(\"github app key rejected (check ENCRYPTED header / cert-vs-key swap): %w\", err)\n}","preventionTips":["Reject keys whose PEM header reads 'ENCRYPTED PRIVATE KEY' during provisioning.","Keep tls.crt and tls.key in clearly named files to prevent swaps.","Verify keys with openssl pkey -in key.pem -noout before deployment."],"tags":["go","github-app","pem","rsa","pkcs8"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}