WCF: добавление Nonce в UsernameToken
Я пытаюсь подключиться к веб-службе, написанной на Java, но там что-то я не могу понять.
Используя WCF и customBinding, почти все кажется прекрасным, за исключением одной части сообщения SOAP, поскольку он пропускает узлы Nonce и Created part.
Очевидно, я что-то пропустил, поэтому, если бы вы могли указать мне в правильном направлении, это было бы очень полезно.
Здесь пользовательская привязка:
<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 внутри части безопасности?
Ответы
Ответ 1
Чтобы создать nonce, мне пришлось изменить несколько вещей
Сначала добавлена пользовательская привязка в моей конфигурации
<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
и изменить его для создания nonce (только случайный хеш, закодированный 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);
}
Ответ 2
У меня была та же проблема. Вместо пользовательского сериализатора токенов я использовал MessageInspector
, чтобы добавить правильный UsernameToken
в метод BeforeSendRequest
. Затем я использовал настраиваемое поведение для применения исправления.
Весь процесс документирован (с демонстрационным проектом ) в моем сообщении в блоге Поддержка WS-I Basic Profile Password Digest в прокси-клиенте WCF. Кроме того, вы можете просто прочитать PDF.
Если вы хотите следовать моему прогрессу до решения, вы найдете его в StackOverflow под названием "Ошибка в клиенте WCF, использующем веб-службу Axis 2 с помощью WS-Security UsernameToken PasswordDigest схема проверки подлинности":
Ответ 3
В этой статье приведен пример полной интеграции профиля UserNameToken с переваренным паролем в конвейер безопасности WCF.
Ответ 4
Стоит отметить, что Rick Strahl сделал сообщение в блоге (на что он ссылается на этот вопрос), где он объясняет все это довольно четко и предлагает решения как для пароля, так и для PasswordDigest.
Я публикую это, потому что я нашел эту статью изначально, не мог по-настоящему следовать ей, и нашел сообщение Рика намного позже. Это может сэкономить некоторое время.
WCF WSSecurity и WSE Nonce Authentication
Ответ 5
Мне также пришлось поместить сегмент UserNameHeader в заголовок сообщения SOAP:
<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;
}