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

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

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


person Ringo Leese    schedule 15.08.2015    source источник
comment
удачи в этом? :)   -  person vtortola    schedule 20.10.2015
comment
не совсем. Я попытался извлечь соответствующий код из https://github.com/MSOpenTech/http2-katana, но все еще получаю исключение, когда пытаюсь подключиться через https.   -  person Ringo Leese    schedule 11.01.2016


Ответы (3)


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

person Max Novich    schedule 02.02.2016

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

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

// 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);
}
person Kris    schedule 01.09.2020

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

person Dylan P    schedule 15.07.2018