router-for-me/CLIProxyAPI · error

private_key is not an RSA key

Error message

private_key is not an RSA key

What it means

From ensureRSAPrivateKey when a 'PRIVATE KEY' (PKCS#8) block parses successfully but the resulting key is not an *rsa.PrivateKey (keyutil.go:102-106). Vertex signing requires RSA; an ECDSA or Ed25519 key inside a PKCS#8 wrapper triggers this error.

Source

Thrown at internal/auth/vertex/keyutil.go:105

	if block == nil {
		return nil, fmt.Errorf("pem block is nil")
	}

	if block.Type == "RSA PRIVATE KEY" {
		if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
			return nil, fmt.Errorf("private_key invalid rsa: %w", err)
		}
		return block, nil
	}

	if block.Type == "PRIVATE KEY" {
		key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
		if err != nil {
			return nil, fmt.Errorf("private_key invalid pkcs8: %w", err)
		}
		rsaKey, ok := key.(*rsa.PrivateKey)
		if !ok {
			return nil, fmt.Errorf("private_key is not an RSA key")
		}
		der := x509.MarshalPKCS1PrivateKey(rsaKey)
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: der}, nil
	}

	// Attempt auto-detection: try PKCS#1 first, then PKCS#8.
	if rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		der := x509.MarshalPKCS1PrivateKey(rsaKey)
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: der}, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
		if rsaKey, ok := key.(*rsa.PrivateKey); ok {
			der := x509.MarshalPKCS1PrivateKey(rsaKey)
			return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: der}, nil
		}
	}
	return nil, fmt.Errorf("private_key uses unsupported format")
}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Regenerate the key as RSA of at least 2048 bits: openssl genrsa -out key.pem 2048, then rebuild the service account JSON
  2. Or use an official GCP-issued service account key, which is always RSA
  3. Verify the algorithm: openssl pkey -in key.pem -noout -text | head -1 (should say RSA)

Example fix

# before
openssl ecparam -name prime256v1 -genkey -out key.pem
# after
openssl genrsa -out key.pem 2048
Defensive patterns

Strategy: type-guard

Validate before calling

key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err == nil && !isRSA(key) {
    return fmt.Errorf("key algorithm is %T; RSA required", key)
}

Type guard

func isRSAKey(any) bool
// Go:
func isRSAKey(k any) bool {
    _, ok := k.(*rsa.PrivateKey)
    return ok
}

Prevention

When it happens

Trigger: User configures an EC (prime256v1) or Ed25519 service-account-style key as the Vertex credential; key generated with openssl ecparam or genkey -algorithm ed25519 instead of RSA; GCP always issues RSA, so this usually means a custom/homemade key.

Common situations: Organizations minting their own keys for private Vertex-compatible endpoints; converting existing EC TLS keys for signing experiments; misunderstanding that the field must be RSA.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/bc996cdac2996665. Report an issue: GitHub.