如何在 .NET C# 中为 HTTP/2 服务器实现 TLS-ALPN
Posted
技术标签:
【中文标题】如何在 .NET C# 中为 HTTP/2 服务器实现 TLS-ALPN【英文标题】:How to implement TLS-ALPN in .NET C# for a HTTP/2 server 【发布时间】:2015-11-08 10:26:38 【问题描述】:有谁知道,我如何在 .NET 中实现TLS-ALPN?
我已经实现了一个基本的 HTTP/2 服务器,但没有 TLS 加密。 我在谷歌搜索,但我只找到了 C、Java 或其他语言的资源,但没有找到 .NET (C#) 的资源
【问题讨论】:
运气好吗? :) 并非如此。我试图从github.com/MSOpenTech/http2-katana 中提取相关代码,但当我尝试通过 https 连接时仍然出现异常。 【参考方案1】:根据 Github 上的 HttpTwo 项目,由于存在错误,目前无法实现。
更新:.NET 不支持它。你可以在这里投票:https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/6264363-add-support-for-alpn-to-system-net-security-sslstr
引用:
HTTP/2 RFC 声明安全连接必须使用 ALPN 协商协议。不幸的是,.NET 的 SslStream 没有能力 将应用程序协议指定为 TLS 身份验证的一部分,因此 它不支持 ALPN。有一个issue tracking this on dotnetfix 但似乎这不会发生 很快(尤其是在单声道和 .NET 4.x 上)。
【讨论】:
【参考方案2】:.NET Core 2.1.2 包含支持 ALPN 所需的对 SslStream 的必要更改。它尚未记录,但添加它的拉取请求是 here
【讨论】:
【参考方案3】:其实是可以的。通过一些反射,您可以在客户端或服务器 hello 中注入任何扩展。
这里有一些代码可以给你一个想法:
// 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 C# 中为 HTTP/2 服务器实现 TLS-ALPN的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ASP.NET C# 中为图标创建 onClick 事件
如何在 dotnet core 中为 HttpClient 指定 HTTP/2“先验知识”模式?
如何在 Asp.net c# 中为 GridView 行启用双击和单击
如何在 C# 中为枚举值添加描述以与 ASP.NET MVC 中的下拉列表一起使用? [复制]