2010-06-23 3 views
13

J'ai un client WCF se connectant à un service Web Axis2 basé sur Java (hors de mon contrôle). Il est sur le point d'appliquer WS-Security, et j'ai besoin de réparer le client .NET. Cependant, j'ai du mal à fournir l'authentification correcte. Je suis conscient que WSE 3.0 pourrait le rendre plus facile, mais je préférerais ne pas revenir à une technologie obsolète. Les problèmes similaires (non résolus) incluent this, this et this.Erreur dans le client WCF consommant le service Web Axis 2 avec le schéma d'authentification WS-Security UsernameToken PasswordDigest

Le message SOAP devrait ressembler à ceci:

<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> 

Cependant, le mien ressemble à ceci:

<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> 

Mon client ressemble à ceci: post-scriptum Notez le requis Paramètre SecurityHeaderType. Qu'est-ce que c'est?

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

actuel de liaison est la suivante:

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

J'ai aussi essayé une liaison personnalisée et a obtenu une erreur semblable:

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

et le point final (Adresse évidemment changé ...):

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

Le c faute ustom qui est retourné est la suivante:

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

J'ai lu beaucoup sur les en-têtes personnalisés, des jetons, des liaisons et mon cerveau est complètement confus. Quelqu'un peut-il suggérer un processus étape par étape pour envoyer le message dans le bon format?

This semble être la voie à suivre pour WCF, en utilisant des jetons personnalisés, mais comment devrait-on appliquer le condensé et nonce comme nécessaire?

Toute aide bienvenue.

MISE À JOUR

J'ai rencontré un succès limité. J'ai utilisé la bibliothèque Microsoft.Web.Services3 pour créer un nom d'utilisateur avec le résumé correct.Je l'ai ensuite créé mon propre comportement personnalisé et dans la méthode BeforeSendRequest je l'ai fait ce qui suit pour injecter l'en-tête:

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

ajouter le comportement comme ceci:

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

Je peux maintenant voir les en-têtes traversant:

<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> 

Mais j'obtiens l'erreur:

<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> 

Il semble y avoir un manque type attribut sur le nœud de mot de passe:

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

Cependant, je ne suis pas sûr que le tracé de la sécurité et les paramètres d'enregistrement sont une couverture supprimant les attributs et le contenu de ces nœuds. J'ai essayé d'utiliser le paramètre logKnownPii dans la journalisation des diagnostics, mais les informations de sécurité restent masquées. Des idées sur celui-là?

Répondre

5

Je peux confirmer que la mise à jour depuis ma question fonctionne réellement:

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

Et le client:

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

Le message d'erreur était sans rapport. L'en-tête de sécurité fonctionne avec une basicHttpBinding très simple:

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

de cela en action se trouve sur mon blog: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/

+0

Malheureusement, cela ressemble à quelque chose qui peut disparaître à l'avenir. La spécification ONVIF dit maintenant: "Les services définis dans cette norme doivent être protégés en utilisant l'authentification Digest selon à [RFC 2617] à l'exception des périphériques hérités supportant [WS-UsernameToken]." source: https://www.onvif.org/specs/core/ONVIF-Core-Specification-v1706.pdf 5.12.1 – cube45

1

J'ai eu un problème similaire récemment et j'ai arrêté de chercher une solution non-WSE. Après quelques jours d'arracher mes cheveux j'ai fini de télécharger le SDK WSE 3.0, en générant une classe proxy en utilisant WseWsdl3.exe, et en créant une nouvelle politique pour le nom d'utilisateur. J'étais opérationnel en 15 minutes. Ce qui suit travaille actuellement pour moi.

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

Cette question est bien écrit - merci beaucoup. En référence au commentaire "Comment utiliser ceci" de @ Junto, il s'avère que le paramètre SecurityHeader de la méthode de service peut être utilisé pour ajouter l'en-tête. J'ai inclus un exemple ci-dessous. Je crois que ce qui se passe est que l'outil SvcUtil.exe est barfing en essayant de lire les DTD WS *. Ce n'est pas évident lorsque vous utilisez l'assistant "Ajouter une référence de service". Mais il est très évident lorsque vous exécutez svcutil.exe à partir de la ligne de commande. Parce que svcutil.exe ne parvient pas à lire les DTD WS *, l'objet SecurityHeader n'est pas bien développé. Mais Microsoft vous donne une sortie avec la propriété .Any. Vous pouvez sérialiser la classe UsernameToken directement dans la propriété .Any et votre en-tête sera ajouté au message. Encore une fois, merci pour cette excellente question.

Comment utiliser le paramètre SecurityHeader pour ajouter un en-tête de sécurité UsernameToken:

Outils nécessaires:

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

requis Référence:

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

WCF appel de service:

// 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() l'a fait pour moi, merci! – Robotron

Questions connexes