28 January, 2009
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
I got the following custom binding
keyEntropyMode=”CombinedEntropy” messageProtectionOrder=”SignBeforeEncryptAndEncryptSignature”
messageSecurityVersion=”WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10″
requireSecurityContextCancellation=”true” requireSignatureConfirmation=”false”>
replayWindow=”00:05:00″ sessionKeyRenewalInterval=”10:00:00″
sessionKeyRolloverInterval=”00:05:00″ reconnectTransportOnFailure=”false”
timestampValidityDuration=”00:05:00″ cookieRenewalThresholdPercentage=”60″ />
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″ />
securityHeaderLayout=”Strict” includeTimestamp=”true” keyEntropyMode=”CombinedEntropy”
messageProtectionOrder=”SignBeforeEncryptAndEncryptSignature”
messageSecurityVersion=”WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10″
requireSecurityContextCancellation=”true” requireSignatureConfirmation=”false”>
replayWindow=”00:05:00″ sessionKeyRenewalInterval=”10:00:00″
sessionKeyRolloverInterval=”00:05:00″ reconnectTransportOnFailure=”true”
timestampValidityDuration=”00:05:00″ cookieRenewalThresholdPercentage=”60″ />
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″ />
bypassProxyOnLocal=”false” hostNameComparisonMode=”StrongWildcard”
keepAliveEnabled=”true” maxBufferSize=”65536000″ proxyAuthenticationScheme=”Anonymous”
realm=” transferMode=”Buffered” unsafeConnectionNtlmAuthentication=”false”
useDefaultWebProxy=”true” />
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.