java - C# WCF 客户端绑定(bind) 互操作 Blackboard Java WS-Security over HTTPS 传输

标签 java wcf soap ws-security blackboard

我在使 WCF 绑定(bind)与 Blackboard Java Web 服务 API 配合使用时遇到问题。

(Simple answer would be if anyone has got this working could you please post a working binding for WCF to Blackboard)

我花了几个小时尝试不同的配置和自定义编码绑定(bind)。

一些不成功的尝试:

calling-a-ws-security-java-web-service-with-c-sharp-client

wcf-client-with-ws-security 12-common-wcf-interop-confusions

configure-wcf-for-ws-security-with-username-over-https

wcf-client-connecting-to-java-soap-web-service-using-ws-security

ClearUsernameBinding

关于 JAVA 和 WCF 的 WS-Security 还有很多事情要做,但我不会继续说下去。

似乎每次我让一件事工作时,另一件事就会中断。现在我觉得我在兜圈子,让自己更加困惑。

作为我的第一个测试,我想要做的就是简单地初始化 Context 对象并使用具有 WCF 代理的管理测试用户帐户登录。

黑板文档 ContextWS

为了确保所有这些都有效,我首先下载了 .Net WSE 2.0 的示例代码并进行了测试,它运行得很好。

现在,当我使用 WCF 和绑定(bind)时,我无法得到相同的行为。

首先与非常古老的 WSE 2.0 成功交换 ======================================

WSE 2.0 ContextWS 初始化

<?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 初始化成功响应

<?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 登录请求

<?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 登录成功响应

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

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

所以我知道这适用于我们的环境,并且我知道用户可以登录。

使用 WCF 我能够使初始化工作,但随后它会丢失 session 。它不会将返回的 session ID 放入下一条消息的密码字段中。当然,我尝试过手动执行此操作;但我收到一条错误消息,指出密码字段是只读的。

现在我的 WCF 配置和代码使我最接近上述通信。

WCF App.Config 绑定(bind)

<?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# 代码

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

这就是 SOAP 通信的样子。

WCF ContextWS 初始化请求

<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 初始化成功响应

<?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 登录请求

<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 登录失败响应

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

正如您所看到的, session 返回 ID 尚未添加到登录请求的密码字段中,因此存在“无效 session :

一切似乎都很顺利。

简而言之,如果有人知道如何实现从 WCF 客户端到 Blackboard Java Webservice API 的绑定(bind),那么示例将非常棒。另一方面,我希望其他比我更了解 WCF 与 Java 绑定(bind)的人可能能够看一下上面的内容,看看我哪里出错了。

如果任何人能够为我提供任何帮助以使其正常工作,我将不胜感激,非常感谢。我真的希望这只是我错过的一些愚蠢的事情。

抱歉问了这么长的详细问题。

最佳答案

感谢大量的阅读和一些有用的示例,我才得以实现这一目标。带 WCF 的黑板。

感谢两位: 阿贾德克斯·洛佩兹

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

约翰尼·洛克哈特 "Incoming message does not contain required Security header"

BB 是错误的,所以你最好在 WCF 论坛中搜索该帖子

示例类

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

    }

}

使用示例

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

简单包装类

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

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

配置

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

关于java - C# WCF 客户端绑定(bind) 互操作 Blackboard Java WS-Security over HTTPS 传输,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31466801/

相关文章:

java - 使用 wsdl2code 通过 WSDL 连接 web 服务抛出 java.net.MalformedURLException : Protocol not found:

java - 在 Java 中使用对称加密保护磁盘上的私钥

Java:Object.wait(long) 损坏

c# - 对 WCF 服务的 POST 调用出现问题 - 400 BAD REQUEST

c# - 当默认配置已经存储在其他地方时,是否不需要对默认配置进行硬编码?

java - 将自定义http header 添加到Spring Boot WS调用(wstemplate)

java - ksoap2-android信封的定制

java - 用 Set 实现 equals

java - CTRL-G 在 Eclipse 中做什么?

c# - 如何重构 "using"语句以避免代码重复?