WCF: Introp - Signing without primary signature requires timestamp.

WCF: Introp - Signing without primary signature requires timestamp.

Security Requirement:

  1. SSL Channel
  2. SAML token for authentication as part of <security> header
  3. TimeStamp being added after the SAML Token

 

Working request from .Net client:

<wsse:Security S:mustUnderstand="true">

<wsu:Timestamp xmlns:ns17="https://docs.oasis-open.org/ws-sx/ws-

secureconversation/200512" xmlns:ns16="https://schemas.xmlsoap.org/soap/envelope/"

wsu:Id="_1">

<wsu:Created>2015-12-23T16:30:10Z</wsu:Created>

<wsu:Expires>2015-12-23T16:35:10Z</wsu:Expires>

</wsu:Timestamp>

<saml2:Assertion xmlns:ds="https://www.w3.org/2000/09/xmldsig#"

xmlns:exc14n="https://www.w3.org/2001/10/xml-exc-c14n#"

xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"

xmlns:xenc="https://www.w3.org/2001/04/xmlenc#"

xmlns:xs="https://www.w3.org/2001/XMLSchema" ID="_906f6505770a46018fa4d9fed4fc9713"

IssueInstant="2015-12-23T16:30:10.153Z" Version="2.0">

</saml2:Assertion>

<ds:Signature xmlns:ns17="https://docs.oasis-open.org/ws-sx/ws-secureconversation/200512"

xmlns:ns16="https://schemas.xmlsoap.org/soap/envelope/" Id="_2">

</ds:Signature>

</wsse:Security>

 

Failure request from Java client:

<wsse:Security xmlns:wsse="https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="true">

<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"

xmlns:xs="https://www.w3.org/2001/XMLSchema" ID="_517089b7d6ec435da62fcdead4ec067a"

IssueInstant="2015-12-23T16:22:57.014Z" Version="2.0">

</saml2:Assertion>

<ds:Signature xmlns:ds="https://www.w3.org/2000/09/xmldsig#" Id="SIG-1224">

</ds:Signature>

<wsu:Timestamp wsu:Id="TS-1223">

<wsu:Created>2015-12-23T16:22:57.014Z</wsu:Created>

<wsu:Expires>2015-12-23T17:22:57.014Z</wsu:Expires>

</wsu:Timestamp>

</wsse:Security>

 

Failure stack from WCF traces:

<ExceptionType>System.ServiceModel.Security.MessageSecurityException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Signing without primary signature requires timestamp.</Message><StackTrace>at System.ServiceModel.Security.ReceiveSecurityHeader.ProcessSupportingSignature(SignedXml signedXml, Boolean isFromDecryptedSource)at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(Message&amp; message, TimeSpan timeout)at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout)at System.ServiceModel.Security.SecurityProtocol.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)at System.ServiceModel.Channels.SecurityChannelListener`1.ServerSecurityChannel`1.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationState)

 

Reason for failure:

WCF expects Timestamp to be on top of the signature element which is used to sign it.

 

Specified in WS standards:

https://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/ws-securitypolicy-1.2-spec-os.html#_Toc161826554

Capture 

 

 

 

 

 

 

Signed elements inside the security header MUST occur before the signature that signs them.

For example: A timestamp MUST occur before the signature that signs it.

For above requirement, we are using custom binding with following configuration:

For testing, I am using KerberosOverTransport, but it can also be IssuedTokenOverTransport (to meet above req).

<customBinding>

<binding name="NewBinding1">

<textMessageEncoding />

<security authenticationMode="KerberosOverTransport"

messageProtectionOrder="SignBeforeEncrypt"

securityHeaderLayout="Lax"

allowInsecureTransport="true" enableUnsecuredResponse="true" />

<httpsTransport />

</binding>

</customBinding>

As we can observe even after setting security Header Layout to “Lax”, (defaulted to “Strict”) which should ideally make sure that the position of timestamp should not matter… it still fails.

At this point, I am not sure why… it so far it may be a design issue.

 

Workaround?

Current custom binding requires SAML/Windows token as Endorsing Supporting Token. For which WSDL looks like this:

EndorsingSupportingTokens being seen in WSDL of the service...

====

-<sp:EndorsingSupportingTokens xmlns:sp="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy">

-<wsp:Policy>

-<sp:KerberosToken sp:IncludeToken="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Once">

-<wsp:Policy>

<sp:WssGssKerberosV5ApReqToken11/>

</wsp:Policy>

</sp:KerberosToken>

</wsp:Policy>

</sp:EndorsingSupportingTokens>

 

Now the question is what we need if not Endorsing Supporting Token:

For current requirement, since we are using SAML token for user authentication only.

We just need a SignedSupportingToken:

 

In our case SAML/Kerberos Token are acting like a Supporting Token and not the Primary Token.

