WCF:将Nonce添加到UsernameToken

我正在尝试连接到用Java编写的Web服务,但有些东西我无法弄清楚。

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

这是自定义绑定:

      

这是消息的相关部分:

           

这就是它的外观:

   .. .. 6ApOnLn5Aq9KSH46pzzcZA== 2009-05-13T18:59:23.309Z   

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

为了创建现时,我不得不改变一些事情

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

                

然后,在这里找到这个代码: http : //social.msdn.microsoft.com/Forums/en-US/wcf/thread/4df3354f-0627-42d9-b5fb-6e880b60f8ee并修改它以创建nonce(只是一个随机hash,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}:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + unToken.Password + "" + "<{0}:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" + nonce + "" + "" + createdStr + "", 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); } 

我有同样的问题。 我使用MessageInspectorBeforeSendRequest方法中添加正确的UsernameToken ,而不是自定义标记序列化BeforeSendRequest 。 然后我使用自定义行为来应用修复。

在我的博客文章中支持WCF客户端代理中的WS-I基本配置文件密码摘要,记录整个过程(带有演示项目 )。 或者,您只需阅读PDF即可 。

如果您想跟进我的进展到解决方案,您将在StackOverflow上找到它,标题为“ WCF客户端使用WS-Security UsernameToken PasswordDigest身份validation方案消耗Axis 2 Web服务时出错 ”:

本文提供了将UserNameToken Profile与消化密码完全集成到WCF安全管道中的示例。

值得指出的是,Rick Strahl做了一篇博客文章(他引用了这个问题),在那里他非常清楚地解释了这一点,并为密码和PasswordDigest提供了解决方案。

我发布这篇文章是因为我最初发现这篇文章,并不能真正遵循它,并且很晚才发现Rick的post。 这可能会节省一些人的时间。

WCF WSSecurity和WSE Nonce身份validation

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

    foouser foopass       

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

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