Autenticação de um pedido WCF via certificado de cliente via HTTPS

9

Eu tenho lutado com a configuração para este serviço WCF na semana passada, e estou desacelerando começando a suspeitar que o que estou tentando fazer não é possível, apesar da documentação.

Simplesmente, quero que um serviço WCF exija um certificado de cliente (que o servidor terá em seu armazenamento de certificados) e, em seguida, acesse essa identidade com System.ServiceModel.ServiceSecurityContext. Além disso, isso precisa usar a segurança de transporte.

Aqui está minha configuração do servidor:

<system.serviceModel>
    <services>
      <service behaviorConfiguration="requireCertificate" name="Server.CXPClient">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="PartnerComm.ContentXpert.Server.ICXPClient" />
        <endpoint address="mex" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="mexEndpoint" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="requireCertificate">
          <serviceMetadata httpsGetEnabled="true" />
          <serviceCredentials>
            <serviceCertificate findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
            <clientCertificate>
              <authentication certificateValidationMode="ChainTrust" trustedStoreLocation="LocalMachine" />
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding" maxBufferPoolSize="5242880" maxReceivedMessageSize="5242880">
          <readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="1073741824" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

Aqui está a configuração do meu cliente:

  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
          transactionFlow="false" hostNameComparisonMode="StrongWildcard"
          maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
          textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint" behaviorConfiguration="ClientCertificateBehavior"
        contract="ContentXPertServer.ICXPClient" name="wsHttpEndpoint" />
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
            <clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" />
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>

O código funciona perfeitamente quando o modo de segurança = 'None' é sobre http, mas é claro que não há autenticação e nada no System.ServiceModel.ServiceSecurityContext. Eu tentei dezenas de variações em todos esses elementos, e tudo acaba inevitavelmente com o pedido lançando uma exceção "Uma conexão existente foi forçosamente fechada pelo host remoto".

Estou usando um certificado auto-assinado "CyberdyneIndustries", cujo certificado de CA eu adicionei ao CA store confiável. O certificado é verificado quando eu o visualizo. Eu passei pelo inferno do gerenciamento de namespace http e resolvi esses problemas também. Parece simplesmente que o WCF não suporta isso ... por favor, me diga que estou errado.

TIA.

    
por Chris B. Behrens 04.12.2010 в 01:03

4 respostas

2
Em última análise, decidi tentar a segurança das mensagens, para ver se isso lançaria alguma luz sobre a situação - aconteceu, e vou cortar minhas perdas e ir com isso. Então, não há uma resposta definitiva para isso.

A implementação da segurança de mensagens, no entanto, expôs um grande problema, e isso pode ter sido a raiz do problema de segurança de transporte. Existe uma parte da documentação antivírus do MSDN:

link

Nesta página, o comando para criar o certificado autoassinado é o seguinte:

  

makecert -sk MyKeyName -iv   RootCaClientTest.pvk -n   "CN = tempClientcert" -ic   RootCaClientTest.cer -sr currentuser   -ss my -sky signature -pe

O argumento "signature" deve ser "exchange". Depois de regenerar todos os meus certificados, a segurança da mensagem começou a funcionar. Um grande detalhe de tudo isso é que, se você deseja implementar a segurança de transporte, faça a segurança da mensagem funcionar primeiro, porque as mensagens de erro obtidas do sistema são muito mais descritivas.

    
por Chris B. Behrens 07.12.2010 / 15:49
1

O handshake SSL é bem-sucedido? Ative o log de SChannel para solucionar problemas da camada SSL. Consulte este artigo da antiga base de conhecimento: Como habilitar o log de eventos do Schannel no IIS . Embora seja um KB para W2K e XP, as etapas para ativar o log de SChannel são as mesmas e ainda válidas em sistemas mais novos. Com o registro ativado, você poderá determinar por que o SSL está rejeitando o certificado.

    
por Remus Rusanu 07.12.2010 / 03:33
1

Eu sei que isso tem 3 anos, mas para aqueles que ainda podem estar interessados ...

Eu estou no processo de aprender WCF (segurança entre outras coisas) e consegui fazer as coisas funcionarem corretamente com netTcpBinding (presumivelmente, isso funcionará também para WsHttpBindings) usando o modo de segurança de transporte com um clientCredentialType="Certificate" (e, protectionLevel="EncryptAndSign", embora isso não fosse pertinente ao problema).

Eu também encontrei o erro de fechamento da conexão de força do lado do servidor, mas descobri que estava faltando uma parte da configuração. Está tudo funcionando agora.

Aqui está minha configuração do lado do servidor:

<configuration>
  <system.serviceModel>
    <services>
      <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
        <endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding" bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceBehavior">
          <serviceCredentials>
            <serviceCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            <clientCertificate>
              <authentication certificateValidationMode="PeerTrust"/>
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="TcpCertSecurity">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

E minha configuração do lado do cliente:

<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding"
          bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService"
          behaviorConfiguration="MyServiceBehavior">
        <identity>
          <dns value="MyServiceCert" />
        </identity>
      </endpoint>
    </client>
    <bindings>
      <netTcpBinding>
        <binding name="TcpCertSecurity">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="MyServiceBehavior">
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="PeerTrust" />
            </serviceCertificate>
            <clientCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName" />
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Eu criei uma cadeia de certificados para o servidor (certificado de Raiz Confiável auto-assinado + um certificado criado usando essa raiz) usando a técnica descrita aqui e armazenou o certificado Raiz e o certificado filho no armazenamento de certificados da máquina host do servidor. E, finalmente, importei esse certificado de servidor + chave pública para o armazenamento de certificados na máquina host do cliente (em LocalMachine / TrustedPeople).

    
por acscheiner 03.11.2014 / 23:33
0

WsHttpBinding suporta a autenticação de certificado para segurança de transporte.

Pode haver algumas coisas erradas:

  1. Você adicionou ambos certificados à sua loja? CyberdyneIndustries, bem como uma CA que você usou para assiná-lo? A CA deve estar em "Autoridades de Certificação Raiz Confiáveis"

  2. Além disso, fiz esse auto-hospedado, nunca no servidor Visual Studio Dev. Tente hospedar seu serviço no IIS, pelo menos. Não tenho certeza se o servidor VS Dev suporta certificados.

  3. Tente desativar a autenticação do serviço. Portanto, o cliente não precisa autenticar o serviço. Não sei se você quer isso em seu aplicativo ou não, mas apenas para testes, para que possamos descartar isso

    <behavior name="ClientCertificateBehavior">
    <clientCredentials>
        <clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" />
        <serviceCertificate>
            <authentication certificateValidationMode="None"/>
        </serviceCertificate>
    </clientCredentials>
    

por Vitalik 07.12.2010 / 02:32