github/github-mcp-server · error

private key is %T, want an RSA key

Error message

private key is %T, want an RSA key

What it means

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.

Source

Thrown at internal/githubapp/githubapp.go:78

	}
	return nil
}

func parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("no PEM block found in private key")
	}
	if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		return key, nil
	}
	parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("parsing private key (want PKCS#1 or PKCS#8 RSA): %w", err)
	}
	key, ok := parsed.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("private key is %T, want an RSA key", parsed)
	}
	return key, nil
}

func mintJWT(appID string, privateKey *rsa.PrivateKey, now time.Time) (string, error) {
	header := map[string]string{"alg": "RS256", "typ": "JWT"}
	claims := map[string]any{
		"iat": now.Add(-clockSkew).Unix(),
		"exp": now.Add(jwtLifetime).Unix(),
		"iss": appID,
	}

	headerJSON, err := json.Marshal(header)
	if err != nil {
		return "", fmt.Errorf("encoding JWT header: %w", err)
	}
	claimsJSON, err := json.Marshal(claims)
	if err != nil {

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. 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
  2. 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)
  3. Verify the key type before shipping: openssl pkey -in app.pem -noout -text should print an RSA PRIVATE KEY / 'Public-Key: (2048 bit)'
  4. 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

Example fix

// before: self-generated Ed25519 key
//   openssl genpkey -algorithm ED25519 -out app.pem
//   -> error: private key is ed25519.PrivateKey, want an RSA key

// after: GitHub-issued RSA key, or generated as RSA
//   download {base}/settings/apps/{app}/private_key (RSA .pem)
//   or: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out app.pem
Defensive patterns

Strategy: validation

Validate before calling

func isRSAPEM(pemBytes []byte) bool {
    block, _ := pem.Decode(pemBytes)
    if block == nil {
        return false
    }
    if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
        return true
    }
    k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
    return err == nil && k != nil && strings.Contains(fmt.Sprintf("%T", k), "rsa")
}

Type guard

func asRSAKey(anyKey any) (*rsa.PrivateKey, bool) {
    k, ok := anyKey.(*rsa.PrivateKey)
    return k, ok
}

Try / catch

if _, err := githubapp.NewProvider(cfg, logger); err != nil {
    if strings.Contains(err.Error(), "want an RSA key") {
        // key algorithm wrong: fetch the GitHub-issued .pem
    }
}

Prevention

When it happens

Trigger: 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'.

Common situations: 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.

Related errors


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/75f0e853cb48010d. Report an issue: GitHub.