Code through the pain Ladislav Mrnka's professional blog about software development

31Aug/112

Last week I had to prepare simple security configuration for WCF service - I needed to configure a test service and a client using mutual HTTPS to achieve secure transport and X.509 Binary Token to authenticate requests. Requests should contain only a message data and a security token and no other security headers like a timestamp were allowed. This proved to be more challenging and frustrating than I expected. I started with the simplest possible configuration using CertificateOverTransport authentication mode but it failed with the exception:

System.InvalidOperationException: Signing without primary signature requires timestamp

I found that I'm not the first one who dealt with this problem. MS Connect contains related  "bug" where I expected to find a solution or workaround because WCF is API designed for interoperability with other platforms, isn't it? Surprisingly I didn't find a solution but instead some very odd answer describing that behaviour is "by design". Well, in the context of the used configuration it is really by design as I will describe in this post but the point of the bug was how to do it and Microsoft representative completely failed to answer the question and marked the problem as fixed because of "by design". This really made me furious so I even wrote my own question to MSDN forum. The rest of this article describes the whole route to the working solution as well as description why initial solution didn't work.

My initial approach used common custom binding with security binding element configured to use CertificateOverTransport authenticated mode and timestamp turned off:

<customBinding>
 <binding name="MutualHttpsWithBinaryToken">
   <security authenticationMode="CertificateOverTransport" includeTimestamp="false"
             messageSecurityVersion="WSSecurity10..." />
    <textMessageEncoding messageVersion="Soap11" />
    <httpsTransport requireClientCertificate="true" />
  </binding>
</customBinding>

This configuration looks quite straightforward and exactly like what I wanted. I created the service first and hosted it in IIS. I used Add Service Reference to a create proxy in test client and used my client application to test communication with the service. That resulted in mentioned System.InvalidOperationException. Why? The reason why this happens is part of WS-* specification related to security and security policies. Anyway it is interesting that we can host such service but we cannot consume it.

The related part of security policy assertions described in WSDL:

<sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:TransportToken>
      <wsp:Policy>
        <sp:HttpsToken RequireClientCertificate="true"/>
      </wsp:Policy>
    </sp:TransportToken>
    <sp:AlgorithmSuite>
      <wsp:Policy>
        <sp:Basic256/>
      </wsp:Policy>
    </sp:AlgorithmSuite>
    <sp:Layout>
      <wsp:Policy>
        <sp:Strict/>
      </wsp:Policy>
    </sp:Layout>
  </wsp:Policy>
</sp:TransportBinding>
<sp:EndorsingSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:X509Token sp:IncludeToken="http://.../ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
      <wsp:Policy>
        <sp:RequireThumbprintReference/>
        <sp:WssX509V3Token10/>
      </wsp:Policy>
    </sp:X509Token>
  </wsp:Policy>
</sp:EndorsingSupportingTokens>
<sp:Wss11 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:MustSupportRefThumbprint/>
  </wsp:Policy>
</sp:Wss11>

As we can see the security assertion defines that service expects X.509 token as an endorsing supporting token (it also uses WS-Security 1.1 and certificate thumbprint reference for token which I didn't want). By definition the endorsing token is in case of a symmetric and an asymmetric security bindings (full message security) used to sign the primary signature but in case of a transport security binding it is used to sign the timestamp. The reason why the timestamp must be signed is described in mentioned MS Connect bug. By signing the timestamp the client proves ownership of a correct private key to passed X.509 token (certificate) because the service will validate the signature with a public key passed in the token.

How to solve the issue? Yaron Naveh pointed me out that this must be already solved in UserNameOverTransport authentication mode because am user name token cannot be used to sign a message (at least not the part of the specification implemented in WCF). WS-SecurityPolicy defines multiple ways how to include tokens but generally there are two categories - supporting tokens and supporting endorsing tokens. This scenario requires usage of a supporting token which is only included in a message and not used to sign anything. WCF doesn't support pure supporting tokens. It only supports signed supporting tokens and signed encrypted supporting tokens but that is not a problem when used together with transport binding element where HTTPS provides both signing and encryption.

When creating such binding for WCF we cannot use the configuration only approach. WCF doesn't expose all its binding features in the configuration - namely we cannot define tokens assertions in the configuration. The easiest way to define such binding is using CustomBinding in code:

SecurityBindingElement security = new TransportSecurityBindingElement();
X509SecurityTokenParameters item =
    new X509SecurityTokenParameters(X509KeyIdentifierClauseType.Any,
                                    SecurityTokenInclusionMode.AlwaysToRecipient);
security.EndpointSupportingTokenParameters.SignedEncrypted.Add(item);
security.IncludeTimestamp = false;
security.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10...;
TextMessageEncodingBindingElement encoding =
    new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
transport.RequireClientCertificate = true;

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

This configuration can be wrapped in a new binding class which can be exposed as binding extension to WCF's configuration. Using this binding in WCF service produces following security policy assertions in WSDL:

<sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:TransportToken>
      <wsp:Policy>
        <sp:HttpsToken RequireClientCertificate="true"/>
      </wsp:Policy>
    </sp:TransportToken>
    <sp:AlgorithmSuite>
      <wsp:Policy>
        <sp:Basic256/>
      </wsp:Policy>
    </sp:AlgorithmSuite>
    <sp:Layout>
      <wsp:Policy>
        <sp:Strict/>
      </wsp:Policy>
    </sp:Layout>
  </wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:X509Token sp:IncludeToken="http://.../ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
      <wsp:Policy>
        <sp:RequireDerivedKeys/>
        <sp:WssX509V3Token10/>
      </wsp:Policy>
    </sp:X509Token>
  </wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
  <wsp:Policy>
    <sp:MustSupportRefKeyIdentifier/>
    <sp:MustSupportRefIssuerSerial/>
  </wsp:Policy>
</sp:Wss10>

This is exactly binding configuration required to fulfil initial requirements: it uses mutual HTTPS and it passes X.509 Binary token in requests without passing any other security headers (responses contains no security headers at all). The interesting part is the security assertion of the token. Even we added the token as the signed and encrypted supporting token it is asserted as only signed supporting token. I'm not sure if this is exactly by the specification or if it is a hack but till it work I'm happy with it.

As we can see the solution was at the end quite easy so why didn't Microsoft representative provide it as a workaround in MS Connect bug?

Posted on August 31, 2011 by Ladislav Mrnka
Filed under: WCF
Leave a comment
Comments (2) Trackbacks (0)
  1. Hi Ladislav,

    As you’re experiencing, WCF takes an “interesting” view on interoperability. Basically, the designers seemed to make deviating from the WS standards quite difficult. I have experienced this a bit myself. As you’ve noted, the creator of the service is the one you should be angry at. WS-SecurityPolicy states quite clearly that if transport security is used, then the supporting endorsing token MUST sign the timestamp.

    Nice workaraound though – and one that’s quite handy for me so thanks. As to why the MS rep couldn’t help, well that’s another story!

    Thanks Again,

    mal

  2. Krásný den,

    potřebujeme pomoc při integraci dvou systémů. A vzhledem k tomu, že jsme v taktéž v Praze, tak jsem se chtěl zeptat, jestli si Vás můžeme na pár hodin najmout…

    Je to naprosto stoprocentně Vaše parketa.

    Pište na email, docela nám to spěchá. Takže díky za odpověď. (I ne je odpověď)

    S díky

    Vladimír Vencálek


Leave a comment

(required)

No trackbacks yet.