6

I need to connect my NET Framework 4 Client App, to a Webservice, deployed on a Apache CXF, with WS Security. That service is out of my control.

The service is added to the project as a "Service Reference".

This is the proxy:

ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf AcceptAllCertifications) 

Dim oBinding As New CustomBinding()
Dim oSecurity As SecurityBindingElement

oSecurity = AsymmetricSecurityBindingElement.CreateCertificateOverTransportBindingElement(MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10)
oSecurity.IncludeTimestamp = True

oBinding.Elements.Add(oSecurity)
oBinding.Elements.Add(New CertFixEscapedComma.CertRefEncodingBindingElement())
   ( This a custom message encoder)
CertFixEscapedComma.CertRefEncoder.CERTIFICADO = Convert.ToBase64String(oCertificado.RawData) 

oBinding.CloseTimeout = New TimeSpan(0, 2, 0)

Dim oTransport As New HttpsTransportBindingElement()
oBinding.Elements.Add(oTransport)

Dim oProxyClient As New NameServiceClient(oBinding, New System.ServiceModel.EndpointAddress(New Uri("https://url_service")))
Dim oCertificado As X509Certificate2
oCertificado = function_client_certificate() ' this get the proper cert


oProxyClient.ClientCredentials.ClientCertificate.Certificate = oCertificado
oProxyClient.name_function(params) 'call to the remote service

Well. The server accepts my Request, and send the the response, in this way:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap: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" soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-4A5A4F8820EFD673E7152328322340610394">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" />
        <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
          <wsse:SecurityTokenReference>
            <ds:X509Data>
              <ds:X509IssuerSerial>
                <ds:X509IssuerName>issuer name etc etc cetc </ds:X509IssuerName>
                <ds:X509SerialNumber>62535066537829860999033107852056725154</ds:X509SerialNumber>
              </ds:X509IssuerSerial>
            </ds:X509Data>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
        <xenc:CipherData>
          <xenc:CipherValue>SlU4B4BlMhsEc0ek ... ==</xenc:CipherValue>
        </xenc:CipherData>
        <xenc:ReferenceList>
          <xenc:DataReference URI="#ED-4A5A4F8820EFD673E7152328322340710395" />
        </xenc:ReferenceList>
      </xenc:EncryptedKey>
      <wsu:Timestamp wsu:Id="TS-4A5A4F8820EFD673E7152328322340510393">
        <wsu:Created>2018-04-09T14:13:43.405Z</wsu:Created>
        <wsu:Expires>2018-04-09T14:18:43.405Z</wsu:Expires>
      </wsu:Timestamp>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-4A5A4F8820EFD673E7152328322340710395" Type="http://www.w3.org/2001/04/xmlenc#Content">
      <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
          <wsse:Reference URI="#EK-4A5A4F8820EFD673E7152328322340610394" />
        </wsse:SecurityTokenReference>
      </ds:KeyInfo>
      <xenc:CipherData>
        <xenc:CipherValue>ZB7P3tYgRE4R7RZc0TONazc93t.... W5VoHVw5ywRj4D2hb9dIAaE8PQClm2vw==</xenc:CipherValue>
      </xenc:CipherData>
    </xenc:EncryptedData>
  </soap:Body>
</soap:Envelope>

I get the error "Cannot resolve KeyInfo for unwrapping key". Reading OASIS doc about that kind of soap messages, I think the message it's ok.

I have tried, with the custom encoder, changing "X509IssuerSerial" node, for a ". Same error.

I can, reading the message directly, perform a manual key decryption, using the cert. Then, with the key, I can decrypt the data. So data is correct.

BUT, I don't wan't this. I want use the Service Reference.

Going through NET Code, I see that stack trace:

System.ServiceModel.dll!System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.ReadTokenCore(System.Xml.XmlDictionaryReader reader, System.IdentityModel.Selectors.SecurityTokenResolver tokenResolver) 
System.ServiceModel.dll!System.ServiceModel.Security.WSSecurityTokenSerializer.ReadTokenCore(System.Xml.XmlReader reader, System.IdentityModel.Selectors.SecurityTokenResolver tokenResolver)
System.ServiceModel.dll!System.ServiceModel.Security.WSSecurityOneDotZeroReceiveSecurityHeader.DecryptWrappedKey(System.Xml.XmlDictionaryReader reader)
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.ReadEncryptedKey(System.Xml.XmlDictionaryReader reader, bool processReferenceListIfPresent)
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(System.Xml.XmlDictionaryReader reader)
System.ServiceModel.dll!System.ServiceModel.Security.ReceiveSecurityHeader.Process(System.TimeSpan timeout, System.Security.Authentication.ExtendedProtection.ChannelBinding channelBinding, System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy extendedProtectionPolicy)
System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.VerifyIncomingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout, System.ServiceModel.Security.SecurityProtocolCorrelationState[] correlationStates)
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelFactory<System.ServiceModel.Channels.IRequestChannel>.SecurityRequestChannel.ProcessReply(System.ServiceModel.Channels.Message reply, System.ServiceModel.Security.SecurityProtocolCorrelationState correlationState, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelFactory<System.__Canon>.SecurityRequestChannel.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Dispatcher.RequestChannelBinder.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message)
mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type)
 ... mycode_calling_the_service()... 

