TWSSH Documentation
Complete API reference for TWSSH - a high-performance, enterprise-grade SSH library for .NET applications.
Installation
TWSSH targets multiple .NET versions for maximum compatibility:
- .NET Standard 2.1
- .NET 6.0
- .NET 8.0
NuGet Package
dotnet add package TwSsh
Manual Installation
Extract the SDK and add a reference to TwSsh.Core.dll in your project.
Quick Start
Here's a complete example to connect and run a command:
using TwSsh.Client;
using TwSsh.Authentication;
using TwSsh.Licensing;
// Initialize license (required)
LicenseManager.Instance.Initialize("YOUR-LICENSE-KEY");
// Create client with settings
var settings = new SshClientSettings
{
Host = "example.com",
Port = 22,
Credential = new PasswordCredential("username", "password")
};
await using var client = new SshClient(settings);
// Connect and authenticate
await client.ConnectAsync();
await client.AuthenticateAsync(settings.Credential);
// Run a command
var result = await client.RunCommandAsync("uname -a");
Console.WriteLine(result.Output);
Console.WriteLine($"Exit code: {result.ExitCode}");
Use ConnectAndAuthenticateAsync() as a shorthand for both operations.
Licensing
TWSSH requires a valid license to operate. Initialize the license at application startup:
using TwSsh.Licensing;
// Option 1: Initialize with license key string
LicenseManager.Instance.Initialize("YOUR-LICENSE-KEY");
// Option 2: Load from file
LicenseManager.Instance.InitializeFromFile("path/to/license.lic");
// Option 3: Start a trial (30 days)
LicenseManager.Instance.InitializeTrial(30);
// Option 4: Auto-detect license from common locations
LicenseManager.Instance.TryAutoInitialize();
License Types
| Type | Connections | Features |
|---|---|---|
Trial |
2 | All Team features (30 days) |
Developer |
5 | SSH, Shell, SFTP, SCP, Public Key Auth |
Team |
25 | + Port Forwarding, Advanced Crypto, Connection Pooling |
Enterprise |
Unlimited | All features + Priority Support |
SshClient
TwSsh.Client
The main class for SSH connections. Implements IAsyncDisposable.
Constructors
Properties
| Property | Type | Description |
|---|---|---|
State |
SshClientState |
Current connection state |
IsConnected |
bool |
Whether connected to server |
IsAuthenticated |
bool |
Whether authentication succeeded |
ConnectionInfo |
SshConnectionInfo? |
Connection details after connect |
Connection Methods
Establishes connection and performs key exchange.
Connects and authenticates using the credential from settings.
Authenticates with the server. Returns true on success.
Gracefully disconnects from the server.
Command Execution
Executes a command and returns the result with output, error, and exit status.
Creates a command for advanced scenarios with streaming output.
Shell Access
Creates an interactive shell with optional pseudo-terminal.
Subsystems
Opens a subsystem channel (e.g., "sftp").
Port Forwarding
Forwards a local port to a remote destination through the SSH tunnel.
Requests the server forward a remote port to a local destination.
SshClientSettings
TwSsh.Client
Configuration for SSH connections.
| Property | Type | Default | Description |
|---|---|---|---|
Host |
string |
"" |
Remote host address |
Port |
int |
22 |
Remote port |
Credential |
SshCredential? |
null |
Authentication credential |
ConnectionTimeout |
TimeSpan |
30s |
Connection timeout |
OperationTimeout |
TimeSpan |
1m |
Operation timeout |
KeepAliveInterval |
TimeSpan |
0 |
Keep-alive interval (0 = disabled) |
AutoReconnect |
bool |
false |
Auto-reconnect on connection loss |
HostKeyVerification |
HostKeyVerificationMode |
AutoAdd |
Host key verification mode |
HostKeyCallback |
Func<HostKeyEventArgs, bool>? |
null |
Custom host key verification |
Algorithm Preferences
You can specify preferred algorithms (in order of preference):
KeyExchangeAlgorithmsHostKeyAlgorithmsEncryptionAlgorithmsMacAlgorithmsCompressionAlgorithms
Authentication
TwSsh.Authentication
PasswordCredential
Simple password authentication.
var credential = new PasswordCredential("username", "password");
PrivateKeyCredential
Public key authentication using private keys.
// Load from file (pass null for unencrypted keys)
var key = PrivateKeyParser.LoadFromFile("~/.ssh/id_rsa", null);
var credential = new PrivateKeyCredential("username", key);
// With passphrase
var key = PrivateKeyParser.LoadFromFile("~/.ssh/id_rsa", "passphrase");
// Multiple keys
var credential = new PrivateKeyCredential("username", key1, key2, key3);
KeyboardInteractiveCredential
For servers requiring keyboard-interactive authentication.
// Simple password-based keyboard-interactive
var credential = KeyboardInteractiveCredential.FromPassword("username", "password");
// Custom prompt handler
var credential = new KeyboardInteractiveCredential("username",
(instruction, prompts, echos) => {
// Return responses for each prompt
return prompts.Select(p => GetResponse(p)).ToArray();
});
Command Execution
Simple Command
var result = await client.RunCommandAsync("ls -la");
Console.WriteLine(result.Output); // stdout
Console.WriteLine(result.ErrorOutput); // stderr
Console.WriteLine(result.ExitCode); // exit code
Console.WriteLine(result.IsSuccess); // true if exit code == 0
Streaming Output
await using var cmd = await client.CreateCommandAsync("long-running-command");
// Read output as it arrives
var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await cmd.OutputStream.ReadAsync(buffer)) > 0)
{
Console.Write(Encoding.UTF8.GetString(buffer, 0, bytesRead));
}
SshCommandResult Properties
| Property | Type | Description |
|---|---|---|
Output | string | Standard output |
ErrorOutput | string | Standard error |
ExitCode | int | Exit code |
IsSuccess | bool | True if exit code is 0 |
Interactive Shell
ShellStream provides an interactive shell session with PTY support.
Basic Shell
await using var shell = await client.CreateShellAsync();
// Send commands
await shell.WriteLineAsync("cd /var/log");
await shell.WriteLineAsync("ls -la");
// Read response
var output = await shell.ReadLineAsync();
With PTY Settings
var pty = new PtyRequest
{
TerminalType = "xterm-256color",
WidthChars = 120,
HeightRows = 40,
Modes = new Dictionary<TerminalMode, uint>
{
[TerminalMode.Echo] = 1
}
};
await using var shell = await client.CreateShellAsync(pty);
Pattern Matching
// Wait for specific text
var output = await shell.ExpectAsync("$ ", TimeSpan.FromSeconds(5));
// Wait for regex pattern
var output = await shell.ExpectAsync(new Regex(@"password:\s*$"), TimeSpan.FromSeconds(10));
Window Resize
await shell.SendWindowChangeAsync(132, 50);
SFTP
TWSSH provides a full-featured SFTP client for secure file transfer operations:
using TwSsh.Sftp;
// Create SFTP client from authenticated connection
await using var sftp = await SftpClient.ConnectAsync(client);
// Upload a file
await sftp.UploadFileAsync("local.txt", "/remote/path/file.txt");
// Download a file
await sftp.DownloadFileAsync("/remote/data.csv", "local.csv");
// List directory contents
var files = await sftp.ListDirectoryAsync("/home/user");
foreach (var file in files)
{
Console.WriteLine($"{file.FileName} - {file.Attributes.Size} bytes");
}
Progress Reporting
var progress = new Progress<SftpTransferProgress>(p =>
{
Console.WriteLine($"File: {p.FileName}");
Console.WriteLine($"Progress: {p.BytesTransferred} / {p.TotalBytes} bytes");
Console.WriteLine($"Percent: {p.PercentComplete:F1}%");
Console.WriteLine($"Speed: {p.BytesPerSecond / 1024.0:F1} KB/s");
Console.WriteLine($"Elapsed: {p.Elapsed}");
Console.WriteLine($"ETA: {p.EstimatedRemaining}");
});
await sftp.UploadFileAsync("large.zip", "/backup/large.zip",
new SftpTransferOptions(), progress);
SftpTransferProgress Properties
| Property | Type | Description |
|---|---|---|
FileName | string | Name of file being transferred |
BytesTransferred | long | Bytes transferred so far |
TotalBytes | long | Total file size in bytes |
PercentComplete | double | Percentage complete (0-100) |
BytesPerSecond | double | Current transfer speed |
Elapsed | TimeSpan | Time elapsed since start |
EstimatedRemaining | TimeSpan | Estimated time remaining |
IsComplete | bool | Whether transfer is finished |
Directory Operations
// Create directory
await sftp.CreateDirectoryAsync("/home/user/newdir");
// Delete file
await sftp.DeleteFileAsync("/tmp/oldfile.txt");
// Rename/move
await sftp.RenameAsync("/old/path.txt", "/new/path.txt");
// Check existence
if (await sftp.ExistsAsync("/path/to/file"))
Console.WriteLine("File exists!");
Stream-Based Access
// Open remote file as a stream
await using var stream = await sftp.OpenFileAsync(
"/remote/file.txt", FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
SftpClient supports SFTP protocol versions 3-6, automatic version negotiation, resume transfers, batch operations, symbolic links, and server extension detection.
Port Forwarding
Local Port Forwarding
Forward a local port to a remote destination:
// Forward local port 8080 to remote server's port 80
await using var forward = await client.ForwardLocalPortAsync(
"127.0.0.1", // local host
8080, // local port
"localhost", // remote host (relative to SSH server)
80 // remote port
);
// Now http://127.0.0.1:8080 connects to remote:80
Console.WriteLine("Port forwarding active...");
await Task.Delay(TimeSpan.FromMinutes(10));
Remote Port Forwarding
Request the server forward a remote port to your local machine:
// Forward remote port 9000 to local port 3000
await using var forward = await client.ForwardRemotePortAsync(
"0.0.0.0", // remote bind address
9000, // remote port
"127.0.0.1", // local host
3000 // local port
);
// Connections to server:9000 now reach local:3000
Accessing Port Information
The forwarding methods return the base ForwardedPort type. To access port-specific properties, cast to the concrete type:
// ForwardLocalPortAsync returns LocalPortForwarder
await using var tunnel = await client.ForwardLocalPortAsync(
"127.0.0.1", 0, // port 0 = auto-assign
"localhost", 3306
);
// Cast to access the actual assigned port
if (tunnel is LocalPortForwarder lpf)
{
Console.WriteLine($"Tunnel listening on port {lpf.LocalPort}");
}
// ForwardRemotePortAsync returns RemotePortForwarder
await using var remote = await client.ForwardRemotePortAsync(
"0.0.0.0", 0,
"127.0.0.1", 8080
);
if (remote is RemotePortForwarder rpf)
{
Console.WriteLine($"Remote port {rpf.RemotePort} forwarded");
}
ForwardedPort Properties
| Type | Property | Description |
|---|---|---|
LocalPortForwarder | LocalHost | Local bind address |
LocalPortForwarder | LocalPort | Local port number (useful with port 0) |
LocalPortForwarder | RemoteHost | Remote destination host |
LocalPortForwarder | RemotePort | Remote destination port |
RemotePortForwarder | RemoteHost | Remote bind address |
RemotePortForwarder | RemotePort | Remote port number |
RemotePortForwarder | LocalHost | Local destination host |
RemotePortForwarder | LocalPort | Local destination port |
| Both | ErrorOccurred | Event for forwarding errors |
Private Keys
TwSsh.Core.Authentication PrivateKeyParser
TwSsh.Authentication PrivateKeyCredential
PrivateKeyParser is in TwSsh.Core.Authentication, while PrivateKeyCredential is in TwSsh.Authentication. Make sure to include both using statements.
Loading Keys
using TwSsh.Core.Authentication; // For PrivateKeyParser
using TwSsh.Authentication; // For PrivateKeyCredential
// From file (pass null for unencrypted keys)
var key = PrivateKeyParser.LoadFromFile("/path/to/key", null);
// From file with passphrase
var key = PrivateKeyParser.LoadFromFile("/path/to/key", "passphrase");
// From string (pass null for unencrypted keys)
var key = PrivateKeyParser.Parse(keyContent, null);
// From binary data
var key = PrivateKeyParser.ParseBinary(keyBytes);
Supported Formats
| Format | Header |
|---|---|
| OpenSSH | BEGIN OPENSSH PRIVATE KEY |
| PEM RSA | BEGIN RSA PRIVATE KEY |
| PEM EC | BEGIN EC PRIVATE KEY |
| PKCS#8 | BEGIN PRIVATE KEY |
| PKCS#8 Encrypted | BEGIN ENCRYPTED PRIVATE KEY |
Supported Key Types
- RSA - ssh-rsa, rsa-sha2-256, rsa-sha2-512
- ECDSA - nistp256, nistp384, nistp521
- Ed25519 - ssh-ed25519 (.NET 6.0+ only)
Channels
TwSsh.Connection
ISessionChannel
Session channels support commands, shells, and subsystems.
| Method | Description |
|---|---|
RequestPtyAsync() | Request pseudo-terminal |
RequestShellAsync() | Start interactive shell |
RequestExecAsync() | Execute command |
RequestSubsystemAsync() | Start subsystem |
SetEnvironmentVariableAsync() | Set environment variable |
SendSignalAsync() | Send signal to process |
Events
SshClient Events
| Event | EventArgs | Description |
|---|---|---|
Disconnected |
SshDisconnectEventArgs |
Connection was closed |
HostKeyReceived |
HostKeyEventArgs |
Server's host key received |
Host Key Verification
client.HostKeyReceived += (sender, e) => {
Console.WriteLine($"Host key: {e.Fingerprint}");
Console.WriteLine($"Algorithm: {e.HostKeyAlgorithm}");
// Set e.Accept = false to reject the connection
e.Accept = VerifyFingerprint(e.Fingerprint);
};
Exceptions
TwSsh.Common
| Exception | Description |
|---|---|
SshException |
Base exception for all SSH errors |
SshConnectionException |
Connection-related errors (includes DisconnectReason) |
SshAuthenticationException |
Authentication failures (includes AllowedMethods) |
SshChannelException |
Channel operations failed |
LicenseException |
License validation errors |
try
{
await client.ConnectAndAuthenticateAsync();
}
catch (SshAuthenticationException ex)
{
Console.WriteLine($"Auth failed. Allowed methods: {string.Join(", ", ex.AllowedMethods)}");
}
catch (SshConnectionException ex)
{
Console.WriteLine($"Connection failed: {ex.DisconnectReason}");
}
catch (LicenseException ex) when (ex.ErrorType == LicenseErrorType.Expired)
{
Console.WriteLine("License has expired");
}
Logging
TwSsh.Common
Implement ISshLogger for custom logging:
public class ConsoleLogger : ISshLogger
{
public void Log(SshLogLevel level, string message, Exception? ex = null)
{
Console.WriteLine($"[{level}] {message}");
if (ex != null) Console.WriteLine($" Exception: {ex.Message}");
}
public bool IsEnabled(SshLogLevel level) => level >= SshLogLevel.Info;
}
// Use in settings
var settings = new SshClientSettings
{
Host = "example.com",
Credential = credential,
Logger = new ConsoleLogger()
};
SshLogLevel
Trace | Detailed protocol messages |
Debug | Debugging information |
Info | General information |
Warning | Warning messages |
Error | Error messages |
Fatal | Fatal errors |
Enumerations
SshClientState
Disconnected | Not connected |
Connecting | Connection in progress |
Connected | Connected, not authenticated |
Authenticated | Ready for use |
Disconnecting | Disconnection in progress |
HostKeyVerificationMode
None | No verification (insecure) |
AutoAdd | Auto-accept unknown keys |
Strict | Reject unknown keys |
Callback | Use custom callback |
LicenseType
None | No valid license |
Trial | 30-day trial |
Developer | Developer license |
Team | Team license |
Enterprise | Enterprise license |
SshDisconnectReason
HostNotAllowedToConnect | 1 |
ProtocolError | 2 |
KeyExchangeFailed | 3 |
MacError | 5 |
CompressionError | 6 |
ServiceNotAvailable | 7 |
ProtocolVersionNotSupported | 8 |
HostKeyNotVerifiable | 9 |
ConnectionLost | 10 |
ByApplication | 11 |
TooManyConnections | 12 |
AuthCancelledByUser | 13 |
NoMoreAuthMethodsAvailable | 14 |
Supported Algorithms
Key Exchange
- curve25519-sha256
- [email protected]
- ecdh-sha2-nistp256
- ecdh-sha2-nistp384
- ecdh-sha2-nistp521
- diffie-hellman-group-exchange-sha256
- diffie-hellman-group16-sha512
- diffie-hellman-group14-sha256
Host Key
- ssh-ed25519
- ecdsa-sha2-nistp256
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp521
- rsa-sha2-512
- rsa-sha2-256
- ssh-rsa
Encryption
- [email protected]
- [email protected]
- [email protected]
- aes256-ctr
- aes192-ctr
- aes128-ctr
MAC
- [email protected]
- [email protected]
- hmac-sha2-512
- hmac-sha2-256
AEAD ciphers (ChaCha20-Poly1305, AES-GCM) don't require separate MAC algorithms as they provide built-in authentication.
Troubleshooting
Common issues and solutions when using TWSSH.
Connection Errors
| Error Message | Cause | Solution |
|---|---|---|
Connection timed out |
Server unreachable or firewall blocking | Verify host/port, check firewall rules, increase ConnectionTimeout |
Connection refused |
SSH server not running on target port | Verify SSH service is running, check port number |
Host key verification failed |
Server key changed or not trusted | Use HostKeyVerificationMode.Callback to verify key fingerprint |
No matching key exchange |
Server doesn't support modern algorithms | Configure KeyExchangeAlgorithms in settings to include legacy algorithms |
Authentication Errors
| Error Message | Cause | Solution |
|---|---|---|
Authentication failed |
Invalid credentials | Verify username/password, check AllowedMethods in exception |
Permission denied (publickey) |
Key not authorized on server | Add public key to server's ~/.ssh/authorized_keys |
Invalid key format |
Unsupported or corrupt private key | Use OpenSSH format, verify key file integrity |
Passphrase required |
Encrypted key without passphrase | Provide passphrase to PrivateKeyParser.LoadFromFile() |
License Errors
| Error Message | Cause | Solution |
|---|---|---|
License not initialized |
No license loaded before SSH operations | Call LicenseManager.Instance.Initialize() at app startup |
License expired |
License validity period ended | Renew license at [email protected] |
Feature not available |
Feature requires higher license tier | Upgrade license or use LicenseManager.Instance.HasFeature() to check |
Connection limit exceeded |
Too many concurrent connections for license | Close unused connections or upgrade license tier |
SFTP Errors
| Error Message | Cause | Solution |
|---|---|---|
No such file |
Remote path doesn't exist | Verify path, use ExistsAsync() to check first |
Permission denied |
Insufficient permissions on remote | Check file/directory permissions on server |
Directory not empty |
Attempting to delete non-empty directory | Use recursive delete or empty directory first |
Debugging Tips
// Check license status
var info = LicenseManager.Instance.LicenseInfo;
Console.WriteLine($"License: {info.Type}, Expires: {info.Expires}");
Console.WriteLine($"Max connections: {info.MaxConnections}");
// Check connection info after connect
await client.ConnectAsync();
Console.WriteLine($"Server: {client.ConnectionInfo.ServerVersion}");
Console.WriteLine($"Algorithms: {client.ConnectionInfo.Algorithms}");
// Handle auth failure to see allowed methods
try
{
await client.AuthenticateAsync(credential);
}
catch (SshAuthenticationException ex)
{
Console.WriteLine($"Allowed methods: {string.Join(", ", ex.AllowedMethods)}");
}
© 2025 TWSSH. All rights reserved.