sharpSsh - .NET 的安全 Shell (SSH) 库
SSH2 协议的 C# 实现。
引言
最近,我需要从我的 C# 代码连接到 SSH 服务器。我需要执行一个简单的任务:登录到远程 Linux 设备,执行一个命令并读取响应。我知道有很多免费的 Java SSH 库,我希望能找到一个免费的 .NET 库来完成这个任务,但我只找到了商业组件。在试验了一个名为 JSch 的开源 Java SSH 库之后,我决定尝试将其移植到 C#,仅作为一种练习。结果就是附带的 sharpSsh 库和这篇解释如何使用它的文章。
背景
SSH (Secure Shell) 是一种通过网络登录到另一台计算机、在远程机器上执行命令以及在机器之间移动文件的协议。它在不安全的通道上提供强大的身份验证和安全通信。JSch 库是 SSH2 协议套件的纯 Java 实现;它包含许多功能,如端口转发、X11 转发、安全文件传输,并支持多种加密和 MAC 算法。JSch 根据 BSD 风格的许可证授权。
我的 C# 版本不是 JSch 的完整移植。我只移植了完成我的简单任务所需的最低限度的功能。以下列表总结了该库支持的功能
- 密钥交换:diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1。
- 加密:3des-cbc
- MAC:hmac-md5
- 主机密钥类型:ssh-rsa 和部分 ssh-dss。
- 用户认证:password, publickey (RSA)
- 生成 RSA 密钥对。
- 更改私钥的密码。
- SCP 和 SFTP
请查看我的 主页 以获取 SharpSSH 的最新版本和功能列表。
使用代码
首先声明一下。代码未经完全测试,我不能保证任何级别的性能、安全性或质量。该库和文章的目的是让我自己(也许还有您)了解 SSH 协议以及 C# 和 Java 之间的差异。
为了提供最简单的 SSH 通信 API,我在 Tamir.SharpSsh
命名空间下创建了两个包装类,它们封装了 JSch 的内部结构
SshStream
- 一个基于流的类,用于在 SSH 通道上读写数据。Scp
- 一个用于处理 SSH 通道上文件传输的类。
在 SSH 通道上读写数据
SshStream
类使在 SSH 通道上读写数据如同任何 I/O 读写任务一样简单。它的构造函数接收三个参数:远程主机名或 IP 地址、用户名和密码。它在构造时立即连接到远程服务器。
//Create a new SSH stream
SshStream ssh = new SshStream("remoteHost", "username", "password");
//..The SshStream has successfully established the connection.
现在,我们可以设置一些属性
//Set the end of response matcher character
ssh.Prompt = "#";
//Remove terminal emulation characters
ssh.RemoveTerminalEmulationCharacters = true;
Prompt
属性是一个匹配响应结尾的字符串。当使用 ReadResponse()
方法时,设置此属性非常有用。该方法会一直读取并缓冲来自 SSH 通道的数据,直到在响应中匹配到 Prompt
字符串,然后才会返回结果字符串。例如,Linux shell 提示符通常以 '#' 或 '$' 结尾,所以在执行命令后,匹配这些字符以检测命令响应的结束会很有用(该属性实际上接收任何正则表达式模式并将其与响应进行匹配,因此可以匹配更复杂的模式,例如 "\[[^@]*@[^]]*]#\s" ,它匹配 Linux 主机的 bash shell 提示符 [user@host dir]#
)。Prompt
属性的默认值是 "\n"
,它只是告诉 ReadResponse()
方法返回一行响应。
响应字符串通常包含转义序列字符,这些字符是终端仿真信号,用于指示连接的 SSH 客户端如何显示响应。但是,如果我们只对“干净”的响应内容感兴趣,可以通过将 RemoveTerminalEmulationCharacters
属性设置为 true
来省略这些字符。
现在,向 SSH 流读写数据将按如下方式进行
//Writing to the SSH channel
ssh.Write( command );
//Reading from the SSH channel
string response = ssh.ReadResponse();
当然,仍然可以使用 SshStream
的标准 Read
/Write
I/O 方法,这些方法在 System.IO.Stream
API 中可用。
使用 SCP 传输文件
使用 Scp
类与 SSH 服务器之间传输文件非常简单。以下代码片段演示了如何做到这一点
//Create a new SCP instance
Scp scp = new Scp();
//Copy a file from local machine to remote SSH server
scp.To("C:\fileName", "remoteHost",
"/pub/fileName", "username", "password");
//Copy a file from remote SSH server to local machine
scp.From("remoteHost", "/pub/fileName",
"username", "password", "C:\fileName");
Scp
类还具有一些用于跟踪文件传输进度的事件
Scp.OnConnecting
- 在 SSH 连接初始化时触发。Scp.OnStart
- 在文件传输开始时触发。Scp.OnEnd
- 在文件传输结束时触发。Scp.OnProgress
- 在文件传输进度更新时触发(可以设置ProgressUpdateInterval
属性来修改毫秒级的进度更新间隔时间)。
运行示例
演示项目是一个简单的控制台应用程序,演示了 SshStream
和 Scp
类的用法。它会提示用户输入远程 SSH 服务器的主机名、用户名和密码,并展示了一个简单的 SSH 会话以及与远程 SSH 机器之间进行文件传输的示例。
以下是连接到 Linux shell 的 SSH 连接的屏幕截图
这是使用 SCP 将文件从 Linux 机器传输到我的 PC 的文件传输截图
在演示项目 zip 文件中,您还将找到一个 examples 目录,其中包含一些展示原始 JSch API 用法的类。这些示例直接从原始 JSch 库随附的 Java 示例翻译而来,并展示了公钥认证、已知主机文件、密钥生成、SFTP 等高级选项的用法。
参考文献
- SharpSSH 主页 - 新版本和 bug 修复将发布在此处,请查看此页面以获取最新更新。
- JSch - JSch 是 SSH2 协议套件的纯 Java 实现。我的 C# 库基于此项目。
- Mentalis.org - Mentalis.org 包含一些很棒的 .NET 安全和网络项目。我将他们的 HMAC 和 Diffie Hellman 类用作我 SSH 实现的一部分。
- Granados - 一个全功能的开源 C# SSH1 和 SSH2 实现。我是在完成自己的库之后发现这个项目的。