带证书身份验证的WCF服务:“调用者未通过服务进行身份验证”

时间:2017-10-16 16:07:53

标签: c# wcf authentication certificate

我正在尝试按照本指南设置基于证书的身份验证的WCF服务:https://msdn.microsoft.com/en-au/library/ff648360.aspx?f=255&MSPPError=-2147217396

我已经遵循了所有步骤(我认为),总结如下:
1)生成自签名CA证书并将其安装为计算机级可信CA:

enter image description here

2)为服务生成证书(由CA证书CN=QvxServiceCert签名)并在机器级别安装:

enter image description here

3)配置WCF服务端点行为以使用证书。我的服务配置如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>

  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding">
          <security>
            <message clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="QvxServiceBehavior">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <serviceCertificate findValue="CN=QvxServiceCert" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="esqQvxScheduler.Service.QvxSchedulerAPI" behaviorConfiguration="QvxServiceBehavior">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding"
          name="wsHttpEndpoint" bindingName="" contract="esqQvxScheduler.Service.IQvxSchedulerAPI" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/QvxSchedulerAPI/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

4)为客户端生成证书(再次由CA证书签名,此证书具有CN=QvxClientCert)并在用户级别安装:

enter image description here

5)配置WCF客户端行为以将其用于身份验证。这是我的客户端配置:

    <?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <system.serviceModel>
        <behaviors>
            <endpointBehaviors>
                <behavior name="qvxClientBehavior">
                    <clientCredentials>
                        <clientCertificate findValue="CN=QvxClientCert" />
                    </clientCredentials>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <bindings>
            <wsHttpBinding>
                <binding name="wsHttpEndpoint">
                    <security>
                        <message clientCredentialType="Certificate" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8733/QvxSchedulerAPI/" binding="wsHttpBinding" behaviorConfiguration="qvxClientBehavior"
                bindingConfiguration="wsHttpEndpoint" contract="QvxSchedulerAPI.IQvxSchedulerAPI"
                name="wsHttpEndpoint">
                <identity>
                    <certificate encodedValue="[A LONG AUTOGENERATED STRING THE MEANING OF WHICH I HAVE NO IDEA]" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

6)通过在命令行上发出以下命令,让运行客户端访问证书的用户:cacls "C:\Users\MyUsername\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-742627442-1779984360-2302642487-1000" /E /G "MyUser-PC\MyUsername":R

这一切都是按照指南完成的,没有任何我能看到的问题。一切似乎都没问题......但是当我尝试从我的客户那里调用服务时,我得到了这个令人沮丧的模糊和无益的例外:

Unhandled Exception: System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
   at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
   --- End of inner exception stack trace ---

Server stack trace:
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.TlsnegoTokenProvider.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
   at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

1 个答案:

答案 0 :(得分:1)

好吧,经过大量的试验和错误,结果证明您必须为您的CA安装CRL,或者您必须明确指定您不需要撤销检查,方法是将此添加到您的行为的ClientCredentials标记:

<serviceCertificate>
    <authentication revocationMode="NoCheck"/>
</serviceCertificate>

,反之亦然,服务方面:

<clientCertificate>
    <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
</clientCertificate>

此外,您需要删除客户端中的自动生成的证书标记,并将其替换为查找证书的说明:

<identity>
    <certificateReference findValue="CN=QvxServiceCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
</identity>