在我的C#应用程序中,我必须通过https调用Web服务并使用我已经拥有的.crt文件进行验证。以下是满足此类需求的正确解决方案。我得到一个有效的解决方案后,我已经更新了这篇文章,认为它可能会帮助像我这样的人。
解决方案: 以下代码必须在整个应用程序执行中只执行一次。有了这个,我们设置ServerCertification和SSL属性,每当调用reqest时将使用它们:
public static void setSSLCertificate()
{
clientCert = new X509Certificate2(AUTHEN_CERT_FILE); // Pointing to the .crt file that will be used for server certificate verification by the client
System.Net.ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);
}
public static bool customXertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPoicyErrors)
{
switch (sslPoicyErrors)
{
case System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors:
case System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch:
case System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable:
break;
}
return clientCert.Verify(); // Perform the Verification and sends the result
}
请求通常像我们没有实现SSL那样完成。这是一个邮政请求代码:
private static String SendPost(String uri, String post_data)
{
String resData = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
// turn request string into byte[]
byte[] postBytes = Encoding.ASCII.GetBytes(post_data);
Stream requestStream = null;
try
{
// Send it
request.ContentLength = postBytes.Length;
requestStream = request.GetRequestStream();
requestStream.Write(postBytes, 0, postBytes.Length);
}
catch (WebException we)
{ // If SSL throws exception that will be handled here
if (we.Status == WebExceptionStatus.TrustFailure)
throw new Exception("Exception Sending Data POST : Fail to verify server " + we.Message);
}
catch (Exception e)
{
throw new Exception("Exception Sending Data POST : " + e.Message, e.InnerException);
}
finally
{
if (requestStream != null)
requestStream.Close();
}
// Get the response
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
if (response == null)
return "";
StreamReader sr = new StreamReader(response.GetResponseStream());
resData = sr.ReadToEnd().Trim();
sr.Close();
}
catch (Exception e)
{
throw new Exception("Error receiving response from POST : " + e.Message, e.InnerException);
}
finally
{
if (response != null)
response.Close();
}
return resData;
}
特别感谢Dipti Mehta,他的探索帮助我通过接受服务器证书来实现目标。她帮助我解决了我的困惑。我终于找到了如何使用客户端使用.crt文件验证服务器证书。
希望这有助于某人。
由于
答案 0 :(得分:11)
当您浏览到HTTPS站点时,您可能会看到一个对话窗口,询问您是否要信任Web服务器提供的证书。因此,接受证书的责任由用户处理。让我们回到webservice场景,如果你想调用位于使用SSL和HTTPS的web服务器上的web服务,就会出现问题。
当您通过代码拨打电话时,没有弹出对话框窗口,并询问您是否信任该证书;可能你会得到以下异常:
发生了'System.Net.WebException'类型的未处理异常 system.dll中
其他信息:基础连接已关闭:可以 不与远程服务器建立信任关系。
但是这个问题有一个解决方案,您可以通过创建自己的CertificatePolicy类(实现ICertificatePolicy
接口)在代码中解决此问题。在这个类中,您必须编写自己的CheckValidationResult函数,该函数必须返回true或false,就像在对话框窗口中按yes或no一样。出于开发目的,我创建了以下接受所有证书的类,因此您将不再获得令人讨厌的WebException:
public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
{
public TrustAllCertificatePolicy()
{}
public bool CheckValidationResult(ServicePoint sp, X509Certificate cert,WebRequest req, int problem)
{
return true;
}
}
正如您所看到的,CheckValidationResult函数始终返回true,因此所有证书都将受到信任。如果要使此类更安全一些,可以使用X509Certificate参数添加其他检查。要使用此CertificatePolicy,您必须告诉ServicePointManager使用它:
System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
在调用您的Web服务之前,必须完成此操作(在应用程序生命周期中一次)。
答案 1 :(得分:1)
嗨Tvd,
我不确定您提供的解决方案实际上是解决问题的有效方法。另外,您对HttpWebRequest.ClientCertificates
的一些评论表明了这一点。
首先,区分验证客户端证书的服务器和验证服务器证书的客户端非常重要。集合HttpWebRequest.ClientCertificates
用于将客户端证书发送到服务器,因此服务器可以验证客户端是谁。您的问题(据我所知)是如何通过本地存储在客户端的证书验证未通过默认验证的服务器证书(例如自签名证书)。
在这种情况下,解决方案确实是使用System.Net.ServicePointManager.ServerCertificateValidationCallback
并提供自定义验证。但是,您的验证方法似乎是错误的:它验证本地证书,而不关心服务器发送的证书。我使用的是这样的:
public static bool customXertificateValidation(
Object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
return clientCert.Equals(certificate);
};
此方法确保如果服务器证书通过默认验证(无错误),它将接受它,然后将证书的本地客户端副本与服务器提供的内容进行比较。只有,如果Equals测试通过,客户端可以安全地继续。
答案 2 :(得分:0)
通过“验证”你的意思是认证?在这种情况下,.crt是不够的,它只包含一个公钥。您需要私钥来验证自己并将其放入ClientCertificates。您可以从.pfx文件中读取一个文件或将其导入证书容器并从那里使用它。
答案 3 :(得分:0)
自从System.Net.ICertificatePolicy被弃用以来,我认为正确的方法应该是创建一个RemoteCertificateValidationCallback委托:
void Awake()
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateCertification;
}
void OnDestroy()
{
ServerCertificateValidationCallback = null;
}
public static bool ValidateCertification(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
print("VALIDATE!");
return true;
}