2010-06-23 9 views
13

Ho un client WCF che si connette a un servizio Web Axis2 basato su Java (fuori dal mio controllo). Sta per essere applicato a WS-Security e ho bisogno di riparare il client .NET. Tuttavia, sto lottando per fornire l'autenticazione corretta. Sono consapevole che WSE 3.0 potrebbe renderlo più semplice, ma preferirei non tornare a una tecnologia obsoleta.Errore nel client WCF che utilizza il servizio Web Axis 2 con WS-Security UsernameToken PasswordDigest authentication scheme

Problemi simili (non risolti), includere this, this e this.

Il messaggio SOAP dovrebbe simile a questa:

<wsse:UsernameToken> 
    <wsse:Username><!-- Removed--></wsse:Username> 
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"><!-- Removed--></wsse:Password> 
    <wsse:Nonce><!-- Removed--></wsse:Nonce> 
    <wssu:Created>2010-05-28T12:50:33.675+01:00</wssu:Created> 
</wsse:UsernameToken> 

Tuttavia, la mia assomiglia a questo:

<s:Header> 
<h:Security xmlns:h="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"></h:Security> 
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
<u:Timestamp u:Id="_0"> 
<u:Created>2010-06-23T10:31:23.441Z</u:Created> 
<u:Expires>2010-06-23T10:36:23.441Z</u:Expires> 
</u:Timestamp> 
<o:UsernameToken u:Id="uuid-d329b3b2-6a1f-4882-aea6-ec6b8a492de7-1"> 
<o:Username> 
<!-- Removed--> 
</o:Username> 
<o:Password> 
<!-- Removed--> 
</o:Password> 
</o:UsernameToken> 
</o:Security> 
</s:Header> 

Il mio cliente si presenta così: P.S. Nota: richiesto Parametro SecurityHeaderType. Cos'è quello?

public MyAck SendRequest(MyRequest request) 
{ 
RemoteServicePortTypeClient client = new RemoteServicePortTypeClient(); 

client.ClientCredentials.UserName.UserName = "JAY"; 
client.ClientCredentials.UserName.Password = "AND"; 

    // what is the difference between the two different Credential types?? 
    //client.ClientCredentials.HttpDigest.ClientCredential.UserName = "SILENT"; 
    //client.ClientCredentials.HttpDigest.ClientCredential.Password = "BOB"; 

SecurityHeaderType sht = new SecurityHeaderType(); 
//sht.Any = ???; // How do I use this??? 
//sht.AnyAttr = ???; // How do I use this ??? 

// SecurityHeaderType is a required parameter 
return client.RemoteServiceOperation_Provider(sht, request); 
} 

attuale legame è la seguente:

<basicHttpBinding> 
    <binding name="CustomBinding"> 
     <security mode="TransportWithMessageCredential"> 
      <transport clientCredentialType="None"></transport> 
      <message clientCredentialType="UserName" /> 
     </security> 
    </binding> 
</basicHttpBinding> 

Ho anche provato una consuetudine vincolante e ottenuto un errore simile:

<customBinding> 
    <binding name="myCustomBindingConfig"> 
    <security authenticationMode="UserNameOverTransport" 
     messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11" 
     securityHeaderLayout="Strict" 
     includeTimestamp="false"></security> 
    <textMessageEncoding messageVersion="Soap11"></textMessageEncoding> 
    <httpsTransport /> 
    </binding> 
</customBinding> 

E endpoint (Indirizzo ovviamente cambiato ...):

<endpoint address="https://www.somecompany.com/uat/axis/services/RemoteServiceOperation_Provider" 
     binding="basicHttpBinding" bindingConfiguration="CustomBinding" 
     contract="RemoteService.RemoteServicePortType" 
     name="RemoteService_UAT" /> 

Il c colpa ustom che viene restituito è il seguente:

<ErrorID>0</ErrorID> 
<ErrorType>UNEXPECTED</ErrorType> 
<ErrorDescription><![CDATA[Array index out of range: 0]]></ErrorDescription> 
<TimeStamp>2010-06-23T13:28:54Z</TimeStamp> 

Ho letto un sacco circa personalizzati intestazioni, gettoni, attacchi e il mio cervello è completamente confuso. Qualcuno può suggerire una procedura passo passo per l'invio del messaggio nel formato giusto?

