我正在尝试连接到用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元素?
答案 0 :(得分:21)
要创建现时,我不得不改变一些事情
首先,在我的配置中添加了自定义绑定
<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);
}
答案 1 :(得分:11)
我遇到了同样的问题。我使用MessageInspector
代替自定义令牌序列化程序,在UsernameToken
方法中添加了正确的BeforeSendRequest
。然后我使用自定义行为来应用修复。
我的博文demo project中记录了整个过程(Supporting the WS-I Basic Profile Password Digest in a WCF client proxy}。或者,您可以阅读PDF。
如果您想跟进我的进度到解决方案,您可以在StackOverflow上找到标题为“Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme”:
答案 2 :(得分:7)
This article提供了将UserNameToken Profile与消化密码完全集成到WCF安全管道中的示例。
答案 3 :(得分:7)
值得指出的是,Rick Strahl撰写了一篇博文(他引用了这个问题),在那里他非常清楚地解释了这一点,并为密码和PasswordDigest提供了解决方案。
我发布这篇文章是因为我最初发现这篇文章,并不能真正遵循它,并且很晚才找到Rick的帖子。这可能会节省一些人的时间。
答案 4 :(得分:-1)
我还必须在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);
}
}
可以轻松添加其他代码,例如Nonce
和Created
。
该类使用如下:
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;
}