我尝试使用SFTP将文件发送到某个服务器。在此过程中,我遇到异常
Renci.SshNet.Common.SshException:私钥文件无效。位于Renci.SshNet.PrivateKeyFile.Open(流私钥,字符串密码短语)
使用PuTTYgen生成密钥,下面显示的是私钥文件的示例格式。它同时具有公钥和私钥。
PuTTY-User-Key-File-2: ssh-rsa
Encryption:none
comment: rsa-key-20190327
Public-Lines: 4
AAAAB.....
......
Private-Lines: 8
AAAAgQ......
.......
Private-MAC: 54901783....
我从配置文件中的上述文件复制了私钥部分,并在代码中以SftpKey
的形式访问它。
获得了上述密钥的OpenSSH格式,如下所示
------BEGIN RSA PRIVATE KEY-----
MIIE....
.......
------END RSA PRIVATE KEY-------
我只从上面的文件中复制了关键部分,并复制到我的配置文件中,运行了我的代码。问题没有解决。
下面是我用于SFTP上传的代码
var fileLength = data.Length;
var keyStr = ConfigurationManager.ConnectionStrings["SftpKey"].ConnectionString;
using (var keystrm = new MemoryStream(Convert.FromBase64String(keyStr)))
{
var privateKey = new PrivateKeyFile(keystrm);
using (var ftp = new SftpClient(_ftpServer, _ftpUser, new[] { privateKey }))
{
ftp.ErrorOccurred += ErrorOccurred;
ftp.Connect();
ftp.ChangeDirectory(_ftpPath);
using (var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
{
ftp.UploadFile(dataStream, Path.GetFileName(message.MessageId), true,
(length) => result = fileLength == (int)length);
}
ftp.Disconnect();
}
}
代码有什么问题吗?或者可能是什么问题?任何帮助都非常感谢。
3条答案
按热度按时间ecr0jaav1#
由于这是该错误消息的首要答案,因此我认为有必要扩展您最初问题中的一个要点-转换为OpenSSH格式密钥。
Renci.SshNet不能使用以下列字符开头的PuTTY密钥:
您可以使用puttygen.exe转换为OpenSSH格式
1.在puttygen.exe中装入你的密钥文件
1.转换〉导出OpenSSH密钥(不是“强制使用新文件格式”选项)
这将生成一个以以下内容开头的密钥:
这会奏效的
4ngedf3f2#
我只复制了上面文件中的关键部分
你需要在
MemoryStream
中有完整的密钥文件。并且和文件中的完全一样(就像你在文本密钥文件中使用FileStream
一样)。所以没有Convert.FromBase64String
。实际上,在前面的代码中,我展示了一个成功发送文件的现有实现。
则连接字符串不包含您声明的内容。检查
PrivateKeyFile.Open
的实现。它显式检查流是否以---- BEGIN ... PRIVATE KEY
开头。如果不是,则抛出 “Invalid private key file "。实际上,将多行内容存储到连接字符串中可能是不可能的(或困难的)。如果您的代码曾经工作过,那一定是因为您的
SftpKey
连接字符串包含一个完整的密钥文件(包括BEGIN ... PRIVATE KEY
信封),但(再次)以Base64编码进行编码(作为一行)。如下所示:它会给予如下字符串:
zbdgwd5y3#
如果私钥的格式不正确(例如被压缩到一行中,从而与www.example.com上的正则表达式不匹配),也会发生相同的错误https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/PrivateKeyFile.cs#L156
私钥格式必须超过80列的多行。https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/PrivateKeyFile.cs#L68-表达式包含“{1,80}"。
在我的例子中,私钥没有密码保护,所以我能够使用下面的代码正确地重新格式化它;重新引入新行;在构造存储器流之前。