WCF: Adicionando Nonce ao UsernameToken

Estou tentando me conectar a um serviço da web, escrito em Java, mas há algo que não consigo entender.

Usando o WCF e um customBinding, quase tudo parece estar bem, exceto uma parte da mensagem SOAP, já que está faltando os nós de partes Nonce e Created. Obviamente estou perdendo alguma coisa, então se você pudesse me apontar na direção certa, seria muito apreciado.

Aqui está a binding personalizada:

      

E aqui está a parte relevante da mensagem:

           

E é assim que deve ser:

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

Então a pergunta é: Como eu poderia introduzir os elementos Nonce e Created dentro da parte de segurança?

Para criar o nonce, eu tive que mudar algumas coisas

Primeiro, adicionei uma binding personalizada na minha configuração

                

Em seguida, pegue este código encontrado aqui: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4df3354f-0627-42d9-b5fb-6e880b60f8ee e modifique-o para criar o nonce (apenas um random hash, base-64 codificado)

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

Eu tive o mesmo problema. Em vez do serializador de token personalizado, usei um MessageInspector para adicionar o UsernameToken correto no método BeforeSendRequest . Em seguida, usei um comportamento personalizado para aplicar a correção.

Todo o processo está documentado (com um projeto de demonstração ) em meu blog. Suportando o resumo de senhas do perfil básico do WS-I em um proxy do cliente WCF . Alternativamente, você pode apenas ler o PDF .

Se você quiser acompanhar meu progresso até a solução, você o encontrará no StackOverflow intitulado ” Erro no cliente da Web do WCF que consome o serviço da Web do Axis 2 com o esquema de autenticação WS-Security UsernameToken PasswordDigest “:

Este artigo fornece amostra com integração total do perfil UserNameToken com senha digerida no pipeline de segurança do WCF.

Vale ressaltar que Rick Strahl fez um post no blog (que ele faz referência a essa pergunta), onde explica tudo com bastante clareza e oferece soluções tanto para a senha quanto para o PasswordDigest.

Eu postei isso porque encontrei este artigo originalmente, não consegui acompanhá-lo e encontrei o post de Rick muito mais tarde. Isso pode poupar algumas pessoas algum tempo.

Autenticação WCS WSSecurity e WSE Nonce

Eu também tive que colocar um segmento UserNameHeader no header da mensagem SOAP:

    foouser foopass       

Isso foi realizado com um header de mensagem personalizado:

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

Outras tags, como Nonce e Created , podem ser facilmente adicionadas.

A class é usada da seguinte maneira:

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

Nota: MessageSigningCertificate é um certificado X.509, eu li de um arquivo:

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