WCF:将 Nonce 添加到 UsernameToken

Posted

技术标签:

【中文标题】WCF:将 Nonce 添加到 UsernameToken【英文标题】:WCF: Adding Nonce to UsernameToken 【发布时间】:2010-10-28 04:03:23 【问题描述】:

我正在尝试连接到一个用 Java 编写的 Web 服务,但有些东西我想不通。

使用 WCF 和 customBinding,除了 SOAP 消息的一部分之外,几乎一切似乎都很好,因为它缺少 Nonce 和 Created 部分节点。 显然我错过了一些东西,所以如果你能指出我正确的方向,那将不胜感激。

这是自定义绑定:

<binding name="CustomHTTPBinding">
    <security includeTimestamp="false" authenticationMode="UserNameOverTransport" defaultAlgorithmSuite="Basic256" requireDerivedKeys="True"
              messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
    </security>
    <textMessageEncoding maxReadPoolSize="211" maxWritePoolSize="2132" messageVersion="Soap11"
                         writeEncoding="utf-8"/>
    <httpsTransport />
</binding>

这是消息的相关部分:

<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <o:UsernameToken u:Id="uuid-c306efd1-e84c-410e-a2ad-1046b368582e-1">
        <o:Username>
            <!-- Removed-->
        </o:Username>
        <o:Password>
            <!-- Removed-->
        </o:Password>
    </o:UsernameToken>
</o:Security>

它应该是这样的:

<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
 <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-25763165">
    <wsse:Username>..</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">..</wsse:Password>
    <wsse:Nonce>6ApOnLn5Aq9KSH46pzzcZA==</wsse:Nonce>
    <wsu:Created>2009-05-13T18:59:23.309Z</wsu:Created>
 </wsse:UsernameToken>
</wsse:Security>

所以问题是:如何在安全部分中引入 Nonce 和 Created 元素?

【问题讨论】:

您找到解决方案了吗?我很想知道。 最后我们使用 WSE 2 来解决我们遇到的问题,而不是 WCF。在那里,我们添加了一个自定义策略以将 UsernameToken 应用于服务请求,我认为就是这样。 【参考方案1】:

要创建随机数,我必须更改一些内容

首先,在我的配置中添加自定义绑定

<system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="myCustomBindingConfig">
          <security includeTimestamp="false" 
                    authenticationMode="UserNameOverTransport" 
                    defaultAlgorithmSuite="Basic256" 
                    requireDerivedKeys="true"
                    messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
          </security>
          <textMessageEncoding messageVersion="Soap11"></textMessageEncoding>
          <httpsTransport maxReceivedMessageSize="2000000000" />
        </binding>
      </customBinding>
    </bindings>
</system.serviceModel>

<client>
    <endpoint address="https://..." [other tags] 
        binding="customBinding" bindingConfiguration="OrangeLeapCustomBindingConfig"/>
</client>

然后,获取这里的代码:http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4df3354f-0627-42d9-b5fb-6e880b60f8ee 并修改它以创建随机数(只是一个随机哈希,base-64 编码)

protected override void WriteTokenCore(System.Xml.XmlWriter writer, System.IdentityModel.Tokens.SecurityToken token)

    Random r = new Random();
    string tokennamespace = "o";
    DateTime created = DateTime.Now;
    string createdStr = created.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
    string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(SHA1Encrypt(created + r.Next().ToString())));
    System.IdentityModel.Tokens.UserNameSecurityToken unToken = (System.IdentityModel.Tokens.UserNameSecurityToken)token;
    writer.WriteRaw(String.Format(
    "<0:UsernameToken u:Id=\"" + token.Id + "\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
    "<0:Username>" + unToken.UserName + "</0:Username>" +
    "<0:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" +
    unToken.Password + "</0:Password>" +
    "<0:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
    nonce + "</0:Nonce>" +
    "<u:Created>" + createdStr + "</u:Created></0:UsernameToken>", tokennamespace));


protected String ByteArrayToString(byte[] inputArray)

    StringBuilder output = new StringBuilder("");
    for (int i = 0; i < inputArray.Length; i++)
    
    output.Append(inputArray[i].ToString("X2"));
    
    return output.ToString();

protected String SHA1Encrypt(String phrase)

    UTF8Encoding encoder = new UTF8Encoding();
    SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
    byte[] hashedDataBytes = sha1Hasher.ComputeHash(encoder.GetBytes(phrase));
    return ByteArrayToString(hashedDataBytes);

【讨论】:

