{"record":{"id":"75f0e853cb48010d","repo":"github/github-mcp-server","slug":"private-key-is-t-want-an-rsa-key","errorCode":null,"errorMessage":"private key is %T, want an RSA key","messagePattern":"private key is %T, want an RSA key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/githubapp/githubapp.go","lineNumber":78,"sourceCode":"\t}\n\treturn nil\n}\n\nfunc parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode(pemBytes)\n\tif block == nil {\n\t\treturn nil, errors.New(\"no PEM block found in private key\")\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(\"parsing private key (want PKCS#1 or PKCS#8 RSA): %w\", err)\n\t}\n\tkey, ok := parsed.(*rsa.PrivateKey)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"private key is %T, want an RSA key\", parsed)\n\t}\n\treturn key, nil\n}\n\nfunc mintJWT(appID string, privateKey *rsa.PrivateKey, now time.Time) (string, error) {\n\theader := map[string]string{\"alg\": \"RS256\", \"typ\": \"JWT\"}\n\tclaims := map[string]any{\n\t\t\"iat\": now.Add(-clockSkew).Unix(),\n\t\t\"exp\": now.Add(jwtLifetime).Unix(),\n\t\t\"iss\": appID,\n\t}\n\n\theaderJSON, err := json.Marshal(header)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"encoding JWT header: %w\", err)\n\t}\n\tclaimsJSON, err := json.Marshal(claims)\n\tif err != nil {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/githubapp/githubapp.go#L60-L96","documentation":"parsePrivateKey successfully decoded a PEM block and parsed it as a PKCS#8 key, but the parsed key is not RSA (the %T verb prints the concrete Go type, e.g. *ecdsa.PrivateKey or ed25519.PrivateKey). The JWT in mintJWT is signed with RS256 via rsa.SignPKCS1v15, which requires an *rsa.PrivateKey, so any other algorithm is rejected at load time. GitHub Apps issue RSA PEM files, so a non-RSA key almost always means a self-generated key of the wrong type.","triggerScenarios":"NewProvider is called with Config.PrivateKeyPEM containing an ECDSA P-256 key generated via 'openssl ecparam -genkey', an Ed25519 key from 'ssh-keygen -t ed25519' converted to PEM, or an openssl 'PRIVATE KEY' block from 'openssl genpkey -algorithm ED25519'. PKCS#1 parse fails, PKCS#8 parse succeeds, the type assertion parsed.(*rsa.PrivateKey) at internal/githubapp/githubapp.go:76 fails, and the %T in the message reports e.g. '*ecdsa.PrivateKey'.","commonSituations":"A developer generates their own keypair instead of downloading the .pem from the GitHub App settings page; a CI pipeline converts the GitHub-issued key and re-wraps it with the wrong algorithm; an org rotates keys using a generic 'openssl genpkey' template that defaults to a non-RSA algorithm.","solutions":["Download the private key .pem from the GitHub App's settings page (GitHub generates an RSA key) and point GITHUB_APP_PRIVATE_KEY_PATH at that file","If a self-generated key must be used, generate an RSA one: openssl genrsa -out app.pem 2048 (PKCS#1) or openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 (PKCS#8)","Verify the key type before shipping: openssl pkey -in app.pem -noout -text should print an RSA PRIVATE KEY / 'Public-Key: (2048 bit)'","Check that a PEM mistransformation did not occur (e.g. a JSON/JWK export re-encoded as EC) and re-export the original GitHub-issued PEM"],"exampleFix":"// before: self-generated Ed25519 key\n//   openssl genpkey -algorithm ED25519 -out app.pem\n//   -> error: private key is ed25519.PrivateKey, want an RSA key\n\n// after: GitHub-issued RSA key, or generated as RSA\n//   download {base}/settings/apps/{app}/private_key (RSA .pem)\n//   or: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out app.pem","handlingStrategy":"validation","validationCode":"func isRSAPEM(pemBytes []byte) bool {\n    block, _ := pem.Decode(pemBytes)\n    if block == nil {\n        return false\n    }\n    if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {\n        return true\n    }\n    k, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n    return err == nil && k != nil && strings.Contains(fmt.Sprintf(\"%T\", k), \"rsa\")\n}","typeGuard":"func asRSAKey(anyKey any) (*rsa.PrivateKey, bool) {\n    k, ok := anyKey.(*rsa.PrivateKey)\n    return k, ok\n}","tryCatchPattern":"if _, err := githubapp.NewProvider(cfg, logger); err != nil {\n    if strings.Contains(err.Error(), \"want an RSA key\") {\n        // key algorithm wrong: fetch the GitHub-issued .pem\n    }\n}","preventionTips":["Always use the .pem downloaded from the GitHub App settings page; never generate your own key for a GitHub App","Add a CI check that runs openssl pkey -in app.pem -noout -text and greps for RSA before deploying","Validate the key with x509 parsing in a startup probe so the process fails fast instead of at first token refresh"],"tags":["crypto","rsa","github-app","configuration","startup"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}