C # WCF Client Binding Interop Blackboard Java WS-Sécurité sur le transport HTTPS


J'ai du mal à faire fonctionner la liaison WCF avec l'API Blackboard Java Web Services.

(La réponse simple serait que si quelqu'un a ce travail, pourriez-vous veuillez poster une liaison de travail pour WCF sur Blackboard)

J'ai passé des heures à essayer différentes configurations et liaisons codées personnalisées.

Quelques tentatives infructueuses:

Appel-a-ws-security-java-web-service-avec-c-sharp-client

Wcf client-avec-ws-security 12-commune-wcf-interop-confusions

Configurer-wcf-pour-ws-security-avec-nom d'utilisateur-sur-https

Wcf client-connexion-pour-java-savon-web-service-aide-ws-security

ClearUsernameBinding

Il y a beaucoup plus à voir avec JAVA et WS-Security avec WCF mais je ne vais pas continuer.

Il semble que chaque fois que je reçois une chose qui fonctionne, une autre se brise. Maintenant je pense que je vais tourner en rond et de juste faire moi-même encore plus confus.

Comme premier test, ce que j'essaie de faire est d'initialiser simplement l'objet Context et de me connecter à l'aide d'un compte d'utilisateur de test Admin avec un proxy WCF.

Blackboard Doc ContextWS

Pour m'assurer que tout cela a fonctionné, j'ai d'abord téléchargé l'exemple de code pour.Net WSE 2.0 et testé cela, cela a parfaitement fonctionné.

Maintenant, lorsque j'utilise WCF et binding, je ne peux pas obtenir ce même comportement.

D'abord l'échange réussi avec le très vieux WSE 2.0 ===================================

Nous vous invitons à nous contacter pour plus d'informations.]}

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <soap:Header>
        <wsa:Action>initialize</wsa:Action>
        <wsa:MessageID>uuid:b975e989-a4ce-4e1e-abd6-500945346c40</wsa:MessageID>
        <wsa:ReplyTo>
            <wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
        </wsa:ReplyTo>
        <wsa:To>https://Blackboard.Server.Name/webapps/ws/services/Context.WS</wsa:To>
        <wsse:Security soap:mustUnderstand="1">
            <wsu:Timestamp wsu:Id="Timestamp-47d0d017-4fd1-46c2-b1b4-2431402cf847">
                <wsu:Created>2015-07-16T04:58:02Z</wsu:Created>
                <wsu:Expires>2015-07-16T05:03:02Z</wsu:Expires>
            </wsu:Timestamp>
            <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-1b71e23a-2d84-40a5-9509-b75902ec8b76">
                <wsse:Username>session</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</wsse:Password>
                <wsse:Nonce>lAW2qXrXZ1maNNkCEzlHGA==</wsse:Nonce>
                <wsu:Created>2015-07-16T04:58:02Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </soap:Header>
    <soap:Body />
</soap:Envelope>

WSE 2.0 ContextWS Réponse à la réussite de l'initialisation

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns:initializeResponse xmlns:ns="http://context.ws.blackboard">
            <ns:return>c2762f357bbc42a4a88d33e4e42486b8</ns:return>
        </ns:initializeResponse>
    </soapenv:Body>
</soapenv:Envelope>

WSE 2.0 ContextWS Demande de connexion

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <soap:Header>
        <wsa:Action>login</wsa:Action>
        <wsa:MessageID>uuid:a823128b-efb4-49e1-87d9-fd35167f0bfc</wsa:MessageID>
        <wsa:ReplyTo>
            <wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
        </wsa:ReplyTo>
        <wsa:To>https://Blackboard.Server.Name/webapps/ws/services/Context.WS</wsa:To>
        <wsse:Security soap:mustUnderstand="1">
            <wsu:Timestamp wsu:Id="Timestamp-c38daf19-6b39-4391-a3f8-bcc030064a3e">
                <wsu:Created>2015-07-16T04:58:15Z</wsu:Created>
                <wsu:Expires>2015-07-16T05:03:15Z</wsu:Expires>
            </wsu:Timestamp>
            <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-65948746-e616-436a-85f4-d2e1023e39be">
                <wsse:Username>session</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">c2762f357bbc42a4a88d33e4e42486b8</wsse:Password>
                <wsse:Nonce>T0xs8aiaiODMK3sfKgDQtg==</wsse:Nonce>
                <wsu:Created>2015-07-16T04:58:15Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </soap:Header>
    <soap:Body>
        <login xmlns="http://context.ws.blackboard">
            <userid>test_admin</userid>
            <password>TestPassword</password>
            <clientVendorId>TestClient</clientVendorId>
            <clientProgramId>TestPOC</clientProgramId>
            <loginExtraInfo xsi:nil="true" />
            <expectedLifeSeconds>10000000</expectedLifeSeconds>
        </login>
    </soap:Body>
</soap:Envelope>

WSE 2.0 ContextWS Réponse de réussite de connexion

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns:loginResponse xmlns:ns="http://context.ws.blackboard">
            <ns:return>true</ns:return>
        </ns:loginResponse>
    </soapenv:Body>
</soapenv:Envelope>

===================================

, Donc je sais que cela fonctionne pour notre environnement et je sais que l'utilisateur peut connexion.

En utilisant WCF, je peux faire fonctionner l'initialisation mais cela perd la session. Il ne met pas l'ID de session retourné dans le champ Mot de passe pour le message suivant. J'ai essayé de le faire manuellement bien sûr; mais je reçois une erreur indiquant que le champ de mot de passe est en lecture seule.

Maintenant pour ma configuration WCF et mon code qui m'a rapproché le plus de la communication ci-dessus.

Application WCF.Liaison de configuration

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    </system.serviceModel>
        <bindings>
          <customBinding>
                <binding name="WCFSoapInteropJavaWS"  closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"  >
                  <textMessageEncoding messageVersion="Soap11" writeEncoding="utf-8" />
                  <security authenticationMode="UserNameOverTransport" enableUnsecuredResponse="true" allowSerializedSigningTokenOnReply="true"
                            messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
                            includeTimestamp="true" allowInsecureTransport="true" canRenewSecurityContextToken="false" >
                  </security>
                  <httpsTransport  authenticationScheme="Anonymous"  />
                </binding>

              </customBinding>
         </bindings>

        <client>

            <endpoint 
                address="https://Blackboard.Server.Name:443/webapps/ws/services/Context.WS"
                binding="customBinding" bindingConfiguration="WCFSoapInteropJavaWS"
                contract="ContextWS.ContextWSPortType" name="Context.WCFSoapInteropJavaWS" />

        </client>

    </system.serviceModel>
</configuration>

WCF C# code

 public bool testWrapper(String userId, String userPassword){


             try
             {
                 context = new ContextWrapper("Context.WCFSoapInteropJavaWS");

                 context.ClientCredentials.UserName.UserName = "session";
                 context.ClientCredentials.UserName.Password = "nosession";

                 context.initialize();

                 //context.ClientCredentials.UserName.Password = "886d935527944f94a3526288e39a555e";  // SessionGUID_HERE Throws a Read Only Error for Pasword

                 bool retval = context.login(userId, userPassword, vendorId, programId, null, expectedLife);

                 return retval;
             }
             catch (System.Exception e)
             {
                 lastError = e;
                 return false;
             }
        }

Voici à quoi ressemble la communication SOAP.

WCF ContextWS Demande d'initialisation

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
        <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo+FmveflwUtMgSATRu3Ht9EAAAAAmYVJsX+bhUeYcTDsFqFktkqe8xmMiA1MpXouaouXgJwACQAA</VsDebuggerCausalityData>
        <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <u:Timestamp u:Id="_0">
                <u:Created>2015-07-16T07:15:05.109Z</u:Created>
                <u:Expires>2015-07-16T07:20:05.109Z</u:Expires>
            </u:Timestamp>
            <o:UsernameToken u:Id="uuid-1237f56c-7c68-4d40-a756-7ff2c19a3235-1">
                <o:Username>session</o:Username>
                <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</o:Password>
            </o:UsernameToken>
        </o:Security>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
</s:Envelope>

WCF ContextWS l'Initialisation de la Réponse de Réussite

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns:initializeResponse xmlns:ns="http://context.ws.blackboard">
            <ns:return>886d935527944f94a3526288e39a555e</ns:return>
        </ns:initializeResponse>
    </soapenv:Body>
</soapenv:Envelope>

WCF ContextWS Demande de connexion

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
        <VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo+JmveflwUtMgSATRu3Ht9EAAAAAmYVJsX+bhUeYcTDsFqFktkqe8xmMiA1MpXouaouXgJwACQAA</VsDebuggerCausalityData>
        <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <u:Timestamp u:Id="_0">
                <u:Created>2015-07-16T07:15:14.033Z</u:Created>
                <u:Expires>2015-07-16T07:20:14.033Z</u:Expires>
            </u:Timestamp>
            <o:UsernameToken u:Id="uuid-1237f56c-7c68-4d40-a756-7ff2c19a3235-1">
                <o:Username>session</o:Username>
                <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nosession</o:Password>
            </o:UsernameToken>
        </o:Security>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <login xmlns="http://context.ws.blackboard">
            <userid>Test_admin</userid>
            <password>TestPassword</password>
            <clientVendorId>TestClient</clientVendorId>
            <clientProgramId>TestPOC</clientProgramId>
            <loginExtraInfo xsi:nil="true"/>
            <expectedLifeSeconds>10000000</expectedLifeSeconds>
        </login>
    </s:Body>
</s:Envelope>

WCF ContextWS Échec de la Connexion de Réponse

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <soapenv:Fault>
            <faultcode>soapenv:Server</faultcode>
            <faultstring>[WSFW001]Invalid session</faultstring>
            <detail />
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

Comme vous pouvez le voir, l'id de retour de session n'a pas été ajouté au champ mot de passe de la demande de connexion, il y a donc un " Invalide Session:

Tout semblait aller si bien.

En bref, si quelqu'un sait comment réaliser une liaison d'un client WCF à l'API et à l'exemple Blackboard Java Webservice serait fantastique. Autre sage, j'espère que quelqu'un d'autre qui en sait plus sur les liaisons WCF vers Java que moi pourrait jeter un oeil à ce qui précède voir où je vais mal.

Toute aide que quelqu'un peut me donner pour que cela fonctionne serait très appréciée, alors merci. Je suis vraiment en espérant qu'il soit juste quelque chose de stupide qui me manque.

Désolé pour une question aussi longue et détaillée.

Author: INK, 2015-07-17

1 answers

Grâce à des tas de lectures et quelques échantillons utiles, j'ai pu faire fonctionner cela. Tableau noir avec WCF.

Merci aux deux: Ajadex Lopez

Http://www.isyourcode.com/2010/08/attaching-oasis-username-tokens-headers.html

Johnny Lockhart "Le message entrant ne contient pas d'en-tête de sécurité requis"

BB est buggie donc vous pourriez faire mieux de rechercher sur le forum WCF pour trouver le post

Échantillon classe

using System;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel;
using System.Xml;
using System.Security.Cryptography;
using System.Text;


namespace BBWcfWrapper
{


    /// <summary>
    /// Coupled with the additional classes below, allows for injecting the WS-Security header into a WCF Service call without requiring SSL on the server.
    /// </summary>
    /// <remarks>http://isyourcode.blogspot.com/2010/08/attaching-oasis-username-tokens-headers.html</remarks>
    public class BBWSSecurityBehavior : IEndpointBehavior
    {
        public MessageInspector MessageInspector { get; set; }

        public BBWSSecurityBehavior(MessageInspector messageInspector)
        {
            MessageInspector = messageInspector;
        }

        public void Validate(ServiceEndpoint endpoint)
        { }
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        { }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            if (this.MessageInspector == null) throw new InvalidOperationException("Caller must supply ClientInspector.");
            clientRuntime.MessageInspectors.Add(MessageInspector);
        }

    }

    public class MessageInspector : IClientMessageInspector
    {
        public MessageHeader[] Headers { get; set; }
        public MessageInspector(params MessageHeader[] headers)
        {
            Headers = headers;
        }
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            if (Headers != null)
            {
                for (int i = Headers.Length - 1; i >= 0; i--)
                {
                    request.Headers.Insert(0, Headers[i]);
                }
            }

            return request;
        }
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {
        }
    }


    public class SecurityHeader : MessageHeader
    {

        public string SystemUser { get; set; }
        public string SystemPassword { get; set; }
        public SecurityHeader(string systemUser, string systemPassword)
        {
            SystemUser = systemUser;
            SystemPassword = systemPassword;
        }
        public override string Name
        {
            get { return "Security"; }
        }
        public override string Namespace
        {
            get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
        }

        protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement("wsse", Name, Namespace);
            writer.WriteXmlnsAttribute("wsse", Namespace);
            writer.WriteAttributeString("soap", "mustUnderstand", Namespace, "1");
        }

        protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            WriteHeader(writer);
        }

        private void WriteHeader(XmlDictionaryWriter writer)
        {
            var createDate = DateTime.Now;

            //Start Parent Elements 
            writer.WriteStartElement("wsu","Timestamp","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
            writer.WriteAttributeString("wsu","id","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd","Timestamp-6557466");

            #region Start Child Elements

            writer.WriteStartElement("wsu", "Created", XmlConvert.ToString(createDate, "yyyy-MM-ddTHH:mm:sszzzzzz"));
            writer.WriteEndElement();   //End Created

            writer.WriteStartElement("wsu", "Expires", XmlConvert.ToString(createDate.AddDays(1), "yyyy-MM-ddTHH:mm:sszzzzzz"));
            writer.WriteEndElement();   //End Expires

            #endregion

            writer.WriteEndElement();   //End Timestamp

            //Start Parent Elements 
            writer.WriteStartElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
            writer.WriteXmlnsAttribute("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

            #region Start Child Elements

            writer.WriteStartElement("wsse", "Username", null);
            writer.WriteString(SystemUser);
            writer.WriteEndElement();//End Username 

            writer.WriteStartElement("wsse", "Password", null);
            writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
            writer.WriteString(SystemPassword);
            writer.WriteEndElement();//End Password 

            // unique Nonce value - encode with SHA-1 for 'randomness'
            // in theory the nonce could just be the GUID by itself
            // This is used to stop Replay attacks 
            writer.WriteStartElement("wsse", "Nonce", null);
            writer.WriteString(GetSHA1String(Guid.NewGuid().ToString()));
            writer.WriteEndElement();//Nonce 

            writer.WriteStartElement("wsu", "Created", null);
            writer.WriteString(XmlConvert.ToString(createDate, "yyyy-MM-ddTHH:mm:sszzzzzz"));
            writer.WriteEndElement();   //End Created

            #endregion

            writer.WriteEndElement();//End UsernameToken
            writer.Flush(); 

        }

        protected string GetSHA1String(string phrase)
        {
            SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
            byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
            return Convert.ToBase64String(hashedDataBytes);
        }

    }

}

Exemple D'Utilisation

  calendar = new CalendarWrapper("Calendar.BB_WSSecurity_Binding");

        //This adds a custom security Headder for WCF and Java WS-Security Interop
        calendar.Endpoint.Behaviors.Add(new BBWSSecurityBehavior(new MessageInspector(BbWsAuth.SecurityHeader)));

        calendar.initializeCalendarWS(false);

Classe Wrapper simple

    public class CalendarWrapper : CalendarWSPortTypeClient
{
    public CalendarWrapper() : base() { }

    public CalendarWrapper(string endpointConfigurationName) : base(endpointConfigurationName) { }
    public CalendarWrapper(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { }
}

Configuration

 <bindings>

    <basicHttpsBinding>
      <binding name="BB_WSSecurity_Binding" messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="4000000" >
        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
          maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      </binding>
    </basicHttpsBinding>

  </bindings>

  <client>

  <endpoint address="https://xxxxxxxxxx/webapps/ws/services/Calendar.WS"
           binding="basicHttpsBinding" bindingConfiguration="BB_WSSecurity_Binding"
            contract="CalendarWS.CalendarWSPortType" name="Calendar.BB_WSSecurity_Binding"  />
 0
Author: INK, 2015-10-13 01:26:25