简体   繁体   English

使用 WS-Security UsernameToken PasswordDigest 身份验证方案使用 Axis 2 Web 服务的 WCF 客户端出错

[英]Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme

I have a WCF client connecting to a Java based Axis2 web service (outside my control).我有一个 WCF 客户端连接到基于 Java 的 Axis2 Web 服务(在我的控制之外)。 It is about to have WS-Security applied to it, and I need to fix the .NET client.它即将应用 WS-Security,我需要修复 .NET 客户端。 However, I am struggling to provide the correct authentication.但是,我正在努力提供正确的身份验证。 I am aware that WSE 3.0 might make it easier, but I would prefer not to revert to an obsolete technology.我知道 WSE 3.0 可能会使它更容易,但我不想恢复到过时的技术。

Similar issues (unsolved), include this , this and this .类似的问题(未解决),包括thisthisthis

The SOAP message should look like this: SOAP 消息如下所示:

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

However, mine looks like this:但是,我的看起来像这样:

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

My client looks like this: PS Note the required SecurityHeaderType param.我的客户端看起来像这样: PS 注意所需的SecurityHeaderType 参数。 What is that?那是什么?

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

Current binding is as follows:当前绑定如下:

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

I've also tried a custom binding and got a similar error:我还尝试了自定义绑定并得到了类似的错误:

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

And endpoint (Address obviously changed...):和端点(地址显然改变了......):

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

The custom fault that is being returned is as follows:正在返回的自定义故障如下:

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

I've read lots about custom headers, tokens, bindings and my brain is completely confused.我已经阅读了很多关于自定义标头、标记、绑定的内容,但我的大脑完全糊涂了。 Can anyone suggest a step by step process for sending the message in the right format?任何人都可以建议以正确格式发送消息的分步过程吗?

This appears to be the way forward for WCF, using custom tokens, but how should one apply the digest and nonce as required?似乎是 WCF 的前进方向,使用自定义令牌,但是应该如何根据需要应用摘要和随机数?

Any help welcomed.欢迎任何帮助。

UPDATE更新

I've had some limited success.我取得了一些有限的成功。 I've used the Microsoft.Web.Services3 library to create a UsernameToken with the correct digest.我使用 Microsoft.Web.Services3 库创建了一个具有正确摘要的 UsernameToken。 I've then created my own custom behavior and in the BeforeSendRequest method I've done the following to inject the header:然后,我创建了自己的自定义行为,并在 BeforeSendRequest 方法中执行了以下操作以注入标头:

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

I add the behavior like so:我添加这样的行为:

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

I can now see the headers going across:我现在可以看到标题:

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

But I'm getting the error:但我收到错误:

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

There appears to be a missing Type attribute on the password node:密码节点上似乎缺少Type属性:

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

However, I'm not sure if the security tracing and logging settings are blanket removing the attributes and content of those nodes.但是,我不确定安全跟踪和日志记录设置是否全面删除了这些节点的属性和内容。 I've attempted to use the logKnownPii setting in the diagnostics logging, but the security information remains obscured.我尝试在诊断日志中使用logKnownPii设置,但安全信息仍然模糊不清。 Any ideas on that one?对那个有什么想法吗?

I can confirm that the UPDATE from my question actually works:我可以确认我的问题中的 UPDATE 确实有效:

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

And the client:和客户:

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

The error message was unrelated.错误消息是无关的。 The security header works with a very simple basicHttpBinding:安全标头与一个非常简单的 basicHttpBinding 配合使用:

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

This question is well written -- many thanks.这个问题写得很好 - 非常感谢。 In reference to @Junto's "How do I use this" comment, it turns out that the SecurityHeader param on the service method can be used to add the header.参考@Junto 的“我如何使用这个”评论,事实证明服务方法上的 SecurityHeader 参数可用于添加标头。 I've included an example below.我在下面包含了一个例子。 I believe that what's happening is that the SvcUtil.exe tool is barfing when trying to read the WS* DTDs.我相信正在发生的事情是 SvcUtil.exe 工具在尝试读取 WS* DTD 时出现异常。 This is not obvious when you use the "Add Service Reference" wizard.当您使用“添加服务引用”向导时,这并不明显。 But it is very obvious when you run svcutil.exe from the command line.但是当您从命令行运行 svcutil.exe 时,这一点非常明显。 Because svcutil.exe fails to read the WS* DTD's, the SecurityHeader object is not well developed.由于 svcutil.exe 无法读取 WS* DTD,因此没有很好地开发 SecurityHeader 对象。 But Microsoft gives you an out with the .Any property.但是 Microsoft 为您提供了 .Any 属性。 You can serialize the UsernameToken class right into the .Any property and your header will be added to the message.您可以将 UsernameToken 类直接序列化到 .Any 属性中,您的标头将添加到消息中。 Again, thanks for this excellent question.再次感谢您提出的这个好问题。

How to use the SecurityHeader parameter to add a UsernameToken security header:如何使用 SecurityHeader 参数添加 UsernameToken 安全标头:

Required tools:所需工具:

Fiddler2 (or similar) -- you really can't figure any of this out without inspecting the http headers. Fiddler2(或类似的)——如果不检查 http 标头,你真的无法弄清楚这些。

Required Reference:所需参考:

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

WCF service call: 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>

I had a similar problem recently and gave up searching for a non-WSE solution.我最近遇到了类似的问题,并放弃了寻找非 WSE 解决方案。 After a couple days of pulling my hair out I ended downloading the WSE 3.0 SDK, generating a proxy class using WseWsdl3.exe, and creating a new policy for the UsernameToken.几天后,我终于下载了 WSE 3.0 SDK,使用 WseWsdl3.exe 生成了一个代理类,并为 UsernameToken 创建了一个新策略。 I was up and running in 15min.我在 15 分钟内启动并运行。 The following is currently working for 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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM