我正试图从我的FTP服务器下载文件,但从我的生产服务器(从我的笔记本电脑,我能够下载该文件)我得到错误"对SSPI的调用失败了";以及内部异常"收到的消息出乎意料或格式错误"。这是我的代码:
var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\temp\";
var downloadedFileName = "file.xls";
FtpWebRequest request = null;
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
return true;
};
// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");
// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
request.EnableSsl = true;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse(); // <--- ERROR
Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);
SSPI是Windows组件。如果你用的是Windows 2008,那就意味着它很老了。
错误"收到的消息出乎意料或格式错误"。似乎表明服务器证书使用不支持的格式(例如,使用未知的密码),因此不能被SSPI层处理。
你有几个解决方案:
- 升级Windows
- 降级服务器证书
- 不要使用SSL
- 使用不依赖于SSPI的替代FTP库
要在。net框架内使用SSL证书,我们需要同时提供证书和相应的私钥。要实现这一点,我们需要使用p12(.pfx)文件,它结合了这两个。在我的项目中,我使用OpenSSL使用自签名证书,所以我使用下面的命令将证书和私钥组合在一起
pkcs12 -export -out ca.pfx -inkey ca.key -in ca.crt
pkcs12 -export -out client.pfx -inkey client.key -in client.crt
将为每个证书创建p12(.pfx)文件。然后在代码中使用它们,如下所示:
见下文,查找更多信息
参考
也许这个链接对你也有帮助。
好运根据前面的建议,您的代码应该是这样的:
var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\temp\";
var downloadedFileName = "file.xls";
var certName = "MyCertName" //Use yours
var password = "MyCertPassword"; //Use yours
FtpWebRequest request = null;
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
return true;
};
//Load certificates (one or more)
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");
// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
request.ClientCertificates = certificates; //Use certificates
request.EnableSsl = true;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse(); // <--- ERROR
Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);
注意:我没有测试解决方案,因为我没有一个环境来测试它
TLS握手失败,因为两个服务器无法就公共密码套件达成一致。
IIS Crypto在web应用的服务器上启用额外的密码套件。下载并运行IIS Crypto,在其"密码套件"选项卡上勾选"其他密码套件",然后重新启动计算机。
如果诊断失败,我建议在机器上安装Wireshark和你的。net Core应用。如果再次发生TLS握手失败,则发送如下消息:Alert (Level: Fatal, Description: Handshake Failure)有些密码套件比其他密码套件更安全,所以检查ref link可能有助于解决您的问题。
临时解决方案:
使用Windows注册表设置通道的最小键长度:
[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSecurityProvidersSCHANNELKeyExchangeAlgorithmsDiffie-Hellman]
"ClientMinKeyBitLength"=dword:00000200
Ref: Link1, link2, link3,Link4