SOAP-запрос с заголовками безопасности (WSSE)
Я ссылаюсь на файл 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". Но безрезультатно!