This sembra essere la via da seguire per WCF, utilizzando token personalizzati, ma come si dovrebbe applicare il digest e il nonce come richiesto?

Qualsiasi aiuto accolto.

UPDATE

Ho avuto un successo limitato. Ho usato la libreria Microsoft.Web.Services3 per creare un nome utenteToken con il digest corretto.Ho quindi creato il mio comportamento personalizzato e nel metodo BeforeSendRequest che ho fatto quanto segue per iniettare l'intestazione:

object IClientMessageInspector.BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
{ 
    UsernameToken ut = new UsernameToken("USERNAME", "PASSWORD", PasswordOption.SendHashed); 

    XmlElement securityElement = ut.GetXml(new XmlDocument()); 

    MessageHeader myHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", securityElement, false); 
    request.Headers.Add(myHeader); 

    return Convert.DBNull; 
} 

aggiungo il comportamento in questo modo:

CustomBehavior behavior = new CustomBehavior("USERNAME", "PASSWORD"); 
client.Endpoint.Behaviors.Add(behavior); 

posso ora vedere le intestazioni attraversando:

<s:Header> 
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
<wsse:UsernameToken wsu:Id="SecurityToken-c6aeb72d-4d36-4650-abd3-33cc66caac6d" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> 
<wsse:Username> 
<!-- Removed--> 
</wsse:Username> 
<wsse:Password> 
<!-- Removed--> 
</wsse:Password> 
<wsse:Nonce> 
<!-- Removed--> 
</wsse:Nonce> 
<wsu:Created>2010-06-24T16:23:58Z</wsu:Created> 
</wsse:UsernameToken> 
</Security> 
</s:Header> 

Ma sto ottenendo l'errore:

<soapenv:Fault> 
<faultcode xmlns="">soapenv:Server</faultcode> 
<faultstring xmlns="">WSDoAllReceiver: security processing failed; nested exception is: 
    org.apache.ws.security.WSSecurityException: General security error (WSSecurityEngine: Callback supplied no password for: USERNAME)</faultstring> 
<faultactor xmlns="">urn:Remote_Provider</faultactor> 
<detail xmlns=""> 
<CUSTOMError xmlns="urn:customerror:v01"> 
<ErrorID>0</ErrorID> 
<ErrorType>UNEXPECTED</ErrorType> 
<ErrorDescription><![CDATA[WSDoAllReceiver: security processing failed; nested exception is: 
    org.apache.ws.security.WSSecurityException: General security error (WSSecurityEngine: Callback supplied no password for: USERNAME)]]></ErrorDescription> 
<TimeStamp>2010-06-24T17:23:59Z</TimeStamp> 
</CUSTOMError> 
</detail> 
</soapenv:Fault> 

Sembra che ci sia una mancanza Tipo attributo sul nodo password

Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest" 

Tuttavia, non sono sicuro se la sicurezza rintracciamento e impostazioni di registrazione sono coperta di rimuovere gli attributi e contenuto di tali nodi. Ho tentato di utilizzare l'impostazione logKnownPii nella registrazione diagnostica, ma le informazioni sulla sicurezza rimangono oscurate. Qualche idea su quello?

risposta

5

posso confermare che l'aggiornamento dalla mia domanda funziona realmente:

object IClientMessageInspector.BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
{ 
    UsernameToken ut = new UsernameToken("USERNAME", "PASSWORD", PasswordOption.SendHashed); 

    XmlElement securityElement = ut.GetXml(new XmlDocument()); 

    MessageHeader myHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", securityElement, false); 
    request.Headers.Add(myHeader); 

    return Convert.DBNull; 
} 

E il client:

CustomBehavior behavior = new CustomBehavior("USERNAME", "PASSWORD"); 
client.Endpoint.Behaviors.Add(behavior); 

Il messaggio di errore è correlato. L'intestazione di sicurezza funziona con un semplice basicHttpBinding:

<basicHttpBinding> 
    <binding name="BasicSOAPBinding"> 
     <security mode="Transport" /> 
    </binding> 
</basicHttpBinding> 

Esempio di codice di questo in azione può essere trovato sul mio blog: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/

+0

