2009-05-22 28 views
21

Estoy tratando de conectarme a un servicio web, escrito en Java, pero hay algo que no puedo entender.WCF: Agregar Nonce a UsernameToken

Usando WCF y un CustomBinding, casi todo parece estar bien, excepto una parte del mensaje SOAP, ya que le faltan los nodos Nonce y Created. Obviamente me falta algo, así que si pudieras dirigirme en la dirección correcta, sería muy apreciado.

Aquí es la unión de encargo:

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

y aquí está la parte pertinente del mensaje:

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

Y esto es como debe mirar:

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

Así que la pregunta es : ¿Cómo podría introducir los elementos Nonce y Created dentro de la parte de seguridad?

+1

¿Has encontrado una solución para esto? Me interesaría saber – Jon

+0

Al final usamos WSE 2 para solucionar los problemas que estábamos teniendo, en lugar de WCF. Allí agregamos una política personalizada para aplicar el UsernameToken a la solicitud de servicio y eso fue lo que creo. –

Respuesta

19

Para crear el nonce, he tenido que cambiar algunas cosas

En primer lugar, añadido un enlace personalizado en mi 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> 

Luego, tome este código que se encuentra aquí: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/4df3354f-0627-42d9-b5fb-6e880b60f8ee y modificarlo para crear el nonce (solo un hash aleatorio, codificado en 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); 
} 
+0

Gracias por la respuesta. No estoy en posición de intentarlo en este momento, pero parece estar bien, así que lo aceptaré. –

+0

Puede ser una respuesta anterior, pero puede que haya resuelto un problema que tengo, ¡hablando con el servicio web de una tienda de Java! ¡Gracias! La pieza que faltaba estaba en la página de Microsoft vinculada, después de agregar el comportamiento personalizado, el nombre de usuario y la contraseña deben establecerse (en el campo service.ClientCredentials.UserName) –

+0

@JohnT: Creo que me falta esa pieza faltante. Estoy haciendo lo siguiente: 'mhsClient.ChannelFactory.Endpoint.Behaviors.Remove (); mhsClient.ChannelFactory.Endpoint.Behaviors.Add (new CustomCredentials()); mhsClient.ClientCredentials.UserName.UserName = "username"; mhsClient.ClientCredentials.UserName.Password = "contraseña"; ' y obtengo el siguiente error: ' "El texto no se puede escribir fuera del elemento raíz" '. –

11

Tuve el mismo problema. En lugar del serializador de token personalizado utilicé un MessageInspector para agregar el UsernameToken correcto en el método BeforeSendRequest. Luego utilicé un comportamiento personalizado para aplicar la corrección.

Todo el proceso está documentado (con un demo project) en mi publicación de blog Supporting the WS-I Basic Profile Password Digest in a WCF client proxy. Alternativamente, puede leer el PDF.

Si quieres seguir mi progreso a través de la solución, lo encontrará en StackOverflow titulado, "Error in WCF client consuming Axis 2 web service with WS-Security UsernameToken PasswordDigest authentication scheme":

+1

Gracias, me salvaste el día. – Sebacote

+0

@Sebacote contenta de ayudar. Fue doloroso trabajar, así que pensé en compartir mi experiencia. – Junto

7

This article proporciona la muestra con plena integración de UserNameToken perfil con contraseña digerida en la tubería de seguridad WCF.

+0

Y esta respuesta me llevó a poder utilizar una contraseña de resumen :) –

0

también tuve que poner un segmento UserNameHeader en la cabecera del mensaje 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> 

Esto se logró con una cabecera de mensaje 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); 
    } 
} 

Otras etiquetas, tales como Nonce y Created, podría ser agregado fácilmente

La clase se utiliza de la siguiente manera:

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 es una X.509 certificado, lo leí de un archivo:

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

Vale la pena señalar que Rick Strahl hizo una entrada en el blog (que hace referencia a esta pregunta) donde se explica todo muy claramente y ofrece soluciones tanto para apenas contraseña y también PasswordDigest.

Publico esto porque encontré este artículo originalmente, no pude seguirlo, y encontré la publicación de Rick mucho más tarde. Esto podría salvar a algunas personas en algún momento.

WCF WSSecurity and WSE Nonce Authentication

+0

Las respuestas anteriores dejan unos pocos pasos para principiantes de SOAP, este artículo ofrece una explicación paso a paso de cada paso en el camino. – h8a

+0

Esto es exactamente lo que estaba buscando. La respuesta aceptada aquí no explica dónde colocar el código, sino que conduce a la publicación de blog de Rick Strahl. Upvoting esta respuesta. – MeanGreen

Cuestiones relacionadas