Windows环境下C# Web应用生成OpenSSH格式SSH密钥对的方案咨询
嘿,我来给你梳理几个可行的方案,不用折腾Cygwin也能在Windows服务器的C# Web应用里生成OpenSSH格式的密钥对:
方案1:用.NET自带API手动生成(无需第三方依赖)
OpenSSH的密钥格式本质是对RSA/ED25519等算法生成的密钥做了特定编码,你可以直接用.NET框架自带的加密类来生成密钥,再手动转换为OpenSSH格式。
举个RSA密钥对生成的示例:
using System; using System.Security.Cryptography; using System.Text; public static class SshKeyGenerator { public static (string PublicKey, string PrivateKey) GenerateRsaKeyPair(int keySize = 2048) { using var rsa = RSA.Create(keySize); // 生成OpenSSH格式公钥 var publicParams = rsa.ExportParameters(false); var publicKeyBytes = new byte[11 + Encoding.ASCII.GetByteCount("ssh-rsa") + publicParams.Exponent.Length + publicParams.Modulus.Length]; var index = 0; // 写入"ssh-rsa"标识的长度和内容 var sshRsaBytes = Encoding.ASCII.GetBytes("ssh-rsa"); WriteBigEndianInt(sshRsaBytes.Length, publicKeyBytes, ref index); Array.Copy(sshRsaBytes, 0, publicKeyBytes, index, sshRsaBytes.Length); index += sshRsaBytes.Length; // 写入指数的长度和内容 WriteBigEndianInt(publicParams.Exponent.Length, publicKeyBytes, ref index); Array.Copy(publicParams.Exponent, 0, publicKeyBytes, index, publicParams.Exponent.Length); index += publicParams.Exponent.Length; // 写入模数(去掉开头可能存在的0x00字节) var modulus = publicParams.Modulus; if (modulus[0] == 0) { var trimmedModulus = new byte[modulus.Length - 1]; Array.Copy(modulus, 1, trimmedModulus, 0, trimmedModulus.Length); modulus = trimmedModulus; } WriteBigEndianInt(modulus.Length, publicKeyBytes, ref index); Array.Copy(modulus, 0, publicKeyBytes, index, modulus.Length); var publicKey = $"ssh-rsa {Convert.ToBase64String(publicKeyBytes)} your-app@server"; // 生成兼容OpenSSH的PKCS#8格式私钥 var privateKey = rsa.ExportPkcs8PrivateKeyPem(); return (publicKey, privateKey); } private static void WriteBigEndianInt(int value, byte[] buffer, ref int index) { buffer[index++] = (byte)(value >> 24); buffer[index++] = (byte)(value >> 16); buffer[index++] = (byte)(value >> 8); buffer[index++] = (byte)value; } }
优点:完全依赖.NET框架,不需要任何额外组件,兼容性强。
缺点:需要手动处理OpenSSH的编码规则,如果要支持ED25519等其他算法,编码逻辑会更复杂。
方案2:使用BouncyCastle NuGet库(推荐)
BouncyCastle是一款成熟的开源加密库,支持几乎所有主流加密算法,并且可以直接导出OpenSSH格式的密钥对,省去手动编码的麻烦。
首先安装对应版本的NuGet包:
- .NET Core/.NET 5+:
Install-Package BouncyCastle.NetCore - .NET Framework:
Install-Package BouncyCastle
示例代码(生成ED25519密钥对):
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; using System.IO; using System.Text; public static class SshKeyGenerator { public static (string PublicKey, string PrivateKey) GenerateEd25519KeyPair() { var generator = new Ed25519KeyPairGenerator(); generator.Init(new Ed25519KeyGenerationParameters(new SecureRandom())); var keyPair = generator.GenerateKeyPair(); // 生成OpenSSH公钥 var pubParams = (Ed25519PublicKeyParameters)keyPair.Public; var pubKeyBytes = new byte[Encoding.ASCII.GetByteCount("ssh-ed25519") + 4 + pubParams.GetEncoded().Length]; var index = 0; var sshEdBytes = Encoding.ASCII.GetBytes("ssh-ed25519"); WriteBigEndianInt(sshEdBytes.Length, pubKeyBytes, ref index); Array.Copy(sshEdBytes, 0, pubKeyBytes, index, sshEdBytes.Length); index += sshEdBytes.Length; WriteBigEndianInt(pubParams.GetEncoded().Length, pubKeyBytes, ref index); Array.Copy(pubParams.GetEncoded(), 0, pubKeyBytes, index, pubParams.GetEncoded().Length); var publicKey = $"ssh-ed25519 {Convert.ToBase64String(pubKeyBytes)} your-app@server"; // 生成OpenSSH格式私钥(兼容PKCS#8) using var ms = new MemoryStream(); var writer = new PemWriter(new StreamWriter(ms)); writer.WriteObject(keyPair.Private); writer.Writer.Flush(); var privateKey = Encoding.UTF8.GetString(ms.ToArray()); return (publicKey, privateKey); } private static void WriteBigEndianInt(int value, byte[] buffer, ref int index) { buffer[index++] = (byte)(value >> 24); buffer[index++] = (byte)(value >> 16); buffer[index++] = (byte)(value >> 8); buffer[index++] = (byte)value; } }
优点:支持多种算法(RSA、ED25519、ECDSA等),无需手动处理编码细节,成熟稳定。
缺点:需要引入第三方库,但BouncyCastle是完全开源免费的,在.NET生态中非常常用。
方案3:调用Windows自带的ssh-keygen.exe(仅适用于Windows 10 1809+/Server 2019+)
从Windows 10 1809版本和Windows Server 2019开始,系统默认预装了OpenSSH客户端,其中包含ssh-keygen.exe(路径为C:\Windows\System32\OpenSSH\ssh-keygen.exe)。你可以在C#中通过进程调用这个工具生成密钥对。
示例代码:
using System; using System.Diagnostics; using System.IO; public static class SshKeyGenerator { public static (string PublicKey, string PrivateKey) GenerateKeyPairWithSshKeyGen(string tempOutputPath, string passphrase = null) { var sshKeyGenPath = Path.Combine(Environment.SystemDirectory, "OpenSSH", "ssh-keygen.exe"); if (!File.Exists(sshKeyGenPath)) { throw new InvalidOperationException("未找到ssh-keygen.exe,请确保系统为Windows 10 1809+/Server 2019+,或手动安装OpenSSH客户端组件"); } var args = $"-t rsa -b 2048 -f \"{tempOutputPath}\""; args += string.IsNullOrEmpty(passphrase) ? " -N \"\"" : $" -N \"{passphrase}\""; var processInfo = new ProcessStartInfo { FileName = sshKeyGenPath, Arguments = args, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using var process = Process.Start(processInfo); process.WaitForExit(); if (process.ExitCode != 0) { var errorMsg = process.StandardError.ReadToEnd(); throw new InvalidOperationException($"生成密钥失败:{errorMsg}"); } var publicKey = File.ReadAllText($"{tempOutputPath}.pub"); var privateKey = File.ReadAllText(tempOutputPath); // 清理临时文件 File.Delete(tempOutputPath); File.Delete($"{tempOutputPath}.pub"); return (publicKey, privateKey); } }
优点:生成的密钥完全符合OpenSSH标准,支持ssh-keygen的所有参数(如指定算法、密码保护等),无需自己处理格式细节。
缺点:依赖特定Windows版本,旧版本服务器需要手动通过「Windows功能」安装OpenSSH客户端。
关于自行实现的必要性
如果你的场景必须完全脱离第三方库和系统工具,那可以基于方案1的思路,参考OpenSSH密钥格式规范手动实现,但需要处理大量边缘情况(如不同算法的编码差异、密钥校验等)。对于大多数业务场景,方案2或3会更省心可靠。
内容的提问来源于stack exchange,提问作者MoreInput