感谢您的回答。我暂时没有资格试一试,但看起来还可以,所以我会接受。 可能是一个旧答案,但它可能解决了我遇到的一个问题,与 Java 商店的网络服务交谈!谢谢!缺少的部分是在链接的 Microsoft 页面中,添加自定义行为后,必须设置用户名和密码(在 service.ClientCredentials.UserName 字段中) @JohnT:我想我错过了那一个缺失的部分。我正在执行以下操作:mhsClient.ChannelFactory.Endpoint.Behaviors.Remove&lt;ClientCredentials&gt;(); mhsClient.ChannelFactory.Endpoint.Behaviors.Add(new CustomCredentials()); mhsClient.ClientCredentials.UserName.UserName = "username"; mhsClient.ClientCredentials.UserName.Password = "password";,我收到以下错误:"Text cannot be written outside the root element" 我正在使用的服务没有抱怨丢失随机数,所以我把它排除在外。我已经描述了我的工作细节here。 @IsaacKleinman:啊。不幸的是,我需要它,但感谢您的文章!老实说,我没想到会有任何回应,而且绝对不会这么快:D【参考方案2】:

我遇到了同样的问题。我使用MessageInspectorBeforeSendRequest 方法中添加正确的UsernameToken,而不是自定义令牌序列化程序。然后我使用自定义行为来应用修复。

整个过程记录在我的博文Supporting the WS-I Basic Profile Password Digest in a WCF client proxy 中(带有demo project)。或者,您可以阅读PDF。

如果您想了解我的解决方案进度,您可以在 *** 上找到标题为“Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme”的解决方案:

【讨论】:

谢谢你,你拯救了我的一天。 @Sebacote 很高兴为您提供帮助。开始工作很痛苦,所以我想我会分享我的经验。【参考方案3】:

值得指出的是,Rick Strahl 发表了一篇博文(他引用了这个问题),他非常清楚地解释了这一切,并为 Password 和 PasswordDigest 提供了解决方案。

我发布这篇文章是因为我最初发现这篇文章,无法真正关注它,并且在很久以后才找到 Rick 的帖子。这可能会为一些人节省一些时间。

WCF WSSecurity and WSE Nonce Authentication

【讨论】:

以上答案为 SOAP 初学者遗漏了几个步骤,本文给出了逐步解决此问题的方法并解释了沿途的每一步。 这正是我想要的。此处接受的答案并未说明将代码放在何处,而是指向 Rick Strahl 的博文。赞成这个答案。【参考方案4】:

我还必须在 SOAP 消息头中放置一个 UserNameHeader 段:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:bar:services" xmlns:efm="urn:bar:services">
   <soapenv:Header>
       <efm:UserNameHeader>
           <UserName>foouser</UserName>
           <Password>foopass</Password>
       </efm:UserNameHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:GetUserList/>
   </soapenv:Body>
</soapenv:Envelope>

这是通过自定义消息头完成的:

public class UserNamePasswordHeader : MessageHeader

    private readonly string _serviceUserEmail;
    private readonly string _serviceUserPassword;

    public UserNamePasswordHeader(string serviceUserEmail, string serviceUserPassword)
    
        this._serviceUserEmail = serviceUserEmail;
        this._serviceUserPassword = serviceUserPassword;
    

    public override string Name
    
        get  return "UserNameHeader"; 
    

    public override string Namespace
    
        get  return "urn:bar:services"; 
    

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    
        writer.WriteElementString("UserName", _serviceUserEmail);
        writer.WriteElementString("Password", _serviceUserPassword);
    

其他标签,例如NonceCreated,可以很容易地添加。

类使用如下:

var service = new BarServiceClient();
service.ClientCredentials.ClientCertificate.Certificate = MessageSigningCertificate;

using (new OperationContextScope(service.InnerChannel))

    OperationContext.Current.OutgoingMessageHeaders.Add(
      new UserNamePasswordHeader(serviceUserEmail, serviceUserPassword));

    try
    
        var response = service.GetUserList();
        return response;
    
    finally
    
        service.Close();
    

注意:MessageSigningCertificate 是 X.509 证书,我是从文件中读取的:

private static X509Certificate2 LoadCertificateFromFile(string pfxFilePath, string privateKeyPassword)

    // Load the certificate from a file, specifying the password
    var certificate = new X509Certificate2(pfxFilePath, privateKeyPassword);
    return certificate;

【讨论】:

以上是关于WCF:将 Nonce 添加到 UsernameToken的主要内容,如果未能解决你的问题,请参考以下文章

在 Web 场环境中使用共享缓存来检测 WCF 中的重放攻击

如何将 nonce 属性随机 id 与内联 JavaScript 关联

内容安全策略 nonce 不适用于事件处理程序属性

将命名空间添加到 WCF 方法

使用代码而不是配置文件将消息检查器添加到 WCF 服务

将 WCF 服务发布到 Azure 而不将代码添加到源代码管理?