1

我正在链接到外部公司的 WSDL 文件,我需要使用该文件通过 SOAP 请求发送请求和接收信息。

他们提供了 WSDL 文件,我已将其添加为“服务参考”。通常这就足够了,但是每当我们发送请求时,我们都需要附加一个 wsse 标头和安全性。我们已经设法通过添加标题摸索出路,但是它存在一些问题,我不太确定如何解决。

肥皂请求应该看起来像:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header>
        <wsse:Security
            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">
            <wsse:UsernameToken wsu:Id="UsernameToken-17A419BD1DC95B13F214417185709617">
                <wsse:Username>aUsername</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">##HashedPassword##</wsse:Password>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">DUSgCUI8X3BJrVe8F+fGnQ==</wsse:Nonce>
                <wsu:Created>2015-09-08T13:22:50.961Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
                <stuffhere />
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我的请求目前看起来像;

<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>
        <h:Security
        xmlns:h="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
        <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <h:UsernameToken u:Id="uuid-b9edaf83-70fb-4909-85eb-9773058c7001-1" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <h:Username>aUsername</h:Username>
                <h:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">##HashedPassword##</h:Password>
                <h:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">kRNsqdcQdv8T5y5rTv4O2WeidUM=</h:Nonce>
                <u:Created>2015-09-09T10:48:44.498Z</u:Created>
            </h:UsernameToken>
        </o:Security>
    </s:Header>
    <s:Body >
        <stuffhere />

    </s:Body>   
</s:Envelope>

如您所见,安全标签主要是搞砸了。此外,XMLNS 标签似乎到处都是,我不太确定这是否会导致问题。

目前我们正在使用以下类(我从研究中获得)覆盖标题;

Imports Microsoft.VisualBasic
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Channels
Imports System.ServiceModel
Imports System.Xml
Imports System.ServiceModel.Description
Imports System.ServiceModel.Dispatcher.ClientRuntime
Imports System
Imports System.IdentityModel.Selectors.SecurityTokenSerializer
Imports System.ServiceModel.Security
Imports System.IdentityModel.Tokens
Imports System.Security.Cryptography


Public Class CustomCredentials
    Inherits ClientCredentials
    Public Sub New()
    End Sub

    Protected Sub New(cc As CustomCredentials)
        MyBase.New(cc)
    End Sub

    Public Overrides Function CreateSecurityTokenManager() As System.IdentityModel.Selectors.SecurityTokenManager
        Return New CustomSecurityTokenManager(Me)
    End Function

    Protected Overrides Function CloneCore() As ClientCredentials
        Return New CustomCredentials(Me)
    End Function
End Class

Public Class CustomSecurityTokenManager
    Inherits ClientCredentialsSecurityTokenManager
    Public Sub New(cred As CustomCredentials)
        MyBase.New(cred)
    End Sub

    Public Overrides Function CreateSecurityTokenSerializer(version As System.IdentityModel.Selectors.SecurityTokenVersion) As System.IdentityModel.Selectors.SecurityTokenSerializer
        Return New CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity11)
    End Function
End Class

Public Class CustomTokenSerializer
    Inherits WSSecurityTokenSerializer
    Public Sub New(sv As SecurityVersion)
        MyBase.New(sv)
    End Sub

    'Protected Overrides Sub WriteKeyIdentifierClauseCore(writer As XmlWriter, keyIdentifierClause As SecurityKeyIdentifierClause)
    '    MyBase.WriteKeyIdentifierClauseCore(writer, keyIdentifierClause)

    '    writer.WriteRaw("TestClause")

    'End Sub

    Protected Overrides Sub WriteTokenCore(writer As System.Xml.XmlWriter, token As System.IdentityModel.Tokens.SecurityToken)
        Dim userToken As UserNameSecurityToken = TryCast(token, UserNameSecurityToken)

        Dim tokennamespace As String = "h"

        Dim created As DateTime = DateTime.Now
        Dim createdStr As String = created.ToString("yyyy-MM-ddThh:mm:ss.fffZ")

        ' unique Nonce value - encode with SHA-1 for 'randomness'
        ' in theory the nonce could just be the GUID by itself
        Dim phrase As String = Guid.NewGuid().ToString()
        Dim nonce = GetSHA1String(phrase)

        ' in this case password is plain text
        ' for digest mode password needs to be encoded as:
        ' PasswordAsDigest = Base64(SHA-1(Nonce + Created + Password))
        ' and profile needs to change to
        'string password = GetSHA1String(nonce + createdStr + userToken.Password);

        Dim password As String = GetSHA1String(nonce + createdStr + GetSHA1String(userToken.Password))


        writer.WriteRaw(String.Format((Convert.ToString((Convert.ToString((Convert.ToString("<{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>" + userToken.UserName + "</{0}:Username>" + "<{0}:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"">") & 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))


    End Sub

    Protected Function GetSHA1String(phrase As String) As String
        Dim sha1Hasher As New SHA1CryptoServiceProvider()
        Dim hashedDataBytes As Byte() = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase))
        Return Convert.ToBase64String(hashedDataBytes)
    End Function


End Class

我的 web.config 当前配置为;

<system.serviceModel>
<bindings>
  <basicHttpBinding>
    <binding name="shippingAPISoapBinding">
      <!--<security mode="TransportWithMessageCredential">-->
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="UserName" />
        <transport clientCredentialType="Certificate"></transport>
      </security>
    </binding>
    <binding name="shippingAPISoapBinding1"/>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint address="https://www.anEndpoint.com/endpoint" binding="basicHttpBinding" bindingConfiguration="shippingAPISoapBinding" contract="RMailAPI.shippingAPIPortType" name="shippingAPISoapBinding" behaviorConfiguration="ClientCertificateBehavior"  >
  </endpoint>   
 </client>    
</system.serviceModel>

如前所述,我们使用他们的 wsdl 发送请求,完成以下操作;

    'The securityheader we have to attach to be able to call createshipmentrequest1
    'The only things i can access on this are .Any or .AnyAttr which are XML attributes
    Dim securityHeader As New RMailAPI.SecurityHeaderType


    'We declare our requests here
    Dim cs As RMailAPI.createShipmentRequest
    cs = New RMailAPI.createShipmentRequest

    'assign values to cs here
    'These come through into the XML just fine

    Dim cs1 As New RMailAPI.createShipmentRequest1(securityHeader, cs)

    Try
        shipResponse = serviceClient.createShipment(cs1)
    Catch ex As CommunicationException
        Response.Write("msg:" & ex.Message & "<br />")
    End Try

如果这令人困惑,我很抱歉,我发现整个方法非常复杂,我很确定我在这里遗漏了一些非常明显的东西。

我很乐意提供更多信息。

*编辑1;

我尝试将我的标头添加到 web.config 中,但是该方法存在问题,因为我的密码需要在发送随机字符串(“nonce”,我也需要通过)之前进行哈希处理。不确定我是否可以在发送前更改 web.config 标头。

*编辑2;

一直在寻找是否有某种方法可以获得整个标题,或者是否可以像使用“WriteTokenCore”函数一样拦截它。无济于事!

4

0 回答 0