Key Generation
Generate OpenSSH-compatible key files and authorized_keys lines directly from Traversio.
Traversio can generate a fresh OpenSSH key pair without calling ssh-keygen.
SSHOpenSSHKeyPair.generate(...) returns three things together:
authenticationMethod: ready to pass intoSSHClientConfigurationprivateKeyPEM: OpenSSHopenssh-key-v1private-key textauthorizedKeyLine: the matching public line forauthorized_keysor*.pub
That makes the common workflow straightforward:
- generate the key pair
- save the private key with
0600permissions - install the public line on the server
- use the returned
authenticationMethodimmediately, or reload the generated PEM later
API Shape
try SSHOpenSSHKeyPair.generate(
algorithm: SSHOpenSSHKeyPair.Algorithm,
comment: String = "traversio",
encryption: SSHOpenSSHPrivateKeyEncryption? = nil
)Supported algorithms:
.ed25519.ecdsaP256.ecdsaP384.ecdsaP521.rsa(bitCount: Int)
Supported encrypted-export ciphers:
.aes128CTR.aes192CTR.aes256CTR.aes128CBC.aes192CBC.aes256CBC
If you pass SSHOpenSSHPrivateKeyEncryption(passphrase: ...), Traversio emits an encrypted OpenSSH private key using the OpenSSH bcrypt KDF. The default encrypted export matches the current OpenSSH baseline: aes256-ctr with 24 bcrypt rounds.
Choose An Algorithm
For most new client keys, use .ed25519.
Use ECDSA when you specifically need a NIST curve:
.ecdsaP256.ecdsaP384.ecdsaP521
Use RSA when the target environment requires it. Traversio accepts RSA sizes of at least 1024 bits and only whole-byte sizes. New deployments usually choose 2048 or higher.
Generate A New Encrypted Deployment Key
import Traversio
func makeDeploymentKey() throws -> SSHOpenSSHKeyPair {
try SSHOpenSSHKeyPair.generate(
algorithm: .ed25519,
comment: "[email protected]",
encryption: SSHOpenSSHPrivateKeyEncryption(
passphrase: "correct horse battery staple"
)
)
}That gives you:
- an Ed25519 auth method you can use immediately
- an encrypted OpenSSH private key file body
- a matching
authorized_keysline
Save The Generated Files
import Foundation
import Traversio
func writeKeyPair(_ keyPair: SSHOpenSSHKeyPair) throws -> (privateKey: URL, publicKey: URL) {
let sshDirectory = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".ssh", isDirectory: true)
try FileManager.default.createDirectory(
at: sshDirectory,
withIntermediateDirectories: true
)
let privateKeyURL = sshDirectory.appendingPathComponent("id_traversio")
let publicKeyURL = sshDirectory.appendingPathComponent("id_traversio.pub")
try keyPair.privateKeyPEM.write(
to: privateKeyURL,
atomically: true,
encoding: .utf8
)
try keyPair.authorizedKeyLine.write(
to: publicKeyURL,
atomically: true,
encoding: .utf8
)
try FileManager.default.setAttributes(
[.posixPermissions: 0o600],
ofItemAtPath: privateKeyURL.path
)
try FileManager.default.setAttributes(
[.posixPermissions: 0o644],
ofItemAtPath: publicKeyURL.path
)
return (privateKeyURL, publicKeyURL)
}The private key should be stored with restrictive permissions. 0600 is the practical baseline.
Use The Generated Key Immediately
Use the returned authenticationMethod immediately when the key generation and connection flow happen in the same process:
import Traversio
@available(macOS 26.0, iOS 26.0, *)
func connectWithGeneratedKey() async throws {
let keyPair = try SSHOpenSSHKeyPair.generate(
algorithm: .ed25519,
comment: "[email protected]",
encryption: SSHOpenSSHPrivateKeyEncryption(
passphrase: "correct horse battery staple"
)
)
let configuration = SSHClientConfiguration(
host: "example.com",
username: "deploy",
authentication: keyPair.authenticationMethod,
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts")
)
try await SSHClient.withConnection(configuration: configuration) { connection in
let result = try await connection.execute("whoami")
print(String(decoding: result.standardOutput, as: UTF8.self))
}
}This is the simplest path when your app generates the key and then uses it in the same flow.
Reload The Generated PEM Later
If you only kept the file on disk, load it back through the existing OpenSSH helpers:
import Traversio
func reloadGeneratedKey(at path: String) throws -> SSHAuthenticationMethod {
try .ed25519PrivateKey(
contentsOfOpenSSHPrivateKeyFile: path,
passphrase: "correct horse battery staple"
)
}For other algorithms, switch to the matching helper:
try .rsaPrivateKey(...)try .ecdsaPrivateKey(...)
Pick A Custom Cipher Or bcrypt Round Count
import Traversio
func makeRSAKeyForLegacyInterop() throws -> SSHOpenSSHKeyPair {
try SSHOpenSSHKeyPair.generate(
algorithm: .rsa(bitCount: 3072),
comment: "[email protected]",
encryption: SSHOpenSSHPrivateKeyEncryption(
passphrase: "correct horse battery staple",
cipher: .aes256CBC,
rounds: 32
)
)
}Use a custom cipher only when you have a real interoperability reason. The default aes256-ctr path is the best default here.
Install The Public Line On The Server
authorizedKeyLine is already the text OpenSSH expects in ~/.ssh/authorized_keys:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... [email protected]You can:
- append that line to the target account's
authorized_keys - save it as
id_traversio.pub - send it to another provisioning tool that manages SSH login keys
Validation
The current implementation is covered by:
- deterministic round-trip tests for Ed25519, ECDSA P-256/P-384/P-521, and RSA generation
- deterministic encrypted-export tests for every currently supported AES cipher
- macOS
ssh-keygen -ysmoke checks for generated keys
The generated PEM is validated inside Traversio and checked against the system OpenSSH reader on macOS for the supported generation paths.
Limits
Key generation stays intentionally narrow:
- only Ed25519, ECDSA P-256/P-384/P-521, and RSA are exposed
- encrypted export supports the OpenSSH bcrypt KDF with the AES ciphers listed above
- SSH agent integration and keychain-backed key storage remain outside the current surface
- this page focuses on key-file generation; server-side key installation and rotation policy remain application or infrastructure responsibilities
If you only need to authenticate with an existing key file, see Public-Key Authentication. If you want the raw public signatures and method list, see Public API.