So, in "System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.ReadTokenCore", it's trying to "CreateWrappedKeyToken", and the Exception it's throw here:

   WrappedKeySecurityToken CreateWrappedKeyToken(string id, string encryptionMethod, string carriedKeyName,
                SecurityKeyIdentifier unwrappingTokenIdentifier, byte[] wrappedKey, SecurityTokenResolver tokenResolver)
            {
                ISspiNegotiationInfo sspiResolver = tokenResolver as ISspiNegotiationInfo;
                if (sspiResolver != null)
                {
                    ISspiNegotiation unwrappingSspiContext = sspiResolver.SspiNegotiation;
                    // ensure that the encryption algorithm is compatible
                    if (encryptionMethod != unwrappingSspiContext.KeyEncryptionAlgorithm)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.BadKeyEncryptionAlgorithm, encryptionMethod)));
                    }
                    byte[] unwrappedKey = unwrappingSspiContext.Decrypt(wrappedKey);
                    return new WrappedKeySecurityToken(id, unwrappedKey, encryptionMethod, unwrappingSspiContext, unwrappedKey);
                }
                else
                {
                    if (tokenResolver == null)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("tokenResolver"));
                    }
                    if (unwrappingTokenIdentifier == null || unwrappingTokenIdentifier.Count == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MissingKeyInfoInEncryptedKey)));
                    }

                    SecurityToken unwrappingToken;
                    SecurityHeaderTokenResolver resolver = tokenResolver as SecurityHeaderTokenResolver;
                    if (resolver != null)
                    {

unwrappingToken = resolver.ExpectedWrapper; if (unwrappingToken != null)

                        {
                            if (!resolver.CheckExternalWrapperMatch(unwrappingTokenIdentifier))
                            {
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(
                                    SR.GetString(SR.EncryptedKeyWasNotEncryptedWithTheRequiredEncryptingToken, unwrappingToken)));
                            }
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(
                                SR.GetString(SR.UnableToResolveKeyInfoForUnwrappingToken, unwrappingTokenIdentifier, resolver)));
                        }
                    }

...

So, in "unwrappingToken = resolver.ExpectedWrapper", I get a "null".

This could be some kind of "messages namespaces mismatch", or something like that, that I do not see?

Certificate it's ok and valid. It has all the x509v3 properties, and the issuer it's a trusted issuer...

Help me guys, my diopters are increasing with this ...

Morcilla de Arroz
  • 2,104
  • 22
  • 29

2 Answers2

0

I think the soap message is wrong. The wsse:Reference element contains a non-existent URI: URI="#EK-4A5A4F8820EFD673E7152328322340610394". Normally this URI should refer to a wsse:BinarySecurityToken element with a wsu:Id value that corresponds to this URI. This appears to be missing from your soap response.

The wsse:BinarySecurityToken element contains the x509 certificate or, in your case a reference to it. Since the URI cannot be resolved, your client has no way to determine the x509 certificate to use.

Hintham
  • 1,078
  • 10
  • 29
  • EK-4A5A4F8820EFD673E7152328322340610394 it's the Encription Key, referenced from Body to Header. – Morcilla de Arroz Apr 13 '18 at 06:30
  • Yes, I know, but it's wrong. The '#' is missing and it needs to be in an `wsu:Id` attribute. You may be suffering from this bug https://issues.apache.org/jira/browse/CXF-2158?attachmentOrder=desc – Hintham Apr 13 '18 at 06:34
  • https://www.oasis-open.org/committees/download.php/21257/wss-v1.1-spec-errata-os-SOAPMessageSecurity.htm, Chapter 8.6. I think the # is unnecessary, and wsu:ID atributte, it's for Tokens.But I will chech the wsse:BinarySecurityToken possibility and tell. thanks . – Morcilla de Arroz Apr 13 '18 at 07:33
0

Well, finally I could get rid of this message. You need to ensure if the service is using same certificate for auth and encription. You can get the public key's cert using fiddler, for example.

If 'NO' is the answer, you will need custom client credentials, something like this:

Public Class MyClientCredentials
  Inherits ClientCredentials


Public Sub New()

End Sub 

' Perform client credentials initialization.    
Protected Sub New(ByVal other As MyClientCredentials)
    MyBase.New(other)
End Sub

''' <summary>
''' Link to token manager
''' </summary>
''' <returns></returns>
Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager
    ' Return your implementation of the SecurityTokenManager.
    Return New MyClientCredentialsSecurityTokenManager(Me)

End Function

Where, the custom token manager is doing something like this:

Public Class MyClientCredentialsSecurityTokenManager
  Inherits ClientCredentialsSecurityTokenManager
Private _oCredenciales As MyClientCredentials

Public Sub New(ByVal credentials As MyClientCredentials)
    MyBase.New(credentials)
    Me._oCredenciales = credentials

End Sub

''' <summary>
''' Custom token for each operation
''' </summary>
''' <param name="p_oRequirement"></param>
''' <returns></returns>
Public Overrides Function CreateSecurityTokenProvider(ByVal p_oRequirement As SecurityTokenRequirement) As SecurityTokenProvider

    Dim oRes As SecurityTokenProvider = Nothing
    If p_oRequirement.TokenType = SecurityTokenTypes.X509Certificate Then
        Dim direction = p_oRequirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty)
        If direction = MessageDirection.Output Then
            If p_oRequirement.KeyUsage = SecurityKeyUsage.Signature Then
                oRes = New X509SecurityTokenProvider(Me._oCredenciales.ClientCertificate.Certificate)
            Else
                oRes = New X509SecurityTokenProvider(Me._oCredenciales.ServiceCertificate.DefaultCertificate())
            End If
        End If
    Else
        oRes = MyBase.CreateSecurityTokenProvider(p_oRequirement)
    End If

    Return oRes

End Function

Now, apply this to channel:

oServicio.ChannelFactory.Endpoint.Behaviors.Remove(Of ClientCredentials)()
oServicio.ChannelFactory.Endpoint.Behaviors.Add(oCred)

I hope this will help someone on future.

Morcilla de Arroz
  • 2,104
  • 22
  • 29