证书为ClientCredentials的Wcf

本文关键字:Wcf ClientCredentials 证书 | 更新日期: 2023-09-27 18:20:49

在我的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.comclient.com。此RootCA证书安装在客户端和服务器的受信任根存储中。问题是,如果我的client.com证书不在服务器的TrustedPeople存储中,我就不应该执行GetIp()方法,对吗?但我执行它没有任何问题。

问题是,在这种情况下,如何验证客户端证书,并将其公钥放在服务器的TrustedPeople上?

ps:在这篇关于客户端证书的传输安全性的MSDN文章中,有一句话是The server’s certificate must be trusted by the client and the client’s certificate must be trusted by the server.,但即使客户端证书不在服务器TrustedPeople存储中,我也可以从客户端执行Web方法。

证书为ClientCredentials的Wcf

我的建议是使用自定义验证。通过这种方式,您可以设置一些断点,观察验证的进行,并查看您可以根据整个验证过程中可用的数据想出哪些其他验证选项。

首先确保您的绑定需要消息客户端凭据的证书。如果您只使用证书进行传输,则我的测试中的客户端没有进行验证。仅此一点就可以解决您的问题。

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");
        }
    }
}