在我的WCF自托管WebService使用相互证书来验证客户端,我设置了CertificateValidationMode = PeerTrust
但它似乎被忽略了,因为我仍然可以执行某些客户端的方法,我已经删除了相应的证书TrustedPeople
服务器商店。
主持人示例:
static void Main()
{
var httpsUri = new Uri("https://192.168.0.57:xxx/HelloServer");
var binding = new WSHttpBinding
{
Security =
{
Mode = SecurityMode.Transport,
Transport = {ClientCredentialType = HttpClientCredentialType.Certificate}
};
var host = new ServiceHost(typeof(HelloWorld), httpsUri);
//This line is not working
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =X509CertificateValidationMode.PeerTrust;
host.AddServiceEndpoint(typeof(IHelloWorld), binding, string.Empty, httpsUri);
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"server.com");
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
客户端应用:
static void Main(string[] args)
{
try
{
var c = new HelloWorld.HelloWorldClient();
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => true;
c.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"client.com");
Console.WriteLine(c.GetIp());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
我使用 RootCA 证书生成 server.com 和 client.com 。此RootCA证书在客户端和服务器的受信任根存储上进行。
问题是,如果我的 client.com 证书不在服务器的 TrustedPeople 存储中,我不应该执行GetIp()
方法,对吗?但我执行它没有任何问题。
问题是,在这种情况下,如何验证客户端证书将其公钥置于服务器的TrustedPeople上?
ps:在this MSDN带有客户端证书的传输安全性文章中,引用了一句话The server’s certificate must be trusted by the client and the client’s certificate must be trusted by the server.
但我可以从客户端执行web方法,即使客户端证书不在服务器TrustedPeople存储中。
答案 0 :(得分:2)
我的建议是使用custom validation。通过这种方式,您可以设置一些断点并观察验证,并根据整个验证过程中可用的数据查看您可以提出的其他验证选项。
首先确保您的绑定需要消息客户端凭据的证书。如果您仅使用证书进行传输,则我的测试中的客户端不会进行验证。仅此一项就可以解决您的问题。
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType =
MessageCredentialType.Certificate;
要设置自定义验证器,请按照其余部分进行操作。
替换:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.PeerTrust;
使用:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.Custom;
host.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new IssuerNameCertValidator("CN=client.com");
然后添加此项以创建自定义验证器并根据需要进行调整(此验证基于Issuer验证):
public class IssuerNameCertValidator : X509CertificateValidator
{
string allowedIssuerName;
public IssuerNameCertValidator(string allowedIssuerName)
{
if (allowedIssuerName == null)
{
throw new ArgumentNullException("allowedIssuerName");
}
this.allowedIssuerName = allowedIssuerName;
}
public override void Validate(X509Certificate2 certificate)
{
// Check that there is a certificate.
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
// Check that the certificate issuer matches the configured issuer.
if (allowedIssuerName != certificate.IssuerName.Name)
{
throw new SecurityTokenValidationException
("Certificate was not issued by a trusted issuer");
}
}
}