Using .NET, I need to call a third party API which uses mutual authentication and dual encryption using mode Transport bindings. They have successfully received and decrypted my Request, processed it and sent a Response.
In the Header of the SOAP Response is a Security element. This contains an EncryptedKey and Signature element. The Body is separately encrypted.
The key to decrypt the body is contained in the above EncryptedKey element but the actual key within is also encrypted. In the same element is a X509Data element providing certificate details (IssuerName and SerialNumber) to decrypt that key, which can be used to decrypt the Body.
Received Encrpted Response
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<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">
<wsu:Timestamp wsu:Id="TS-ba9edb63-214d-4d6b-8040-ed715a1326c7"></wsu:Timestamp>
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-fede8959-6d80-4166-9e79-4a4229efee4f">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"></xenc:EncryptionMethod>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=Their Name,O=Their Organisation</ds:X509IssuerName>
<ds:X509SerialNumber>1234567*************************987654</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>K+l28afn3i8e..Encrypted...Key....4gnOHW0kxK7aL6l7Q==</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList></xenc:ReferenceList>
</xenc:EncryptedKey>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-77d3082f-2668-4347-9582-83d1ba9f0f8d">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"> </ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
<ds:Reference URI="#TS-ba9edb63-214d-4d6b-8040-ed715a1326c7">
<ds:Transforms> </ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>yKJziO....SrJPNE=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#_2d4d85db-41d7-446b-87f1-0a839b9b3e7f">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>9PNS98w.....KMWU0=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>EaqS4/iiXybpMe6r........RFM2NM6D3X/zpQ==</ds:SignatureValue>
<ds:KeyInfo Id="KI-a6793d90-a124-4960-ae17-f07c43248f53">
<wsse:SecurityTokenReference wsu:Id="STR-1d682670-9dde-40e1-ad14-e29c9c62c474">
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=Their Name,O=Their Organisatio</ds:X509IssuerName>
<ds:X509SerialNumber>7654321*************************456789</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</SOAP-ENV:Header>
<soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_2d4d85db-41d7-446b-87f1-0a839b9b3e7f">
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-3900ed5b-952f-4d4a-976b-6a3e86967010" Type="http://www.w3.org/2001/04/xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></xenc:EncryptionMethod>
<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-fede8959-6d80-4166-9e79-4a4229efee4f"></wsse:Reference>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>7KCEA0yCcd.....Encrypted....Body....O0AIbjdJ3gKQy8=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soap:Body>
</soap:Envelope>
THE PROBLEM : How do I configure the .NET client to: read the x509SerialNumber to get the cert to decrypt EncryptedKey.CipherValue (encrypted...key) then use this to decrypt the body????
When I try and get the Response in code:
Try
Dim response As TheirService.TheirMethodResponse = client.DoMethodAction(request)
Catch ex As Exception
Dim test As String = ex.Message
End Try
I get the following Exception (taken from trace logs):
<Exception>
<ExceptionType>System.ServiceModel.Security.MessageSecurityException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>
Cannot resolve KeyInfo for unwrapping key: KeyInfo 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = X509IssuerSerialKeyIdentifierClause(Issuer = 'CN=Their Name,O=Their Organisation', Serial = '1234567*************************987654')
)
', available tokens 'SecurityTokenResolver
(
TokenCount = 1,
TokenEntry[0] = (AllowedReferenceStyle=External, Token=System.ServiceModel.Security.Tokens.WrappedKeySecurityToken, Parameters=System.ServiceModel.Security.Tokens.WrappedKeySecurityTokenParameters:
InclusionMode: Once
ReferenceStyle: Internal
RequireDerivedKeys: True)
)
'.
</Message>
<StackTrace>
at System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.CreateWrappedKeyToken(String id, String encryptionMethod, String carriedKeyName, SecurityKeyIdentifier unwrappingTokenIdentifier, Byte[] wrappedKey, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.ReadTokenCore(XmlDictionaryReader reader, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityTokenSerializer.ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
at System.IdentityModel.Selectors.SecurityTokenSerializer.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityOneDotZeroReceiveSecurityHeader.DecryptWrappedKey(XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.ReadEncryptedKey(XmlDictionaryReader reader, Boolean processReferenceListIfPresent)
at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)
at System.ServiceModel.Security.StrictModeSecurityHeaderElementInferenceEngine.ExecuteProcessingPasses(ReceiveSecurityHeader securityHeader, XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
at System.ServiceModel.Security.MessageSecurityProtocol.ProcessSecurityHeader(ReceiveSecurityHeader securityHeader, Message& message, SecurityToken requiredSigningToken, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Security.SymmetricSecurityProtocol.VerifyIncomingMessageCore(Message& message, String actor, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Security.MessageSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at SIA_Test_Project.SiaSetupCard.SetupCardService.setupCard2(setupCard2Request request)
at SIA_Test_Project.SiaSetupCard.SetupCardServiceClient.SiaSetupCard_SetupCardService_setupCard2(setupCard2Request request)
at SIA_Test_Project.Module1.Main()
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
</StackTrace>
<ExceptionString>
System.ServiceModel.Security.MessageSecurityException: Cannot resolve KeyInfo for unwrapping key: KeyInfo 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = X509IssuerSerialKeyIdentifierClause(Issuer = 'CN=Their Name,O=Their Organisation', Serial = '1234567*************************987654')
)
', available tokens 'SecurityTokenResolver
(
TokenCount = 1,
TokenEntry[0] = (AllowedReferenceStyle=External, Token=System.ServiceModel.Security.Tokens.WrappedKeySecurityToken, Parameters=System.ServiceModel.Security.Tokens.WrappedKeySecurityTokenParameters:
InclusionMode: Once
ReferenceStyle: Internal
RequireDerivedKeys: True)
)
'.
</ExceptionString>
</Exception>
How I'm setting up the Client:
Dim binding As New Channels.CustomBinding
Dim securityBE As SymmetricSecurityBindingElement = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10)
securityBE.MessageProtectionOrder = Security.MessageProtectionOrder.SignBeforeEncrypt
securityBE.RequireSignatureConfirmation = False
securityBE.EnableUnsecuredResponse = False
binding.Elements.Add(securityBE)
binding.Elements.Add(New TextMessageEncodingBindingElement(MessageVersion.Soap11, System.Text.Encoding.UTF8))
Dim http As New HttpsTransportBindingElement()
http.RequireClientCertificate = True
binding.Elements.Add(http)
Dim endPoint As New EndpointAddress(New Uri("https://blar/blar/services/TheirMethodService"), EndpointIdentity.CreateDnsIdentity("Their Name"))
Dim client As New TheirService.TheirServiceServiceClient(binding, endPoint)
Dim credentials As New ClientCredentials
'Their cert where we have the public key only
credentials.ServiceEncryptingCert = GetCert("c40ca927................c94640cdb7")
'our their company's mutual auth cert where we have the private key
credentials.ClientEncryptingCert = GetCert("e3debd49................e9a6cd39c5")
'our their company's digital signing cert where we have the private key
credentials.ClientSigningCert = GetCert("40091641................d622761b")
client.ChannelFactory.Endpoint.EndpointBehaviors.Remove(GetType(Description.ClientCredentials))
client.ChannelFactory.Endpoint.EndpointBehaviors.Add(credentials)
'only sign the message do not encrypt it
client.Endpoint.Contract.ProtectionLevel = ProtectionLevel.EncryptAndSign
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls Or System.Net.SecurityProtocolType.Tls12
Dim request As New TheirService.TheirMethodRequest
request.ProductRequestItem = New TheirService.ProductRequestItem
'Construct Request
Try
Dim response As TheirService.TheirMethodResponse = client.DoMethodAction(request)
Catch ex As Exception
Dim test As String = ex.Message
End Try
When debugging, requirement.GetProperty in the following Function throws an Exception when querying the Response:
Public Overrides Function CreateSecurityTokenProvider(requirement As SecurityTokenRequirement) As SecurityTokenProvider
Dim result As SecurityTokenProvider
If requirement.TokenType = System.IdentityModel.Tokens.SecurityTokenTypes.X509Certificate Then
Dim direction As MessageDirection
Dim noDirection As Boolean = False
Try
direction = requirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty)
Catch ex As Exception
noDirection = True
End Try
'this may be the client cert
If noDirection Then
result = New X509SecurityTokenProvider(Me.credentials.ClientEncryptingCert)
Else
If direction = MessageDirection.Output Then
If requirement.KeyUsage = IdentityModel.Tokens.SecurityKeyUsage.Signature Then
result = New X509SecurityTokenProvider(Me.credentials.ClientSigningCert)
Else
result = New X509SecurityTokenProvider(Me.credentials.ServiceEncryptingCert)
End If
Else
If requirement.KeyUsage = IdentityModel.Tokens.SecurityKeyUsage.Signature Then
result = New X509SecurityTokenProvider(Me.credentials.ServiceEncryptingCert)
Else
result = New X509SecurityTokenProvider(Me.credentials.ClientSigningCert)
End If
End If
End If
Else
result = MyBase.CreateSecurityTokenProvider(requirement)
End If
Return result
End Function
This is incredibly frustrating. Any help will be much appreciated. Thanks in advance.
Found the resolution - I was very close: Use the AsymmetricSecurityBindingElement and CreateMutualCertificateDuplexBindingElement objects.
Under How I'm setting up the Client:
I was using this which caused the Exception...
Replace with this line and all will work: