title: SOAP request with Security Headers (WSSE)
body: <p>I am linking into an external companies' WSDL file, which i need to use to send requests and recieve information back via SOAP requests.</p>
  
  <p>They provided the WSDL file and i have added that as a 'service reference'. Normally this is enough, however whenever we send a request we need to attach a wsse header and security. We've managed to fumble our way through the adding of the header however there are some issues with it, which i'm not quite sure how to get around.</p>
  
  <p>The Soap Request SHOULD look like : </p>
  
  <pre><code>&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;
      &lt;SOAP-ENV:Header&gt;
          &lt;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"&gt;
              &lt;wsse:UsernameToken wsu:Id="UsernameToken-17A419BD1DC95B13F214417185709617"&gt;
                  &lt;wsse:Username&gt;aUsername&lt;/wsse:Username&gt;
                  &lt;wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"&gt;##HashedPassword##&lt;/wsse:Password&gt;
                  &lt;wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"&gt;DUSgCUI8X3BJrVe8F+fGnQ==&lt;/wsse:Nonce&gt;
                  &lt;wsu:Created&gt;2015-09-08T13:22:50.961Z&lt;/wsu:Created&gt;
              &lt;/wsse:UsernameToken&gt;
          &lt;/wsse:Security&gt;
      &lt;/SOAP-ENV:Header&gt;
      &lt;SOAP-ENV:Body&gt;
                  &lt;stuffhere /&gt;
      &lt;/SOAP-ENV:Body&gt;
  &lt;/SOAP-ENV:Envelope&gt;
  </code></pre>
  
  <p>My request currently looks like;</p>
  
  <pre><code>&lt;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"&gt;
      &lt;s:Header&gt;
          &lt;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"/&gt;
          &lt;o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"&gt;
              &lt;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"&gt;
                  &lt;h:Username&gt;aUsername&lt;/h:Username&gt;
                  &lt;h:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"&gt;##HashedPassword##&lt;/h:Password&gt;
                  &lt;h:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"&gt;kRNsqdcQdv8T5y5rTv4O2WeidUM=&lt;/h:Nonce&gt;
                  &lt;u:Created&gt;2015-09-09T10:48:44.498Z&lt;/u:Created&gt;
              &lt;/h:UsernameToken&gt;
          &lt;/o:Security&gt;
      &lt;/s:Header&gt;
      &lt;s:Body &gt;
          &lt;stuffhere /&gt;
  
      &lt;/s:Body&gt;   
  &lt;/s:Envelope&gt;
  </code></pre>
  
  <p>As you can see the security tags have messed up mainly. Also, the XMLNS tags seem to be all over the place, which I'm not quite sure if that will cause an issue or not.</p>
  
  <p>Currently we are over-riding the header using the following classes (which i got from researching around);</p>
  
  <pre><code>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("&lt;{0}:UsernameToken u:Id=""" + token.Id + """ xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd""&gt;" + "&lt;{0}:Username&gt;" + userToken.UserName + "&lt;/{0}:Username&gt;" + "&lt;{0}:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest""&gt;") &amp; password) + "&lt;/{0}:Password&gt;" + "&lt;{0}:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary""&gt;") &amp; nonce) + "&lt;/{0}:Nonce&gt;" + "&lt;u:Created&gt;") &amp; createdStr) + "&lt;/u:Created&gt;&lt;/{0}:UsernameToken&gt;", 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
  </code></pre>
  
  <p>My web.config is currently configured as;</p>
  
  <pre><code>&lt;system.serviceModel&gt;
  &lt;bindings&gt;
    &lt;basicHttpBinding&gt;
      &lt;binding name="shippingAPISoapBinding"&gt;
        &lt;!--&lt;security mode="TransportWithMessageCredential"&gt;--&gt;
        &lt;security mode="TransportWithMessageCredential"&gt;
          &lt;message clientCredentialType="UserName" /&gt;
          &lt;transport clientCredentialType="Certificate"&gt;&lt;/transport&gt;
        &lt;/security&gt;
      &lt;/binding&gt;
      &lt;binding name="shippingAPISoapBinding1"/&gt;
    &lt;/basicHttpBinding&gt;
  &lt;/bindings&gt;
  &lt;client&gt;
    &lt;endpoint address="https://www.anEndpoint.com/endpoint" binding="basicHttpBinding" bindingConfiguration="shippingAPISoapBinding" contract="RMailAPI.shippingAPIPortType" name="shippingAPISoapBinding" behaviorConfiguration="ClientCertificateBehavior"  &gt;
    &lt;/endpoint&gt;   
   &lt;/client&gt;    
  &lt;/system.serviceModel&gt;
  </code></pre>
  
  <p>As mentioned before we are sending requests using their wsdl which is done doing the following;</p>
  
  <pre><code>    '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:" &amp; ex.Message &amp; "&lt;br /&gt;")
      End Try
  </code></pre>
  
  <p>I'm sorry if this is confusing, I've found this whole method very convoluted and I'm very sure I'm missing something very obvious here.</p>
  
  <p>I'd be happy to provide any more information.</p>
  
  <p>*Edit 1;</p>
  
  <p>I tried adding my headers into the web.config, however there is an issue with that method as my password needs to be hashed before sending with a random string (the "nonce", which I also need to pass through). Not sure If i can change the web.config headers before sending.</p>
  
  <p>*Edit 2; </p>
  
  <p>Been looking to see if there's some way I can get the whole header, or if I can intercept it much in the same way I do with the "WriteTokenCore" function. To no avail!</p>