Home > Development > WCF – WSHttpBinding and Clock Skew

WCF – WSHttpBinding and Clock Skew

I was deploying a WCF service and when testing it started to get the following error:

The security timestamp is invalid because its creation time (’2009-01-28T16:18:26.625Z’) is in the future. Current time is ‘2009-01-28T16:11:40.173Z’ and allowed clock skew is ‘00:05:00′.

A quick search of the internet threw light on what the problem was, by default in WCF the clocks on the client and server have to be within 5 minutes of each other. Now this can be controlled if all the machines involved are on the same network, but as soon as you start having clients on other networks, you will get them with bigger time differences than this. After another bit of searching I found that you can control the size of the allowed clock skew. However, you can’t do this with the WSHttpBinding, you have to create a custom binding.

The problem then is that the documentation about doing this in web.config is pretty poor. With a custom binding you start with nothing about your connection set up and have to build it from scratch, which given the complexity of the possble binding is really difficult. I found several posts in newsgroups asking about it, but for a long time couldn’t find a decent answer. Then I stumbled across http://social.msdn.microsoft.com/forums/en-US/wcf/thread/6554776e-4b05-427f-ad6f-5d72c6579746/ and discovered that you could programmatically create the binding, convert it to a custom binding and save it to a configuration file, which will then contain all the settings you need to duplicate your original binding, but within a custom binding where I could then set maxClockSkew.

The code I used is:

public static void Main(){     //Create the custom binding based on your original binding     WSHttpBinding binding = new WSHttpBinding();     binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;     binding.SendTimeout = new TimeSpan(0, 15, 0);     binding.MaxReceivedMessageSize = 65536000;     CustomBinding custom = new CustomBinding(binding);     //Set up the config file to save     Configuration machineConfig = ConfigurationManager.OpenMachineConfiguration();     ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();     fileMap.ExeConfigFilename = "out.config";     fileMap.MachineConfigFilename = machineConfig.FilePath;     Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);     config.NamespaceDeclared = true;     //Save the binding in the config file     ServiceContractGenerator scg = new ServiceContractGenerator(config);     string sectionName;     string configName;     scg.GenerateBinding(custom, out sectionName, out configName);     config.Save();}So from my original binding specification

<wsHttpBinding>    <binding name="SalamanderBinding" receiveTimeout="00:15:00"            maxReceivedMessageSize="65536000">            <security mode="Message">                <message clientCredentialType="UserName"/>            </security>       <readerQuotas maxArrayLength="65536000"/>     </binding></wsHttpBinding>

I got the following custom binding

<customBinding>    <binding name="SalamanderBinding">        <transactionFlow transactionProtocol="WSAtomicTransactionOctober2004" />        <security defaultAlgorithmSuite="Default" authenticationMode="SecureConversation"            requireDerivedKeys="true" securityHeaderLayout="Strict" includeTimestamp="true"            keyEntropyMode="CombinedEntropy" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"            messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"            requireSecurityContextCancellation="true" requireSignatureConfirmation="false">            <localClientSettings cacheCookies="true" detectReplays="true"                replayCacheSize="900000" maxClockSkew="00:05:00" maxCookieCachingTime="Infinite"                replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00"                sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="false"                timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="60" />            <localServiceSettings detectReplays="true" issuedCookieLifetime="10:00:00"                maxStatefulNegotiations="128" replayCacheSize="900000" maxClockSkew="00:05:00"                negotiationTimeout="00:01:00" replayWindow="00:05:00" inactivityTimeout="00:02:00"                sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00"                reconnectTransportOnFailure="false" maxPendingSessions="128"                maxCachedCookies="1000" timestampValidityDuration="00:05:00" />            <secureConversationBootstrap defaultAlgorithmSuite="Default"                authenticationMode="UserNameForSslNegotiated" requireDerivedKeys="true"                securityHeaderLayout="Strict" includeTimestamp="true" keyEntropyMode="CombinedEntropy"                messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"                messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"                requireSecurityContextCancellation="true" requireSignatureConfirmation="false">                <localClientSettings cacheCookies="true" detectReplays="true"                    replayCacheSize="900000" maxClockSkew="00:05:00" maxCookieCachingTime="Infinite"                    replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00"                    sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true"                    timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="60" />                <localServiceSettings detectReplays="true" issuedCookieLifetime="00:15:00"                    maxStatefulNegotiations="128" replayCacheSize="900000" maxClockSkew="00:05:00"                    negotiationTimeout="00:01:00" replayWindow="00:05:00" inactivityTimeout="00:02:00"                    sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00"                    reconnectTransportOnFailure="true" maxPendingSessions="128"                    maxCachedCookies="1000" timestampValidityDuration="00:05:00" />            </secureConversationBootstrap>        </security>        <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"            messageVersion="Default" writeEncoding="utf-8">            <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"                maxBytesPerRead="4096" maxNameTableCharCount="16384" />        </textMessageEncoding>        <httpTransport manualAddressing="false" maxBufferPoolSize="524288"            maxReceivedMessageSize="65536000" allowCookies="false" authenticationScheme="Anonymous"            bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"            keepAliveEnabled="true" maxBufferSize="65536000" proxyAuthenticationScheme="Anonymous"            realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"            useDefaultWebProxy="true" />    </binding></customBinding>

and I just needed to change the maxClockSkew values.

You do need to set the clock skew on the client and server sides.

Easy when you know how.

Categories: Development Tags:
  1. Jeff
    February 17th, 2009 at 17:07 | #1

    I’ve posted a suggestion to Microsoft’s Connect produce feedback site to have the MaxClockSkew configurable for standard bindings. If you want to see this change check out the link below and vote.

    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=415658

  2. Richard
    February 23rd, 2009 at 13:47 | #2

    Hi Jeff,Good idea, I’ve voted for it.

  1. No trackbacks yet.