- <sp:SignedSupportingTokens xmlns:sp="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy">

- <wsp:Policy>

- <sp:KerberosToken sp:IncludeToken="https://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Once">

- <wsp:Policy>

<sp:RequireDerivedKeys />

<sp:WssGssKerberosV5ApReqToken11 />

</wsp:Policy>

</sp:KerberosToken>

</wsp:Policy>

</sp:SignedSupportingTokens>

 

https://blogs.msdn.com/b/govindr/archive/2006/10/16/supporting-tokens.aspx

 

Primary Token:

The Primary token is the main token that provides security to the message. This signs the message body and other headers as required and serves as the main identity token for the sending party

Supporting Token:

They provide more information about the client. An example of a supporting token can be a Username/Password Token. WCF does not derive tokens from Username/Password and hence this cannot be used as the primary token. In this case the binding between the client and service can be secured with a Mutual Certificate or Kerberos, as the case be, and then you can add the Username/Password token as a Supporting token.

 

Endorsing Supporting Token:

These tokens have keys associated with them and will sign the primary signature and add another signature element to the message called the secondary signature. As you would imagine the secondary signature contains only one reference and it is the signature over the primary signature.

 

In our case when we use custom binding, the WSDL end up in using Endorsing Supporting Token. Because of which we need to have Time Stamp on top of the actual Signature used for signing it. The error which gets thrown believes that there was not timestamp read.

 

<ExceptionType>System.ServiceModel.Security.MessageSecurityException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

<Message>Signing without primary signature requires timestamp. </Message>

<StackTrace>

at System.ServiceModel.Security.ReceiveSecurityHeader.ProcessSupportingSignature(SignedXml signedXml, Boolean isFromDecryptedSource)

at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)

at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)

 

Actual Code:

Capture

 

 

 

 

 

 

 

 

Now the question is why the timestamp is Null?

Cannot answer it now, but this is what the codes says.

 

WS Standard

==========

https://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/ws-securitypolicy-1.2-spec-os.html

 

Exact reason described here

=======

https://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/ws-securitypolicy-1.2-spec-os.html#_Toc161826564

If transport security is used, the signature (Sig2) MUST cover the message timestamp

 

For Issued Token

======

https://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/ws-securitypolicy-1.2-spec-os.html#_Toc161826536

 

To understand the impact of order:

=============

https://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/ws-securitypolicy-1.2-spec-os.html#_Toc161826554

https://docs.oasis-open.org/wss-m/wss/v1.1.1/os/wss-SOAPMessageSecurity-v1.1.1-os.html#_Toc307407973

 

Workaround 1

To find a workaround, since we understand we need a Signed Supporting Token. We need to write a custom code to create the required binding who really does not care about the Time Stamp validation… All it use is Transport Security and read security header to drive the SUPPORTING token for authentication.

 

 

We propose following in-code configuration...

 

static Binding GetBinding()

{

SecurityBindingElement security = new TransportSecurityBindingElement();

KerberosSecurityTokenParameters item = new KerberosSecurityTokenParameters();

//IssuedSecurityTokenParameters

 

security.EndpointSupportingTokenParameters.SignedEncrypted.Add(item);

security.IncludeTimestamp = false;

security.MessageSecurityVersion =

MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;

 

TextMessageEncodingBindingElement encoding = new

TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);

 

HttpsTransportBindingElement transport = new HttpsTransportBindingElement();

 

Binding binding = new CustomBinding(security, encoding, transport);

return binding;

}

 

With the above binding we can configure service to read security header and received token as Supporting Token and not Endorsing Supporting Token and as a result position of time stamp won’t matter now.

Workaround 2:

If code based changes are not an option for us, we can design IReplyChannel from WCF Extensibility and modify the message on the fly at server side.

 

Workaround 3:

Write a customer message Encode and handle the incoming request.

 

Link for samples:

https://onedrive.live.com/redir?resid=7A701D22BF5927B8!2464&authkey=!ABXw7Ge9nP8ZUxE&ithint=folder%2c

Response from PG

This is a known issue that WCF service ignores this setting. There is a fix behind an AppSetting. If we can upgrade to .NET 4.6+, we should be able to enable the below given appsetting directly. Otherwise there is a hotfix that needs to be applied before enabling the setting.

 

To enable the app setting, we just need to modify the config file and add the following entry:

<configuration>

<appSettings>

<add key="wcf:useConfiguredTransportSecurityHeaderLayout" value="true" />

</appSettings>

</configuration>

 

Regarding the hotfix for .NET framework 4.5.2, that is available in the KB – 3035814.

Though the online articles may not describe the fix made in WCF security header layout property, but the fix is actually rolled out as a part of this package.

This is not available for direct download. DEV can contact MS Support team if they need this hotfix.

 

I hope this help.

Saurabh Somani