My application (.net 3.5 sp1) uses the HttpWebRequest to communicate with different endpoints, sometimes its over HTTPS where each hosting server may have a different securi
You can achieve this by this code to close all underlying connections and force a new handshake.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
...
...
...
request.ServicePoint.CloseConnectionGroup(request.ConnectionGroupName);
I had the same issue and wrote proxy class, which opens port on localhost and forwards all traffic to specified host:port.
so the connection goes like this
[your code] --- HTTP ---> [proxy on localhost:port] --- HTTPS ---> [web site]
in fact it can be used to wrap any protocol into SSL/TLS not just HTTP
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace System
{
class sslProxy : IDisposable
{
readonly string host;
readonly int port;
readonly TcpListener listener;
readonly SslProtocols sslProtocols;
bool disposed;
static readonly X509CertificateCollection sertCol = new X509CertificateCollection();
public sslProxy(string url, SslProtocols protocols)
{
var uri = new Uri(url);
host = uri.Host;
port = uri.Port;
sslProtocols = protocols;
listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
listener.BeginAcceptTcpClient(onAcceptTcpClient, null);
Proxy = new WebProxy("localhost", (listener.LocalEndpoint as IPEndPoint).Port);
}
public WebProxy Proxy
{
get;
private set;
}
class stBuf
{
public TcpClient tcs;
public TcpClient tcd;
public Stream sts;
public Stream std;
public byte[] buf;
public stBuf dup;
}
void onAcceptTcpClient(IAsyncResult ar)
{
if (disposed) return;
var tcl = listener.EndAcceptTcpClient(ar);
TcpClient tcr = null;
try
{
listener.BeginAcceptTcpClient(onAcceptTcpClient, null);
var nsl = tcl.GetStream();
tcr = new TcpClient(host, port);
Stream nsr = tcr.GetStream();
if (sslProtocols != SslProtocols.None)
{
var sss = new SslStream(nsr, true);
sss.AuthenticateAsClient(host, sertCol, sslProtocols, false);
nsr = sss;
} // if
var sts = new stBuf() { tcs = tcl, sts = nsl, tcd = tcr, std = nsr, buf = new byte[tcl.ReceiveBufferSize] };
var std = new stBuf() { tcs = tcr, sts = nsr, tcd = tcl, std = nsl, buf = new byte[tcr.ReceiveBufferSize] };
sts.dup = std;
std.dup = sts;
nsl.BeginRead(sts.buf, 0, sts.buf.Length, onReceive, sts);
nsr.BeginRead(std.buf, 0, std.buf.Length, onReceive, std);
} // try
catch
{
tcl.Close();
if (tcr != null) tcr.Close();
} // catch
}
void close(stBuf st)
{
var dup = st.dup;
if (dup != null)
{
dup.dup = st.dup = null;
st.sts.Dispose();
st.std.Dispose();
} // if
}
void onReceive(IAsyncResult ar)
{
var st = ar.AsyncState as stBuf;
try
{
if (!(st.dup != null && st.tcs.Connected && st.sts.CanRead && !disposed)) { close(st); return; };
var n = st.sts.EndRead(ar);
if (!(n > 0 && st.tcd.Connected && st.std.CanWrite)) { close(st); return; };
st.std.Write(st.buf, 0, n);
if (!(st.tcs.Connected && st.tcd.Connected && st.sts.CanRead && st.std.CanWrite)) { close(st); return; };
st.sts.BeginRead(st.buf, 0, st.buf.Length, onReceive, st);
} // try
catch
{
close(st);
} // catch
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
listener.Stop();
} // if
}
}
}
usage example
// create proxy once and keep it
// note you have to mention :443 port (https default)
// ssl protocols to use (enum can use | or + to have many)
var p = new sslProxy("http://www.google.com:443", SslProtocols.Tls);
// using our connections
for (int i=0; i<5; i++)
{
// url here goes without https just http
var rq = HttpWebRequest.CreateHttp("http://www.google.com/") as HttpWebRequest;
// specify that we are connecting via proxy
rq.Proxy = p.Proxy;
var rs = rq.GetResponse() as HttpWebResponse;
var r = new StreamReader(rs.GetResponseStream()).ReadToEnd();
rs.Dispose();
} // for
// just dispose proxy once done
p.Dispose();
Set all of this. In my application it is work for different security protocols.
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Ssl3
| System.Net.SecurityProtocolType.Tls12
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls;
After some of our vendors stopped support for ssl3 while other use it exclusively, many issues appear in our system that could be resolved with functionality from this question. But six years after, we still don't have built in mechanism to achieve this. Our workaround is to explicitly define security protocol that will support all scenarios, like this:
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Ssl3
| System.Net.SecurityProtocolType.Tls12
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls;
Unfortunately, it doesnt look like you can customize this per service point. I would suggest that you file a feature request at the MS Connect website for this area.
As a dirty workaround, you could try executing the sites that require a different security protocol in a new appdomain. Static instances are per appdomain, so that should give you the isolation you need.
As per this answer, the SecurityProtocol setting is actually per-AppDomain, so you could, if you were determined to make it work, create separate AppDomains for separate settings, and marshall your queries across.
Not exactly a "neat" solution, but might just make what you need possible without resorting to third-party libraries.