does anyone know, how I can implement the TLS-ALPN in .NET?
I've implemented a basic HTTP/2 server, but without TLS encryption. I searched in google, but I only found resources for C, Java or other languages, but nothing for .NET (C#)
does anyone know, how I can implement the TLS-ALPN in .NET?
I've implemented a basic HTTP/2 server, but without TLS encryption. I searched in google, but I only found resources for C, Java or other languages, but nothing for .NET (C#)
According to HttpTwo project on Github it is not possible currently because of a bug.
Update: It's not supported in .NET. You can vote for it here: https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/6264363-add-support-for-alpn-to-system-net-security-sslstr
quote:
The HTTP/2 RFC states that secure connections must use ALPN to negotiate the protocol. Unfortunately, .NET's SslStream has no ability to specify application protocols as part of the TLS authentication, so it can't support ALPN. There's an issue tracking this on dotnetfix however it seems like this isn't going to happen very soon (especially on mono and .NET 4.x).
It actually is possible. With some reflection you can inject any extension in client or server hello.
Here's some code to give you an idea:
// Refer IANA on ApplicationProtocols: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
public static void FixALPN(params string[] protocols) {
if (Interlocked.Increment(ref cntFixALPN) > 1)
{
throw new Exception("FixALPN should be called only ONCE, put it in your Main or use a static constructor.");
return;
}
// get the needed (internal) System types
string tpname = typeof(System.Net.HttpListener).AssemblyQualifiedName;
Type tpiface = Type.GetType(tpname.Replace("HttpListener", "SSPIInterface"));
Type tpgsspi = Type.GetType(tpname.Replace("HttpListener", "GlobalSSPI"));
Type tpsdc = Type.GetType(tpname.Replace("HttpListener", "SafeDeleteContext"));
Type tpsecbuf = Type.GetType(tpname.Replace("HttpListener", "SecurityBuffer"));
// create ALPN buffer
ConstructorInfo ci = (from x in tpsecbuf.GetConstructors() where x.GetParameters().Length == 4 select x).First();
var secbufempty = ci.Invoke(new object[] { new byte[0], 0, 0, 0 });
byte[] btsalpn = GetALPNBuffer(protocols);
var secbufalpn = ci.Invoke(new object[] { btsalpn, 0, btsalpn.Length, 18 });
// grab the object to replace...
FieldInfo fi = tpgsspi.GetField("SSPISecureChannel", BindingFlags.NonPublic | BindingFlags.Static);
var secchan = fi.GetValue(null);
// ...and the method(s) we'll use in our intercepted call(s)
MethodInfo miSDC_ISC = tpsdc.GetMethod("InitializeSecurityContext", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo miSDC_ASC = tpsdc.GetMethod("AcceptSecurityContext", BindingFlags.NonPublic | BindingFlags.Static);
// fake the internal interface
var result = new InterfaceImplementer(tpiface, (mcm) => {
MethodInfo mi = (MethodInfo)mcm.MethodBase;
object[] args = mcm.Args;
object ret = null;
if (mi.Name == "InitializeSecurityContext") // For Client Mode
{
if (args[5] == null) // empty input, new connection
{
dynamic[] secbufs = (dynamic[])Activator.CreateInstance(miSDC_ASC.GetParameters()[6].ParameterType, new object[] { 1 });
secbufs[0] = secbufalpn;
object[] sdcargs = new object[] { 0, args[0], args[1], args[2], args[3], args[4],
null,
secbufs,
args[6], args[7]
};
ret = miSDC_ISC.Invoke(null, sdcargs);
args[0] = sdcargs[1];
args[1] = sdcargs[2];
args[7] = sdcargs[9];
}
else
{
ret = mi.Invoke(secchan, args);
}
}
else if (mi.Name == "AcceptSecurityContext") // For Server Mode
{
dynamic[] secbufs = (dynamic[])Activator.CreateInstance(miSDC_ASC.GetParameters()[6].ParameterType, new object[] { 3 });
secbufs[0] = args[2];
secbufs[1] = secbufempty;
secbufs[2] = secbufalpn;
object[] sdcargs = new object[] { 0, args[0], args[1], args[3], args[4],
null,
secbufs,
args[5], args[6]
};
ret = miSDC_ASC.Invoke(null, sdcargs);
args[0] = sdcargs[1];
args[1] = sdcargs[2];
args[6] = sdcargs[8];
}
else
ret = mi.Invoke(secchan, args);
return new ReturnMessage(ret, args, args.Length, mcm.LogicalCallContext, mcm);
}).GetTransparentProxy();
// and set it, done
fi.SetValue(null, result);
}
.NET Core 2.1.2 includes the necessary changes to SslStream required to support ALPN. It isn't documented yet, but the pull request that adds it is here
© 2022 - 2024 — McMap. All rights reserved.