Purtroppo, questo appare come qualcosa che può scomparire in futuro. Le specifiche ONVIF ora dicono: "I servizi definiti in questo standard devono essere protetti utilizzando l'autenticazione digest secondo a [RFC 2617] ad eccezione dei dispositivi legacy che supportano [WS-UsernameToken]." fonte: https://www.onvif.org/specs/core/ONVIF-Core-Specification-v1706.pdf 5.12.1 – cube45

1

Ho avuto un problema simile di recente e ho rinunciato alla ricerca di una soluzione non WSE. Dopo un paio di giorni in cui ho tirato fuori i capelli, ho finito di scaricare l'SDK WSE 3.0, di generare una classe proxy usando WseWsdl3.exe e di creare una nuova politica per UsernameToken. Sono stato attivo e funzionante in 15 minuti. Al momento sta lavorando per me.

RemoteService service = new RemoteService(); //generated class 

UsernameToken token = new UsernameToken(username, password, PasswordOption.SendPlainText); 
Policy policy = new Policy(); 
policy.Assertions.Add(new UsernameOverTransportAssertion()); 

service.SetClientCredential(token); 
service.SetPolicy(policy); 

var result = service.MethodCall(); 
2

Questa domanda è ben scritto - molte grazie. In riferimento al commento "Come usare questo" di @ Junto, risulta che il parametro SecurityHeader sul metodo di servizio può essere utilizzato per aggiungere l'intestazione. Ho incluso un esempio qui sotto. Credo che quello che sta succedendo sia che lo strumento SvcUtil.exe stia bargando durante il tentativo di leggere i DTD WS *. Questo non è ovvio quando si utilizza la procedura guidata "Aggiungi riferimento servizio". Ma è molto ovvio quando si esegue svcutil.exe dalla riga di comando. Poiché svcutil.exe non riesce a leggere i DTD WS *, l'oggetto SecurityHeader non è ben sviluppato. Ma Microsoft ti offre un out con la proprietà .Any. Puoi serializzare la classe UsernameToken direttamente nella proprietà .Any e l'intestazione verrà aggiunta al messaggio. Ancora una volta, grazie per questa eccellente domanda.

Come utilizzare il parametro SecurityHeader per aggiungere un'intestazione UsernameToken di sicurezza:

Strumenti necessari:

Fiddler2 (or similar) -- you really can't figure any of this out without inspecting the http headers.

necessaria Riferimento: chiamata di servizio

Microsoft.Web.Services3.dll -- you can reference this 2.0 framework assembly from your 4.0 assembly 

WCF:

// Initialization of the service... 
_service = new MyService("MyEndpoint", RemoteUri); 

// etc.  

// Calling the service -- note call to GetSecurityHeader() 
_service.ServiceAction(GetSecurityHeader(), "myParam1"); 

// etc. 

/// <summary> 
/// Construct the WSE 3.0 Security Header 
/// </summary> 
private SecurityHeader GetSecurityHeader() 
{ 
    SecurityHeader h = new SecurityHeader(); 
    UsernameToken t = new UsernameToken(RemoteLogin, RemotePassword, PasswordOption.SendPlainText); 
    h.Any = new XmlElement[1]; 
    h.Any[0] = t.GetXml(new XmlDocument()); 
    return h; 
} 

App.config:

<system.serviceModel> 
    <bindings> 
     <basicHttpBinding> 
     <binding name="MyBinding" closeTimeout="00:01:00" openTimeout="00:01:00" 
      receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" 
      bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" 
      maxBufferSize="1048576" maxBufferPoolSize="524288" maxReceivedMessageSize="1048576" 
      messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" 
      useDefaultWebProxy="true"> 
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 
       maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
      <security mode="Transport"> 
      <transport clientCredentialType="None" proxyCredentialType="None" 
       realm="" /> 
      <message clientCredentialType="UserName" algorithmSuite="Default" /> 
      </security> 
     </binding> 
     </basicHttpBinding> 
    </bindings> 
    <client> 
     <endpoint address="http://myservice.com/service.asmx" 
      binding="basicHttpBinding" bindingConfiguration="MyBinding"    contract="MyContract" 
      name="MyEndpoint" /> 
    </client> 
    </system.serviceModel> 
+0

GetSecurityHeader() ha fatto per me, grazie! – Robotron