Как реализовать TLS-ALPN в.NET C# для сервера HTTP/2

Кто-нибудь знает, как я могу реализовать TLS-ALPN в.NET?

Я реализовал базовый сервер HTTP/2, но без шифрования TLS. Я искал в Google, но я нашел только ресурсы для C, Java или других языков, но ничего для.NET (C#)

2 ответа

На самом деле это возможно. С некоторым отражением вы можете вставить любое расширение в привет клиента или сервера.

Вот код, который дает вам представление:

// 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);
}

Согласно проекту HttpTwo на Github, в настоящее время это невозможно из-за ошибки.

Обновление: это не поддерживается в.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 для согласования протокола. К сожалению, .ss StStream не имеет возможности указывать протоколы приложений как часть аутентификации TLS, поэтому он не может поддерживать ALPN. Есть проблема с отслеживанием этого в dotnetfix, однако, похоже, что это произойдет не очень скоро (особенно в mono и.NET 4.x).

.NET Core 2.1.2 включает в себя необходимые изменения в SslStream, необходимые для поддержки ALPN. Это еще не задокументировано, но запрос на добавление, который добавляет его, находится здесь

Другие вопросы по тегам