SshKeyGenerator 2.0.0
dotnet add package SshKeyGenerator --version 2.0.0
NuGet\Install-Package SshKeyGenerator -Version 2.0.0
<PackageReference Include="SshKeyGenerator" Version="2.0.0" />
<PackageVersion Include="SshKeyGenerator" Version="2.0.0" />
<PackageReference Include="SshKeyGenerator" />
paket add SshKeyGenerator --version 2.0.0
#r "nuget: SshKeyGenerator, 2.0.0"
#:package SshKeyGenerator@2.0.0
#addin nuget:?package=SshKeyGenerator&version=2.0.0
#tool nuget:?package=SshKeyGenerator&version=2.0.0
DotNet Core SSH KeyGen
Generate RSA SSH keys in managed C# on Windows, Linux, and macOS. Export private keys as OpenSSH or PKCS#1 RSA/PEM, with optional passphrase protection. The library has no runtime package dependencies and does not invoke external tools.
Project principles
- Keep the library dependency-free. Production code must use the .NET Base
Class Library (BCL), including
System.Security.Cryptography, rather than third-party cryptography libraries such as Bouncy Castle or SSH.NET. - Keep key generation self-contained. Do not require external executables, native crypto packages, or platform-specific interop for generation or export.
- Implement missing format support locally. Where the BCL does not provide an SSH encoding or derivation operation, use a focused managed implementation built on the BCL. The internal bcrypt_pbkdf implementation is one such case; preserve its attribution and license notices.
- Verify interoperability independently. Third-party readers and command-line tools may be used in tests only; they must never become dependencies of the shipped library. Test/build packages must remain separate from runtime code.
- Preserve existing functionality. Keep existing APIs and key formats compatible, and document any observable behavior changes.
Supported Platforms
- .NET Framework 4.5.2 (legacy)
- .NET Standard 2.0
- .NET Standard 2.1
- .NET 5.0
- .NET 6.0
- .NET 8.0
- .NET 10.0
.NET Core applications can use the .NET Standard assets. Existing legacy targets are retained for compatibility; new applications should use a supported .NET runtime.
Installation
SshKeyGenerator is a library for .NET and is available on NuGet:
Install-Package SshKeyGenerator
or
dotnet add package SshKeyGenerator
Usage Example
using var keygen = new SshKeyGenerator.SshKeyGenerator(2048);
var privateKey = keygen.ToPrivateKey();
Console.WriteLine(privateKey);
var publicSshKey = keygen.ToRfcPublicKey();
Console.WriteLine(publicSshKey);
var publicSshKeyWithComment = keygen.ToRfcPublicKey("user@domain.com");
Console.WriteLine(publicSshKeyWithComment);
Passphrases and private key formats
using SshKeyGenerator;
using var keygen = new SshKeyGenerator.SshKeyGenerator(2048);
// Obtain this from your application's secret input mechanism.
string passphrase = GetPassphrase();
// Modern OpenSSH container: -----BEGIN OPENSSH PRIVATE KEY-----
string openSsh = keygen.ToPrivateKey(PrivateKeyFormat.OpenSsh, passphrase, "user@domain.com");
// Equivalent convenience method:
string sameFormat = keygen.ToOpenSshPrivateKey(passphrase, "user@domain.com");
// Traditional PKCS#1 PEM: -----BEGIN RSA PRIVATE KEY-----
string pem = keygen.ToPrivateKey(PrivateKeyFormat.Pem, passphrase);
// Equivalent convenience method:
string samePemFormat = keygen.ToPrivateKey(passphrase);
ToPrivateKey() still returns an unencrypted PKCS#1 PEM key. A null or empty
passphrase exports an unencrypted key in either format. Whitespace and Unicode
passphrases are preserved and encoded as UTF-8. Encryption applies to each export;
it does not change the key pair or encrypt XML/CSP blob exports. OpenSSH comments
are optional; CR, LF, and NUL are rejected.
| Format | Encryption with a passphrase | Key derivation |
|---|---|---|
OpenSSH (openssh-key-v1) |
AES-256-CBC | bcrypt_pbkdf, 64 rounds, random 16-byte salt |
| RSA/PEM (PKCS#1) | AES-256-CBC with PKCS#7 padding | OpenSSL EVP_BytesToKey, MD5, random IV |
Prefer OpenSSH for new integrations. The PEM derivation is the legacy algorithm
required for compatibility with traditional encrypted RSA PRIVATE KEY files;
it provides less resistance to passphrase guessing than bcrypt. This is not
PKCS#8 (ENCRYPTED PRIVATE KEY). Every encrypted export uses fresh randomness.
ToRfcPublicKey() retains its existing name and returns a single-line OpenSSH
authorized_keys entry, not an RFC4716 block. ToPublicKey() returns a
SubjectPublicKeyInfo PUBLIC KEY PEM block. RSA is the key algorithm in both
private key formats.
Save private keys using UTF-8 without a BOM and restrict file access (for example,
chmod 600 id_rsa on Unix). To verify a saved key:
ssh-keygen -y -f id_rsa
# For RSA/PEM only:
openssl rsa -in id_rsa.pem -check -noout
Both commands prompt for an encrypted key's passphrase.
Sample Output
keygen.ToPrivateKey()
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs1O/9F2XlZVaVWEUYfflxx0wDG9FE09hQF2wngA6EemHpe6I
hvdwvQ7obrZ85jSYdBxBRjNAYF3T9+wU+8w6m4qZCVYvqqRjszN8j3VwUBLzWilt
...
wDGzJIkmbhANi+252pH6PAuWjZPfz8COGA6QPPH0/khpHPj1LY4mi5Ubi3YT1uRo
dOGttjtkj9fLX5IQ81G9HR0MdT1Gd4fPenYPOUCgCPB5adpT1BB+
-----END RSA PRIVATE KEY-----
keygen.ToRfcPublicKey()
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzU7/0XZeVlVpVYRRh9
...
+ohFsbcbAMcfLWGUdouyHvLvF21G0z50z2i2CrU7WW4WdrGGfxhU3TeRTXd7Skwk/K7iBBn3oc/xct generated-key
keygen.ToRfcPublicKey("user@domain.com")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzU7/0XZeVlVpVYRRh9
...
+ohFsbcbAMcfLWGUdouyHvLvF21G0z50z2i2CrU7WW4WdrGGfxhU3TeRTXd7Skwk/K7iBBn3oc/xct user@domain.com
Export Methods
There are three methods which can be used to export SSH key data for use by other libraries such as FxSsh:
- byte[] ToBlobs : Export as Blobs
- string ToB64Blob : Export blobs as base64 string
- string ToXml : Export as XML string
These legacy exports are unencrypted. CSP blob exports use a temporary
RSACryptoServiceProvider and depend on platform support for that provider; PEM
and OpenSSH generation use RSA.Create().
Export methods usage:
// Create a new instance
var keygen = new SshKeyGenerator.SshKeyGenerator(4096);
// Get Base64 encoded keys with private key
var base64Keys = keygen.ToB64Blob(true);
// Use in a SSH server. e.g. FxSsh
var server = new SshServer();
server.AddHostKey("ssh-rsa", base64Keys);
Building and testing
Install the .NET 10 SDK (and the .NET 8 runtime to run both test targets).
dotnet build SshKeyGenerator.sln -c Release
dotnet test tests/SshKeyGenerator.Tests.csproj -c Release
# Also verify decryption using installed ssh-keygen and openssl (Unix shell):
SSH_KEYGEN_INTEROP_TESTS=1 dotnet test tests/SshKeyGenerator.Tests.csproj -c Release
# Exercise the .NET Standard implementation on a .NET runtime:
SSH_KEYGEN_INTEROP_TESTS=1 dotnet test tests/SshKeyGenerator.Tests.csproj -c Release -f net8.0 -p:KeyGeneratorTargetFramework=netstandard2.0
dotnet pack src/SshKeyGenerator.csproj -c Release
Tests use SSH.NET as an independent reader, check incorrect passphrases, compare public keys, and verify RSA signatures. Native-tool checks are opt-in locally and run in Linux/macOS CI. Framework 4.5.2 and .NET 5/6 assets are compile-checked; they require their respective runtimes for execution. See THIRD-PARTY-NOTICES.md for the vendored bcrypt license.
Releasing to NuGet
GitHub Actions builds and tests pull requests and pushes on Windows, Linux, and
macOS. The Linux job saves the NuGet package as the nuget-build workflow artifact.
Publishing a GitHub release triggers .github/workflows/release.yml, which reruns
the full build/test matrix before packing and publishing to NuGet.org. Ordinary
pushes and pull requests do not publish packages.
One-time setup for a package owner:
- Configure NuGet trusted publishing
for repository owner
ShawInnes, repositorySshKeyGenerator, and workflow filenamerelease.yml(without the.github/workflows/prefix). Leave the environment field empty; the workflow does not use a GitHub environment. Scope publishing to theSshKeyGeneratorpackage. - Set the GitHub repository Actions variable
NUGET_USERto the NuGet.org username that owns the policy (not an email address). The workflow exchanges its GitHub OIDC token for a short-lived NuGet API key; no stored API key is needed. - Disable the old AppVeyor project/webhook and remove any required AppVeyor status check from branch protection if still configured. AppVeyor configuration and publishing have been removed from this repository.
For version 2.0.0, merge the changes into master, then create and publish a
GitHub release targeting that commit with tag v2.0.0. The tag must equal
v followed by the package version in src/SshKeyGenerator.csproj; a mismatch
fails before publishing. Update <Version> in that project for subsequent releases.
The release workflow saves the package as the nuget-release artifact before
publishing. A rerun skips an already-published package version; NuGet versions
cannot be overwritten. This workflow does not create tags or GitHub releases.
References
- Exporting Private Key: https://stackoverflow.com/a/23739932
- Exporting Public Key: https://stackoverflow.com/a/25591659
- Exporting Public Key in RFC4716 Format: https://stackoverflow.com/a/47917364
Contributors
Thanks to the following contributors for their assistance with this library.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net452 is compatible. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.5.2
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net10.0
- No dependencies.
-
net5.0
- No dependencies.
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on SshKeyGenerator:
| Package | Downloads |
|---|---|
|
Slithin.Core
Package Description |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on SshKeyGenerator:
| Repository | Stars |
|---|---|
|
furesoft/Slithin
A management application for the remarkable tablet
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 147 | 11/12/2026 |
| 1.1.51 | 1,000,533 | 1/29/2023 |
| 1.1.50 | 113,453 | 3/18/2022 |
| 1.1.49 | 1,600 | 3/18/2022 |
| 1.1.48 | 1,493 | 3/18/2022 |
| 1.1.47 | 1,571 | 3/18/2022 |
| 1.1.46 | 1,498 | 3/18/2022 |
| 1.1.45 | 3,852 | 2/5/2022 |
| 1.1.44 | 1,649 | 2/3/2022 |
| 1.1.43 | 1,629 | 2/3/2022 |
| 1.1.42 | 1,558 | 2/3/2022 |
| 1.1.40 | 188,332 | 3/25/2021 |
| 1.1.39 | 1,510 | 3/25/2021 |
| 1.1.38 | 7,072 | 1/16/2021 |
| 1.1.33 | 36,201 | 3/5/2020 |
| 1.1.32 | 1,721 | 3/5/2020 |
| 1.1.31 | 16,001 | 10/30/2019 |
| 1.1.19 | 5,569 | 7/21/2018 |
| 1.1.18 | 2,326 | 7/21/2018 |
| 1.1.17 | 2,311 | 7/21/2018 |