router-for-me/CLIProxyAPI · error

client key is not rsa

Error message

client key is not rsa

What it means

Returned by parseRSAPrivateKeyPEM in internal/home/certificate.go when a PKCS#8 'PRIVATE KEY' PEM parses successfully but the underlying key is not an RSA key (e.g. ECDSA or Ed25519). The home mTLS flow builds an RSA-based CSR (createClientCSR takes *rsa.PrivateKey), so non-RSA keys are rejected.

Source

Thrown at internal/home/certificate.go:276

	return nil
}

func parseRSAPrivateKeyPEM(raw []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(raw)
	if block == nil {
		return nil, fmt.Errorf("client key pem is invalid")
	}
	switch block.Type {
	case "RSA PRIVATE KEY":
		return x509.ParsePKCS1PrivateKey(block.Bytes)
	case "PRIVATE KEY":
		key, errParse := x509.ParsePKCS8PrivateKey(block.Bytes)
		if errParse != nil {
			return nil, errParse
		}
		rsaKey, ok := key.(*rsa.PrivateKey)
		if !ok {
			return nil, fmt.Errorf("client key is not rsa")
		}
		return rsaKey, nil
	default:
		return nil, fmt.Errorf("client key pem type %q is unsupported", block.Type)
	}
}

func createClientCSR(certificateID string, key *rsa.PrivateKey) ([]byte, error) {
	certificateID = strings.TrimSpace(certificateID)
	if certificateID == "" {
		return nil, fmt.Errorf("certificate id is required")
	}
	template := &x509.CertificateRequest{
		Subject: pkix.Name{
			CommonName: certificateID,
		},
	}
	der, errCreate := x509.CreateCertificateRequest(rand.Reader, template, key)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Generate an RSA key: openssl genrsa -out client.key 2048 (or 3072/4096)
  2. Re-run the enrollment flow with the new RSA key so the CSR and issued cert match
  3. If you must keep EC keys, that is unsupported by this code path — switch to RSA

Example fix

# before
openssl genpkey -algorithm Ed25519 -out client.key

# after
openssl genrsa -out client.key 2048
Defensive patterns

Strategy: validation

Validate before calling

// after parsing, or preflight via openssl; in Go preflight the PEM type:
block, _ := pem.Decode(raw)
if block == nil || (block.Type != "RSA PRIVATE KEY" && block.Type != "PRIVATE KEY") {
    return errors.New("client key must be RSA PEM (PKCS1 or PKCS8)")
}
// note: PKCS8 non-RSA still fails at parse; generate with `openssl genrsa`

Prevention

When it happens

Trigger: Generating the client key with openssl ecparam -name ... or openssl genkey ed25519 or openssl genpkey -algorithm EC and pointing client-key at it; using a modern default-key-type tool that emits Ed25519.

Common situations: Operator followed a generic TLS tutorial that recommends EC keys; ssh-keygen output reused as TLS key; cert tooling upgraded to emit EC by default.

Related errors


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