Skip to content

fix(ssh): repair pasted private key material and negotiate RSA signature hash#186

Merged
su-fen merged 1 commit into
mainfrom
fix/ssh-private-key-auth
Jul 21, 2026
Merged

fix(ssh): repair pasted private key material and negotiate RSA signature hash#186
su-fen merged 1 commit into
mainfrom
fix/ssh-private-key-auth

Conversation

@coder-hhx

Copy link
Copy Markdown
Collaborator

Problem

SSH tunnel connections using private-key auth fail with generic errors ("Invalid SSH private key: Could not read key" / "SSH authentication failed") in several common setups.

Root causes

  1. Pasted key material breaks russh's strict PEM reader. russh::keys::decode_secret_key matches -----BEGIN/END ...----- markers by exact line equality and silently drops any base64 line containing a non-base64 character. A key pasted into settings with indentation, a trailing space on any line, collapsed newlines (single-line paste), literal \n escapes (copied out of JSON), CRLF endings, or a BOM fails to decode even though it looks fine in the UI. Verified against russh 0.62.2:
    • indented lines → SshKey: length invalid
    • trailing whitespace / single-line → Could not read key
  2. RSA signature hash was hardcoded to rsa-sha2-256. Servers that only accept legacy ssh-rsa (old OpenSSH/embedded devices) reject the auth outright.
  3. Unhelpful errors for encrypted keys. A missing or wrong passphrase surfaced as "Invalid SSH private key: The key is encrypted" / "SshKey: cryptographic error".

Fix

  • normalize_ssh_private_key_material: strips BOM/zero-width chars, normalizes CRLF and literal \n/\r\n escapes, trims per-line whitespace, and canonically re-folds the PEM block at 64 columns. Legacy encrypted PEM (Proc-Type:/DEK-Info: headers) and PuTTY PPK content are intentionally left line-structured. Applied in resolve_ssh_auth_material for both pasted keys and keys read from privateKeyPath, so existing mangled stored keys are repaired at connect time.
  • RSA hash is now negotiated from the server's server-sig-algs extension (RFC 8308) via best_supported_rsa_hash(), falling back to the previous SHA-256 behavior when the server does not advertise the extension. Non-RSA keys skip the negotiation (avoids the extension-info wait).
  • describe_ssh_private_key_decode_error maps decode failures to actionable messages ("key is encrypted; configure the key passphrase", "Invalid SSH private key or wrong passphrase: ...").

No UI changes (backend-only), so no gateway WebUI mirror is needed. The gateway has no parallel SSH implementation; the Tauri terminal runtime is the single connect path (tunnel + SFTP).

Tests

  • 8 new unit tests covering: all paste-artifact variants normalize to the canonical form and decode; normalization idempotency; encrypted-PEM header and PPK passthrough; resolve_ssh_auth_material with pasted and file-based keys; encrypted key with passphrase; error messages for missing/wrong passphrase.
  • Full suite: cargo test --lib → 518 passed. cargo clippy --lib --tests introduces no new warnings.

…ure hash

Private-key SSH tunnel connections failed with generic errors in common
setups:

- russh's PEM reader matches BEGIN/END markers by exact line equality and
  silently drops base64 lines with stray whitespace, so keys pasted with
  indentation, trailing spaces, collapsed newlines, literal \n escapes,
  CRLF endings, or a BOM failed to decode with 'Could not read key'.
  Normalize key material and canonically re-fold PEM blocks before
  decoding (legacy encrypted PEM headers and PPK content are preserved).
- The RSA signature hash was hardcoded to rsa-sha2-256, which breaks
  against servers that only accept ssh-rsa. Negotiate via the RFC 8308
  server-sig-algs extension and keep SHA-256 as the fallback; non-RSA
  keys skip the negotiation.
- Encrypted-key decode failures now say that a passphrase is required or
  hint at a wrong passphrase instead of a generic 'Invalid SSH private
  key'.
@su-fen
su-fen merged commit 331a77f into main Jul 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants