C# WCF:向 UsernameToken 添加 Nonce
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/896901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WCF: Adding Nonce to UsernameToken
提问by Adam Vigh
I'm trying to connect to a web service, written in Java, but there's something I can't figure out.
我正在尝试连接到一个用 Java 编写的 Web 服务,但有些东西我想不通。
Using WCF and a customBinding, almost everything seems to be fine, except one part of the SOAP message, as it's missing the Nonce and Created part nodes. Obviously I'm missing something, so if you could point me into the right direction, it'd be much appreciated.
使用 WCF 和 customBinding,除了 SOAP 消息的一部分之外,几乎一切似乎都很好,因为它缺少 Nonce 和 Created 部分节点。显然我遗漏了一些东西,所以如果你能指出我正确的方向,我将不胜感激。
Here's the custom binding:
这是自定义绑定:
<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>
And here's the relevant part of the message:
这是消息的相关部分:
<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>
And this's how it should look:
这就是它的外观:
<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>
So the question is: How could I introduce the Nonce and Created elements inside the security part?
所以问题是:如何在安全部分中引入 Nonce 和 Created 元素?
采纳答案by Bron Davies
To create the nonce, I had to change a few things
为了创建随机数,我必须改变一些东西
First, added a custom binding in my config
首先,在我的配置中添加一个自定义绑定
<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>
Then, take this code found here: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4df3354f-0627-42d9-b5fb-6e880b60f8eeand modify it to create the nonce (just a random hash, base-64 encoded)
然后,使用此处找到的代码: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);
}
回答by Rebecca
I had the same problem. Instead of the custom token serializer I used a MessageInspector
to add the correct UsernameToken
in the BeforeSendRequest
method. I then used a custom behavior to apply the fix.
我有同样的问题。我没有使用自定义令牌序列化程序,而是使用 a在方法中MessageInspector
添加正确UsernameToken
的BeforeSendRequest
。然后我使用自定义行为来应用修复。
The entire process is documented (with a demo project) in my blog post Supporting the WS-I Basic Profile Password Digest in a WCF client proxy. Alternatively, you can just read the PDF.
整个过程在我的博客文章Supporting the WS-I Basic Profile Password Digest in a WCF client proxy中有记录(带有一个演示项目)。或者,您可以只阅读PDF。
If you want to follow my progress through to the solution, you'll find it on StackOverflow titled, "Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme":
如果您想了解我的解决方案进度,您可以在 StackOverflow 上找到它,标题为“ WCF 客户端使用 WS-Security UsernameToken PasswordDigest 身份验证方案使用 Axis 2 Web 服务时出错”:
回答by Ladislav Mrnka
This articleprovides sample with full integration of UserNameToken Profile with digested password into WCF security pipeline.
本文提供了将 UserNameToken Profile 与摘要密码完全集成到 WCF 安全管道中的示例。
回答by Handcraftsman
I also had to put a UserNameHeader segment in the SOAP message header:
我还必须在 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>
This was accomplished with a custom message header:
这是通过自定义消息标头完成的:
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);
}
}
Other tags, such as Nonce
and Created
, could easily be added.
可以轻松添加其他标签,例如Nonce
and Created
。
The class is used as follows:
该类的用法如下:
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();
}
}
Note: MessageSigningCertificate is an X.509 certificate, I read it from a file:
注意: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;
}
回答by Matt Kemp
It's worth pointing out that Rick Strahl made a blog post (which he references this question) where he explains it all quite clearly and offers solutions for both just Password and also PasswordDigest.
值得指出的是,Rick Strahl 发表了一篇博客文章(他引用了这个问题),其中他非常清楚地解释了这一切,并为 Password 和 PasswordDigest 提供了解决方案。
I post this because I found this article originally, couldn't really follow it, and found Rick's post much later. This might save some people some time.
我发布这个是因为我最初发现了这篇文章,无法真正关注它,后来才找到 Rick 的帖子。这可能会为某些人节省一些时间。