- Wrapped CNG key casting in try/catch for both public and private key extraction - RSABCrypt and other non-CNG implementations now gracefully skip key blob export - Certificate PEM blob still generated correctly (only separate key blobs affected) - Demoted public key extraction failure from Warning to Verbose (non-critical) - Added informative message about key implementation type on failure
Invoke-OpenSSHConfiguration
A PowerShell toolkit for automated installation and configuration of OpenSSH on Windows systems. Provides idempotent configuration of SSH server settings, public key authentication, certificate-based authentication using Windows certificate stores, and key pair generation.
Features
- Automated Installation: Downloads and installs the latest Win32-OpenSSH MSI from GitHub, with dynamic OS architecture detection
- Public Key Authentication: Idempotently manages
administrators_authorized_keyswith deduplication - Certificate Authentication: Exports Windows Root and Intermediate CA certificates to PEM format for OpenSSH trust
- Key Pair Generation: Creates SSH host keys with configurable algorithm and key size
- Configuration Management: Idempotently modifies
sshd_configwith proper encoding (ASCII, no BOM) - Elevation Handling: Automatically re-launches with elevated privileges when required
Requirements
- Windows 10/11 or Windows Server 2016+
- PowerShell 5.1 or later
- Administrator privileges
- Internet connectivity (for initial OpenSSH download)
Installation
Clone the repository and run the script directly:
git clone https://github.com/yourusername/Invoke-OpenSSHConfiguration.git
cd Invoke-OpenSSHConfiguration
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server
Parameters
| Parameter | Type | Description |
|---|---|---|
-Mode |
String | Installation mode: Client or Server |
-PublicKeyList |
String[] | Array of public keys to add to administrators_authorized_keys |
-GenerateKeyPair |
Switch | Generate SSH host key pair |
-KeyType |
String | Key algorithm: rsa, ecdsa, ed25519 (default: ed25519) |
-KeyBits |
Int | Key size: 256, 384, 521 (ECDSA) or 2048, 3072, 4096 (RSA) |
-EnablePubkeyAuthentication |
Switch | Enable PubkeyAuthentication yes in sshd_config |
-DisablePasswordAuthentication |
Switch | Disable password authentication (PasswordAuthentication no) |
-EnableCertificateAuthentication |
Switch | Configure TrustedUserCAKeys for certificate auth |
-TrustWindowsCertificateAuthorities |
Switch | Export Windows CA certificates to PEM bundle |
-CAInclusionExpression |
String | Regex to filter CAs by Subject or Thumbprint (include matches) |
-CAExclusionExpression |
String | Regex to filter CAs by Subject or Thumbprint (exclude matches) |
-SFTPSubsystemPath |
String | Custom SFTP server path (default: sftp-server.exe, configured automatically) |
-SSHPort |
Int | SSH port number (default: 22, used for firewall rules) |
-LogDirectory |
DirectoryInfo | Custom log directory path |
-ContinueOnError |
Switch | Continue execution on non-fatal errors |
Usage Examples
Basic Server Installation
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server
Server with Public Key Authentication
$PublicKeys = @(
'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExample1... user1@host',
'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExample2... user2@host'
)
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-PublicKeyList $PublicKeys `
-EnablePubkeyAuthentication
Server with Certificate Authentication (Windows CA Trust)
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-TrustWindowsCertificateAuthorities `
-EnableCertificateAuthentication `
-EnablePubkeyAuthentication
This exports all unexpired Root and Intermediate CA certificates from the Windows certificate store to C:\ProgramData\ssh\OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem and configures sshd to trust them.
Generate Host Keys
# Generate Ed25519 key (recommended)
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server -GenerateKeyPair
# Generate RSA 4096-bit key
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server -GenerateKeyPair -KeyType rsa -KeyBits 4096
# Generate ECDSA 384-bit key
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server -GenerateKeyPair -KeyType ecdsa -KeyBits 384
Full Server Configuration
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-PublicKeyList @('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... admin@corp') `
-GenerateKeyPair `
-KeyType ed25519 `
-TrustWindowsCertificateAuthorities `
-EnableCertificateAuthentication `
-EnablePubkeyAuthentication `
-DisablePasswordAuthentication `
-LogDirectory 'C:\Logs\OpenSSH'
Hardened Server (Keys Only, No Passwords)
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-EnablePubkeyAuthentication `
-DisablePasswordAuthentication
Selective CA Trust (Regex Filtering)
# Only trust CAs with "Corp" or "Enterprise" in the subject
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-TrustWindowsCertificateAuthorities `
-CAInclusionExpression '(Corp|Enterprise)' `
-EnableCertificateAuthentication
# Trust all CAs except Microsoft root CAs
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-TrustWindowsCertificateAuthorities `
-CAExclusionExpression 'Microsoft' `
-EnableCertificateAuthentication
# Trust specific CA by thumbprint pattern
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-TrustWindowsCertificateAuthorities `
-CAInclusionExpression '^A1B2C3' `
-EnableCertificateAuthentication
Custom SFTP Server Path
SFTP subsystem is configured automatically. To use a custom path:
.\Invoke-OpenSSHConfiguration.ps1 -Mode Server `
-SFTPSubsystemPath 'C:\Program Files\OpenSSH\sftp-server.exe'
File Locations
| File | Path | Description |
|---|---|---|
| sshd_config | C:\ProgramData\ssh\sshd_config |
SSH server configuration |
| sshd_config.backup.* | C:\ProgramData\ssh\sshd_config.backup.* |
Timestamped config backups |
| administrators_authorized_keys | C:\ProgramData\ssh\administrators_authorized_keys |
Authorized keys for admin users |
| CA Bundle | C:\ProgramData\ssh\OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem |
Trusted CA certificates |
| Generated Keys | C:\ProgramData\ssh\keys\ |
Generated host key pairs |
| Logs | %WINDIR%\Temp\Invoke-OpenSSHConfiguration\Logs\ |
Script execution logs |
Certificate Authentication
The script bridges Windows certificate trust to OpenSSH by exporting CA certificates:
- Reads unexpired CA certificates from
Cert:\LocalMachine\RootandCert:\LocalMachine\CA - Applies optional inclusion/exclusion regex filters on Subject and Thumbprint
- Filters for certificates with Basic Constraints CA flag
- Exports to PEM format bundle
- Configures
TrustedUserCAKeysin sshd_config
Important: OpenSSH does NOT accept expired certificates. When CA certificates are renewed, re-run the script with -TrustWindowsCertificateAuthorities to update the PEM bundle.
Periodic CA Refresh
For automated CA refresh, create a scheduled task:
$Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-ExecutionPolicy Bypass -File "C:\Scripts\Invoke-OpenSSHConfiguration.ps1" -TrustWindowsCertificateAuthorities'
$Trigger = New-ScheduledTaskTrigger -Daily -At '3:00AM'
$Principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'OpenSSH-CA-Refresh' -Action $Action -Trigger $Trigger -Principal $Principal
Toolkit Functions
The toolkit includes reusable functions in Toolkit\Functions\:
| Function | Description |
|---|---|
ConvertFrom-CertificateToPEM |
Converts X.509 certificates to PEM format |
Get-GitRepositoryRelease |
Downloads releases from GitHub repositories |
Get-CurrentUser |
Retrieves current user session information |
Start-ProcessWithOutput |
Executes processes with output capture and logging |
Get-InstalledSoftware |
Queries installed software from registry |
Get-MSIPropertyList |
Reads MSI file properties |
Known Limitations
Current Gaps (Not Yet Implemented)
Server Configuration
- User/group restrictions (AllowUsers, AllowGroups)
- SSH banner/MOTD configuration
- ListenAddress binding to specific interfaces
Security
- No automatic ACL/permission setting on generated keys
Management
- No uninstall/rollback capability
- No certificate expiry warnings
Features Implemented
- ✅ Password authentication control (
-DisablePasswordAuthentication) - ✅ Service auto-start and start (automatic, no switches needed)
- ✅ Windows Firewall rules (automatic per-profile: Domain, Private, Public)
- ✅ Port configuration (
-SSHPort, default 22) - ✅ Config backup before modification (automatic timestamped backups)
- ✅ Config validation (
sshd -t) before restart with automatic rollback - ✅ CA selection via regex (
-CAInclusionExpression,-CAExclusionExpression) - ✅ SFTP subsystem (configured automatically, custom path via
-SFTPSubsystemPath)
Troubleshooting
Script Re-launches in New Window
The script automatically elevates to administrator. This is expected behavior.
Public Keys Not Working
- Verify the key format is correct (starts with
ssh-rsa,ssh-ed25519, etc.) - Check
C:\ProgramData\ssh\administrators_authorized_keysfile permissions - Ensure
PubkeyAuthentication yesis in sshd_config
Certificate Authentication Not Working
- Verify CA certificates are not expired
- Check the PEM bundle exists at
C:\ProgramData\ssh\OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem - Verify
TrustedUserCAKeyspath in sshd_config - Run
sshd -tto validate configuration
Service Won't Start
- Check Windows Event Viewer for sshd errors
- Validate config:
& 'C:\Program Files\OpenSSH\sshd.exe' -t - Ensure no port conflicts on TCP 22
Project Structure
Invoke-OpenSSHConfiguration/
├── Invoke-OpenSSHConfiguration.ps1 # Main script
├── README.md # This file
├── Content/ # Additional content files
├── Toolkit/
│ ├── Toolkit.ps1 # Core toolkit initialization
│ ├── Functions/ # Reusable PowerShell functions
│ │ ├── ConvertFrom-CertificateToPEM.ps1
│ │ ├── Get-GitRepositoryRelease.ps1
│ │ ├── Get-CurrentUser.ps1
│ │ └── ...
│ ├── Modules/ # PowerShell modules
│ └── Tools/ # External tools
└── .augment/
└── rules/ # Coding standards
Contributing
- Follow the coding standards in
.augment/rules/PowershellScripts.md - Use the established patterns (OrderedDictionary, Switch statements, etc.)
- Test changes with
-Verboseflag - Update documentation for new parameters
License
[Add your license here]
Acknowledgments
- Win32-OpenSSH - Microsoft's OpenSSH port for Windows