在我的c#应用程序中,我通过https调用web服务,并使用我已经拥有的.crt文件进行验证。以下是满足此类需求的正确解决方案。一旦我有了一个可行的解决方案,我就更新了这篇文章,认为它可能会帮助像我这样的人。
解决方案:下面的代码在整个应用程序执行过程中只需要执行一次。这样,我们就设置了ServerCertification和SSL属性,这些属性将在调用请求时使用:
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的情况下,请求像我们一样正常地完成。下面是Post请求代码:
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;
}
Special感谢Dipti Mehta,他的解释在很大程度上帮助我实现了接受服务器证书的目标。她帮我解决了我的困惑。我终于找到了如何通过客户端使用.crt文件验证服务器证书。
希望这能帮助到一些人。
谢谢
当您浏览到HTTPS站点时,您可能会看到一个对话框窗口,询问您是否要信任web服务器提供的证书。因此,接受证书的责任由用户处理。让我们回到web服务场景,如果您想调用位于使用SSL和HTTPS的web服务器上的web服务,就会出现问题。
当你从代码中调用时,没有弹出对话框窗口,询问你是否信任证书;可能你会得到以下异常:
System.Net类型的未处理异常。webeexception '发生在system.dll
附加信息:底层连接已关闭:Could未与远程服务器建立信任关系。
但是这个问题有一个解决方案,您可以通过创建自己的CertificatePolicy类(实现ICertificatePolicy
接口)在代码中解决这个问题。在这个类中,您必须编写自己的CheckValidationResult函数,该函数必须返回true或false,就像您在对话框窗口中按yes或no一样。出于开发目的,我创建了以下类,它接受所有证书,因此您将不会再得到讨厌的webeexception:
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服务之前完成(在应用程序生命周期中的一次)。
嗨Tvd,
我不确定你提供的解决方案是否是这个问题的有效解决方案。另外,你们关于HttpWebRequest.ClientCertificates
的一些评论也表明了这一点。
首先,区分验证客户端证书的服务器和验证服务器证书的客户端是很重要的。HttpWebRequest.ClientCertificates
Collection用于向服务器发送客户端证书,以便服务器验证客户端是谁。你的问题(据我所知)是如何通过默认验证的服务器证书(如自签名证书)可以根据本地存储在客户端的证书进行验证。
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);
};
此方法确保如果服务器证书通过默认验证(没有错误),它将接受该证书,然后将证书的本地客户端副本与服务器提供的证书进行比较。
你的"validate"是指认证吗?在这种情况下,.crt是不够的,它只包含一个公钥。您需要私钥来验证自己,并将其放入ClientCertificates中。您可以从.pfx文件中读取一个,也可以将其导入证书容器并从那里使用它。
由于system.net.icercertificatepolicy已弃用,我认为正确的方法应该是创建一个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;
}