2015-07-03 36 views
21

Sto lavorando a un servizio WCF che deve essere utilizzato da un client non sviluppato da me e non è .NET (probabilmente Java).Autenticazione SSL reciproca con WCF: no CertificateRequest e CertificateVerify nella fase di handshake

In ogni caso, il servizio deve supportare l'autenticazione SSL reciproca, dove sia il servizio che il client autenticano con certificati X.509 certificati al livello di trasporto. I certificati sono stati scambiati tra le parti in un momento precedente.

Il mio problema è che non riesco a ottenere la corretta configurazione di WCF in modo tale che l'autenticazione del certificato client funzioni correttamente. Quello che mi aspetto è che, come parte del handshake TLS, il server include anche un Certificate Request, come si vede qui sotto:

enter image description here

A seguito di questo, il client dovrebbe rispondere con un `certificato Verificare' tra l'altro:

enter image description here

L'(ultimo) di configurazione del servizio è questo. Sto utilizzando un'associazione personalizzata, con la modalità di autenticazione impostata su MutualSslNegotiated.

<bindings> 
    <customBinding> 
    <binding name="CarShareSecureHttpBindingCustom"> 
     <textMessageEncoding messageVersion="Soap11" /> 
     <security authenticationMode="MutualSslNegotiated"/> 
     <httpsTransport requireClientCertificate="true" /> 
    </binding> 
    </customBinding> 
</bindings> 

... 

<serviceBehaviors> 
    <behavior name="ServiceBehavior"> 
    <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" /> 
    <serviceDebug includeExceptionDetailInFaults="false" httpHelpPageEnabled="false" /> 
    <serviceCredentials> 
     <serviceCertificate findValue="..." storeLocation="LocalMachine" x509FindType="FindByIssuerName" storeName="My" /> 
     <clientCertificate> 
     <certificate findValue="..." storeName="My" storeLocation="LocalMachine" x509FindType="FindByIssuerName"/> 
     </clientCertificate> 
    </serviceCredentials> 
    </behavior> 
</serviceBehaviors> 

Il Server Ciao parte della stretta di mano si presenta così per tutte le configurazioni di servizio che ho provato, senza CertificateRequest. enter image description here

Altre cose che debbono essere indicati:

  • Il servizio è self hosted e l'ascolto su una porta non predefinita (non 443). Il certificato SSL del server è stato associato a questa porta.
  • Ho anche provato uno basicHttpBinding e uno wsHttpBidning con la modalità di sicurezza impostata su Transport e l'autenticazione client impostata su Certificate, senza risultati (gli stessi risultati effettivamente).

Qualsiasi idea sarebbe apprezzata.

+0

hai cercato di abilitare WCF tracciare e vedere se ci sono errori dettagliate in là? – UserControl

+0

@UserControl: Sì, la traccia è abilitata ma non ci sono purtroppo errori correlati. –

+0

Wild shot - Qualche possibilità che ci siano alcuni certificati di root dalla catena di fiducia del cert del client mancante sul lato server? Cioè, hai il certificato del cliente, ma mancano alcuni certificati di root nella sua catena ...? –

risposta

11

OK, dopo qualche altro tentativo l'ho capito. Pubblicando questo nel caso in cui altri si imbattono nello stesso problema.

Devo continuare dicendo che questo comportamento deve essere menzionato da qualche parte su MSDN, in una posizione che è veramente visibile per chiunque sia alla ricerca di informazioni di sicurezza WCF e non sepolto in profondità nella documentazione di alcuni strumenti.

Le piattaforme in cui sono stato in grado di riprodurre e risolvere questo problema: Windows 8.1 x64 e Windows Server 2008 R2 Standard.

Come ho detto, il mio problema era che non potevo configurare la sicurezza WCF in modo tale che il servizio richiedesse certificati client. Una comune confusione che ho notato mentre cercavo una soluzione è che molte persone credono che il client possa inviare il certificato, se così fosse, senza essere contestato. Questo, naturalmente, non è il caso: il server deve prima chiedercelo e, inoltre, specificare quali CA sono consentite through a CertificateRequest reply.

Per riassumere, la mia situazione era:

  • Il servizio è self-hosted.
  • Il servizio viene eseguito su HTTPS, su una porta non standard (non 443 ma 9000).

Ciò significava che dovevo creare un bind certificato SSL per la porta 9000 utilizzando netsh.exe http add sslcert. Bene, il legame era stato creato ma c'era un problema. Ho trovato solo la questione dopo l'esecuzione netsh http show sslcert solo per controllare il mio legame:

IP:port      : 0.0.0.0:9000 
Certificate Hash    : ... 
Application ID    : ... 
Certificate Store Name  : MY 
Verify Client Certificate Revocation : Enabled 
Verify Revocation Using Cached Client Certificate Only : Disabled 
Usage Check     : Enabled 
Revocation Freshness Time : 0 
URL Retrieval Timeout  : 0 
Ctl Identifier    : (null) 
Ctl Store Name    : (null) 
DS Mapper Usage    : Disabled 
-->Negotiate Client Certificate : Disabled 

il colpevole era l'ultima proprietà del legame, "Negotiate Certificato client", documentata here. Apparentemente, per impostazione predefinita, questa proprietà è disabilitata. È necessario attivarlo esplicitamente durante la creazione del binding.

Ricreare vincolante con la dichiarazione seguente risolto il problema:

netsh.exe http add sslcert ipport=0.0.0.0:9000 certhash=... appid=... certstorename=MY verifyclientcertrevocation=Enable VerifyRevocationWithCachedClientCertOnly=Disable UsageCheck=Enable clientcertnegotiation=Enable 

Prima di controllare gli attacchi che ho provato che ospita un semplice servizio WCF in IIS e abilitare l'autenticazione dei certificati client da lì. È stato molto curioso vedere che sebbene non ci sia stato CertificateRequest rilasciato da IIS, non è riuscito ancora con un 403.7. Anche IIS non ha creato l'associazione con i parametri appropriati.

In ogni caso, ora funziona e questo è il modo in cui è possibile risolvere il problema.

non dimenticare, la configurazione del servizio cambiato così (la sicurezza vincolante) in modo da consentire la negoziazione certificato:

<customBinding> 
    <binding name="CustomHttpBindingCustom" receiveTimeout="01:00:00"> 
    <textMessageEncoding messageVersion="Soap11" /> 
    <security authenticationMode="SecureConversation" requireSecurityContextCancellation="true"> 
     <secureConversationBootstrap allowInsecureTransport="false" authenticationMode="MutualSslNegotiated" requireSecurityContextCancellation="true"></secureConversationBootstrap> 
    </security> 
    <httpsTransport requireClientCertificate="true" /> 
    </binding> 
</customBinding>