-
Notifications
You must be signed in to change notification settings - Fork 61
Description
Summary
NIOSSH supports OpenSSH certificates (ssh-*-cert-v01@openssh.com) for authentication but fails to advertise these algorithm names during key exchange and user authentication. This causes SSH servers to reject certificate-based authentication even though NIOSSH can correctly parse and use certificates.
Environment
- SwiftNIO SSH version: main branch (commit 8f33cac)
- Swift version: 6.0+
- Platform: macOS 15.0, iOS 18.0
Problem Description
When NIOSSH performs key exchange or user authentication, it advertises supported host key algorithms via NIOSSHPrivateKey.hostKeyAlgorithms. However, this property only returns base algorithm names (e.g., ssh-ed25519, ecdsa-sha2-nistp256) and omits the corresponding certificate algorithm names (e.g., ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com).
Code Location
Sources/NIOSSH/Keys And Signatures/NIOSSHPrivateKey.swift lines 62-77
internal var hostKeyAlgorithms: [Substring] {
switch self.backingKey {
case .ed25519:
return ["ssh-ed25519"] // ❌ Missing: "ssh-ed25519-cert-v01@openssh.com"
case .ecdsaP256:
return ["ecdsa-sha2-nistp256"] // ❌ Missing: "ecdsa-sha2-nistp256-cert-v01@openssh.com"
case .ecdsaP384:
return ["ecdsa-sha2-nistp384"] // ❌ Missing: "ecdsa-sha2-nistp384-cert-v01@openssh.com"
case .ecdsaP521:
return ["ecdsa-sha2-nistp521"] // ❌ Missing: "ecdsa-sha2-nistp521-cert-v01@openssh.com"
// ...
}
}Impact
Observable Symptoms:
✅ NIOSSH can parse OpenSSH certificates
✅ NIOSSH can sign with certificate private keys
✅ NIOSSH can verify certificate signatures
❌ SSH servers reject authentication because client didn't advertise certificate support
❌ Certificate-based authentication fails even with valid certificates
Root Cause
During SSH protocol negotiation:
-
Key Exchange (SSH_MSG_KEXINIT):
- Client advertises:
["ssh-ed25519", "ecdsa-sha2-nistp256", ...] - Client omits:
["ssh-ed25519-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com", ...] - Server doesn't know client supports certificates
- Client advertises:
-
User Authentication (SSH_MSG_USERAUTH_REQUEST):
- Client uses certificate for public key auth
- Server checks if
ssh-ed25519-cert-v01@openssh.comwas in advertised algorithms - Server finds it missing → rejects authentication
RFC Requirements
RFC 4252 Section 7 "Public Key Authentication Method: publickey" states that clients should advertise all supported public key algorithms. OpenSSH certificate types are defined in PROTOCOL.certkeys and use the *-cert-v01@openssh.com naming convention per RFC 4250 Section 4.6.1.
Reproduction
Test with OpenSSH Certificate
# Generate certificate (using ssh-keygen)
ssh-keygen -t ed25519 -f test_key
ssh-keygen -s ca_key -I test_user -n test_user test_key.pub
# Try to authenticate with NIOSSH using test_key-cert.pub
# Result: Server rejects because client didn't advertise cert supportExpected vs Actual Behavior
Expected (OpenSSH client):
SSH_MSG_KEXINIT:
server_host_key_algorithms: [
"ssh-ed25519-cert-v01@openssh.com",
"ssh-ed25519",
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
"ecdsa-sha2-nistp256",
...
]
Actual (NIOSSH):
SSH_MSG_KEXINIT:
server_host_key_algorithms: [
"ssh-ed25519",
"ecdsa-sha2-nistp256",
...
]
// ❌ Certificate algorithms missing
Proposed Solution
Update hostKeyAlgorithms to include both base and certificate algorithm names:
internal var hostKeyAlgorithms: [Substring] {
switch self.backingKey {
case .ed25519:
return ["ssh-ed25519-cert-v01@openssh.com", "ssh-ed25519"]
case .ecdsaP256:
return ["ecdsa-sha2-nistp256-cert-v01@openssh.com", "ecdsa-sha2-nistp256"]
case .ecdsaP384:
return ["ecdsa-sha2-nistp384-cert-v01@openssh.com", "ecdsa-sha2-nistp384"]
case .ecdsaP521:
return ["ecdsa-sha2-nistp521-cert-v01@openssh.com", "ecdsa-sha2-nistp521"]
#if canImport(Darwin)
case .secureEnclaveP256:
return ["ecdsa-sha2-nistp256-cert-v01@openssh.com", "ecdsa-sha2-nistp256"]
#endif
}
}Why This Works
- Backward Compatible: Servers that don't support certificates will use base algorithm names
- Enables Certificates: Servers that support certificates will see them advertised
- Matches OpenSSH: OpenSSH client advertises both base and certificate algorithms
- Minimal Change: 4-line change (one per key type)
- No Breaking Changes: Only adds capability, doesn't remove anything
Certificate Algorithm Names
These are defined in NIOSSHCertifiedPublicKey.swift (lines 344-350):
static let p256KeyPrefix = "ecdsa-sha2-nistp256-cert-v01@openssh.com".utf8
static let p384KeyPrefix = "ecdsa-sha2-nistp384-cert-v01@openssh.com".utf8
static let p521KeyPrefix = "ecdsa-sha2-nistp521-cert-v01@openssh.com".utf8
static let ed25519KeyPrefix = "ssh-ed25519-cert-v01@openssh.com".utf8Test Coverage
This change is difficult to unit test without a full SSH server that requires certificates. However, it can be verified by:
- Integration test: Connect to SSH server with certificate requirement
- Packet capture: Verify certificate algorithms appear in SSH_MSG_KEXINIT
- Real-world testing: Authenticate with OpenSSH certificates
Breaking Changes
None. This is a pure capability addition:
- Existing non-certificate authentication continues to work
- Adds support for certificate-based authentication
- No API changes
- No behavior changes for existing code
Impact
This fix enables NIOSSH to use OpenSSH certificates for authentication with:
- Enterprise SSH servers requiring certificate authentication
- Bastion hosts and jump boxes using certificates
- Zero-trust architectures using short-lived certificates
- Organizations using certificate-based access control
- Cloud providers requiring certificate authentication
Files Changed
Sources/NIOSSH/Keys And Signatures/NIOSSHPrivateKey.swift- Line 65: Add
"ssh-ed25519-cert-v01@openssh.com" - Line 67: Add
"ecdsa-sha2-nistp256-cert-v01@openssh.com" - Line 69: Add
"ecdsa-sha2-nistp384-cert-v01@openssh.com" - Line 71: Add
"ecdsa-sha2-nistp521-cert-v01@openssh.com"
- Line 65: Add
Total changes: 4 lines (one per key type)
Priority
IMPORTANT - Blocks certificate-based authentication use cases, which are increasingly common in enterprise and cloud environments.
References
- RFC 4252 Section 7: Public Key Authentication Method
- RFC 4250 Section 4.6.1: Naming conventions for extensions
- OpenSSH PROTOCOL.certkeys: Certificate format specification
- Existing code:
NIOSSHCertifiedPublicKey.swiftlines 344-350 (defines cert prefixes)