Thursday, June 24, 2010

How to use SSL3 instead of TLS in a particular HttpWebRequest

My application has to talk to different hosts over https, and the default setting of ServicePointManager.SecurityProtocol = TLS served me well. The other day, though, I had some NetWare hosts which (as System.Net trace log shows) don't answer the initial TLS handshake message but keep the underlying connection open until it times out, throwing a timeout exception. It seems that Netware's policy regarding unrecognized/invalid requests is not to respond or give any error messages, presumably to reduce attack surface. Very understandable, but this behaviour does not give .NET's built-in TLS-to-SSL3 fallback mechanism a chance to kick in.
I really didn't want to have to degrade the security protocol setting to SSL3 in the whole application for the sake of a few musty Netware hosts, but this ServicePointManager setting is global and there is no way to force a downgrade through HttpWebRequest. Luckily, 'global' has more than one meaning in the .NET world; ServicePointManager settings are actually per-appdomain. This enabled me to work around the problem by creating a separate appdomain set up to use only SSL3, making my data collection object MarshalByRefObject (WebClient and WebRequest are marshal-by-ref too, but better to reduce the number of cross-appdomain calls and avoid marshaling anything more complicated than a string) and creating it there. Worked perfectly combined with a timeout-based detection scheme.