diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln
index 8ec3d0f3a6..6cdeaa1464 100644
--- a/KestrelHttpServer.sln
+++ b/KestrelHttpServer.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26730.16
+VisualStudioVersion = 15.0.27101.0
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
ProjectSection(SolutionItems) = preProject
@@ -121,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Libuv.Fun
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Sockets.FunctionalTests", "test\Kestrel.Transport.Sockets.FunctionalTests\Kestrel.Transport.Sockets.FunctionalTests.csproj", "{9C7B6B5F-088A-436E-834B-6373EA36DEEE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Http2SampleApp", "samples\Http2SampleApp\Http2SampleApp.csproj", "{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -335,6 +337,18 @@ Global
{9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x64.Build.0 = Release|Any CPU
{9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x86.ActiveCfg = Release|Any CPU
{9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x86.Build.0 = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|x64.Build.0 = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Debug|x86.Build.0 = Debug|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x64.ActiveCfg = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x64.Build.0 = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x86.ActiveCfg = Release|Any CPU
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -359,6 +373,7 @@ Global
{B7B0EA74-528F-46B8-9BC4-909D9A67C194} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
{74032D79-8EA7-4483-BD82-C38370420FFF} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
{9C7B6B5F-088A-436E-834B-6373EA36DEEE} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
+ {7BC22A4A-15D2-44C2-AB45-049F0FB562FA} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2D10D020-6770-47CA-BB8D-2C23FE3AE071}
diff --git a/samples/Http2SampleApp/Dockerfile b/samples/Http2SampleApp/Dockerfile
new file mode 100644
index 0000000000..e93d563bde
--- /dev/null
+++ b/samples/Http2SampleApp/Dockerfile
@@ -0,0 +1,14 @@
+FROM microsoft/aspnetcore:2.0.0-stretch
+
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ libssl-dev && \
+ rm -rf /var/lib/apt/lists/*
+
+ARG CONFIGURATION=Debug
+
+WORKDIR /app
+
+COPY ./bin/${CONFIGURATION}/netcoreapp2.0/publish/ /app
+
+ENTRYPOINT [ "/usr/bin/dotnet", "/app/Http2SampleApp.dll" ]
diff --git a/samples/Http2SampleApp/Http2SampleApp.csproj b/samples/Http2SampleApp/Http2SampleApp.csproj
new file mode 100644
index 0000000000..0fd184ca8d
--- /dev/null
+++ b/samples/Http2SampleApp/Http2SampleApp.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netcoreapp2.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Http2SampleApp/Program.cs b/samples/Http2SampleApp/Program.cs
new file mode 100644
index 0000000000..249c41347c
--- /dev/null
+++ b/samples/Http2SampleApp/Program.cs
@@ -0,0 +1,50 @@
+using System.Globalization;
+using System.IO;
+using System.Net;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Http2SampleApp
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddEnvironmentVariables()
+ .Build();
+
+ if (!ushort.TryParse(configuration["BASE_PORT"], NumberStyles.None, CultureInfo.InvariantCulture, out var basePort))
+ {
+ basePort = 5000;
+ }
+
+ var hostBuilder = new WebHostBuilder()
+ .ConfigureLogging((_, factory) =>
+ {
+ // Set logging to the MAX.
+ factory.SetMinimumLevel(LogLevel.Trace);
+ factory.AddConsole();
+ })
+ .UseKestrel(options =>
+ {
+ // Run callbacks on the transport thread
+ options.ApplicationSchedulingMode = SchedulingMode.Inline;
+
+ options.Listen(IPAddress.Any, basePort, listenOptions =>
+ {
+ listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
+ listenOptions.UseTls("testCert.pfx", "testPassword");
+ listenOptions.UseConnectionLogging();
+ });
+ })
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseStartup();
+
+ hostBuilder.Build().Run();
+ }
+ }
+}
diff --git a/samples/Http2SampleApp/Startup.cs b/samples/Http2SampleApp/Startup.cs
new file mode 100644
index 0000000000..6dce6b8a19
--- /dev/null
+++ b/samples/Http2SampleApp/Startup.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Http2SampleApp
+{
+ public class Startup
+ {
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Hello World!");
+ });
+ }
+ }
+}
diff --git a/samples/Http2SampleApp/scripts/build-docker.ps1 b/samples/Http2SampleApp/scripts/build-docker.ps1
new file mode 100644
index 0000000000..eda82ace6f
--- /dev/null
+++ b/samples/Http2SampleApp/scripts/build-docker.ps1
@@ -0,0 +1,3 @@
+dotnet publish --framework netcoreapp2.0 "$PSScriptRoot/../Http2SampleApp.csproj"
+
+docker build -t kestrel-http2-sample (Convert-Path "$PSScriptRoot/..")
diff --git a/samples/Http2SampleApp/scripts/build-docker.sh b/samples/Http2SampleApp/scripts/build-docker.sh
new file mode 100755
index 0000000000..ca226f0b53
--- /dev/null
+++ b/samples/Http2SampleApp/scripts/build-docker.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+dotnet publish --framework netcoreapp2.0 "$DIR/../Http2SampleApp.csproj"
+
+docker build -t kestrel-http2-sample "$DIR/.."
diff --git a/samples/Http2SampleApp/scripts/run-docker.ps1 b/samples/Http2SampleApp/scripts/run-docker.ps1
new file mode 100644
index 0000000000..7b371b6dde
--- /dev/null
+++ b/samples/Http2SampleApp/scripts/run-docker.ps1
@@ -0,0 +1 @@
+docker run -p 5000:5000 -it --rm kestrel-http2-sample
diff --git a/samples/Http2SampleApp/scripts/run-docker.sh b/samples/Http2SampleApp/scripts/run-docker.sh
new file mode 100755
index 0000000000..3039b34a98
--- /dev/null
+++ b/samples/Http2SampleApp/scripts/run-docker.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+docker run -it -p 5000:5000 --rm kestrel-http2-sample
diff --git a/samples/SampleApp/SampleApp.csproj b/samples/SampleApp/SampleApp.csproj
index 7d62f70177..2225608125 100644
--- a/samples/SampleApp/SampleApp.csproj
+++ b/samples/SampleApp/SampleApp.csproj
@@ -6,10 +6,6 @@
true
-
-
-
-
@@ -19,4 +15,8 @@
+
+
+
+
diff --git a/samples/SampleApp/testCert.pfx b/samples/SampleApp/testCert.pfx
deleted file mode 100644
index 7118908c2d..0000000000
Binary files a/samples/SampleApp/testCert.pfx and /dev/null differ
diff --git a/src/Kestrel.Core/Internal/Http2/Http2Frame.Data.cs b/src/Kestrel.Core/Internal/Http2/Http2Frame.Data.cs
index 9a0760fe6e..91f0edb72a 100644
--- a/src/Kestrel.Core/Internal/Http2/Http2Frame.Data.cs
+++ b/src/Kestrel.Core/Internal/Http2/Http2Frame.Data.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
@@ -40,5 +41,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
Payload.Slice(Length - padLength.Value).Fill(0);
}
}
+
+ private void DataTraceFrame(ILogger logger)
+ {
+ logger.LogTrace("'DATA' Frame. Flags = {DataFlags}, PadLength = {PadLength}, PayloadLength = {PayloadLength}", DataFlags, DataPadLength, DataPayload.Count);
+ }
}
}
diff --git a/src/Kestrel.Core/Internal/Http2/Http2Frame.cs b/src/Kestrel.Core/Internal/Http2/Http2Frame.cs
index e0dbf8bd09..f983b632a3 100644
--- a/src/Kestrel.Core/Internal/Http2/Http2Frame.cs
+++ b/src/Kestrel.Core/Internal/Http2/Http2Frame.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{
diff --git a/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs b/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs
index cb2c459152..695297eb56 100644
--- a/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs
+++ b/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs
@@ -10,12 +10,12 @@ namespace Microsoft.AspNetCore.Hosting
{
public static class ListenOptionsTlsExtensions
{
- public static ListenOptions UseTls(this ListenOptions listenOptions, string certificatePath, string privateKeyPath)
+ public static ListenOptions UseTls(this ListenOptions listenOptions, string certificatePath, string password)
{
return listenOptions.UseTls(new TlsConnectionAdapterOptions
{
CertificatePath = certificatePath,
- PrivateKeyPath = privateKeyPath,
+ Password = password,
Protocols = listenOptions.Protocols
});
}
diff --git a/src/Kestrel.Tls/OpenSsl.cs b/src/Kestrel.Tls/OpenSsl.cs
index 17568e4b9c..52af7a8857 100644
--- a/src/Kestrel.Tls/OpenSsl.cs
+++ b/src/Kestrel.Tls/OpenSsl.cs
@@ -15,23 +15,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
public const int OPENSSL_NPN_NEGOTIATED = 1;
public const int SSL_TLSEXT_ERR_OK = 0;
public const int SSL_TLSEXT_ERR_NOACK = 3;
+ public const int SSL_CTRL_CHAIN = 88;
private const int BIO_C_SET_BUF_MEM_EOF_RETURN = 130;
private const int SSL_CTRL_SET_ECDH_AUTO = 94;
- public static int SSL_library_init()
+ public static void SSL_library_init()
{
- return NativeMethods.SSL_library_init();
+ try
+ {
+ // Try OpenSSL 1.0.2
+ NativeMethods.SSL_library_init();
+ }
+ catch (EntryPointNotFoundException)
+ {
+ // It's fine, OpenSSL 1.1 doesn't need initialization
+ }
}
public static void SSL_load_error_strings()
{
- NativeMethods.SSL_load_error_strings();
+ try
+ {
+ NativeMethods.SSL_load_error_strings();
+ }
+ catch (EntryPointNotFoundException)
+ {
+ // Also fine, OpenSSL 1.1 doesn't need it.
+ }
}
public static void OpenSSL_add_all_algorithms()
{
- NativeMethods.OPENSSL_add_all_algorithms_noconf();
+ try
+ {
+ NativeMethods.OPENSSL_add_all_algorithms_noconf();
+ }
+ catch (EntryPointNotFoundException)
+ {
+ // Also fine, OpenSSL 1.1 doesn't need it.
+ }
}
public static IntPtr TLSv1_2_method()
@@ -49,6 +72,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
NativeMethods.SSL_CTX_free(ctx);
}
+ public unsafe static int SSL_CTX_Set_Pfx(IntPtr ctx, string path, string password)
+ {
+ var pass = Marshal.StringToHGlobalAnsi(password);
+ var key = IntPtr.Zero;
+ var cert = IntPtr.Zero;
+ var ca = IntPtr.Zero;
+
+ try
+ {
+ var file = System.IO.File.ReadAllBytes(path);
+
+ fixed (void* f = file)
+ {
+ var buffer = (IntPtr)f;
+ var pkcs = NativeMethods.d2i_PKCS12(IntPtr.Zero, ref buffer, file.Length);
+ var result = NativeMethods.PKCS12_parse(pkcs, pass, ref key, ref cert, ref ca);
+ if (result != 1)
+ {
+ return -1;
+ }
+ if (NativeMethods.SSL_CTX_use_certificate(ctx, cert) != 1) return -1;
+ if (NativeMethods.SSL_CTX_use_PrivateKey(ctx, key) != 1) return -1;
+ if (NativeMethods.SSL_CTX_ctrl(ctx, SSL_CTRL_CHAIN, 1, ca) != 1) return -1;
+ return 1;
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(pass);
+ if (key != IntPtr.Zero) NativeMethods.EVP_PKEY_free(key);
+ if (cert != IntPtr.Zero) NativeMethods.X509_free(cert);
+ if (ca != IntPtr.Zero) NativeMethods.sk_X509_pop_free(ca);
+ }
+ }
+
public static int SSL_CTX_set_ecdh_auto(IntPtr ctx, int onoff)
{
return (int)NativeMethods.SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, IntPtr.Zero);
@@ -109,6 +167,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
return NativeMethods.SSL_get_error(ssl, ret);
}
+ public static int ERR_get_error()
+ {
+ return NativeMethods.ERR_get_error();
+ }
+
+ public static string ERR_error_string(int error)
+ {
+ var buf = NativeMethods.ERR_error_string(error, IntPtr.Zero);
+
+ // Don't free the buffer! It's a static buffer
+ return Marshal.PtrToStringAnsi(buf);
+ }
+
public static void SSL_set_accept_state(IntPtr ssl)
{
NativeMethods.SSL_set_accept_state(ssl);
@@ -263,6 +334,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
[DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
public static extern void ERR_load_BIO_strings();
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int ERR_get_error();
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr ERR_error_string(int error, IntPtr buf);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern IntPtr d2i_PKCS12(IntPtr unsused, ref IntPtr bufferPointer, long length);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int PKCS12_parse(IntPtr p12, IntPtr pass, ref IntPtr pkey, ref IntPtr cert, ref IntPtr ca);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void PKCS12_free(IntPtr p12);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void EVP_PKEY_free(IntPtr pkey);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void X509_free(IntPtr a);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void sk_X509_pop_free(IntPtr ca);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SSL_CTX_ctrl(IntPtr ctx, int cmd, int larg, IntPtr parg);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SSL_CTX_set1_chain(IntPtr ctx, IntPtr sk);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SSL_CTX_use_certificate(IntPtr ctx, IntPtr x509);
+
+ [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int SSL_CTX_use_PrivateKey(IntPtr ctx, IntPtr pkey);
}
}
}
diff --git a/src/Kestrel.Tls/TlsConnectionAdapter.cs b/src/Kestrel.Tls/TlsConnectionAdapter.cs
index 539c8404f3..8dee80eb68 100644
--- a/src/Kestrel.Tls/TlsConnectionAdapter.cs
+++ b/src/Kestrel.Tls/TlsConnectionAdapter.cs
@@ -40,9 +40,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
throw new ArgumentException("Certificate path must be non-null.", nameof(options));
}
- if (options.PrivateKeyPath == null)
+ if (options.Password == null)
{
- throw new ArgumentException("Private key path must be non-null.", nameof(options));
+ throw new ArgumentException("Password must be non-null.", nameof(options));
}
_options = options;
@@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
private async Task InnerOnConnectionAsync(ConnectionAdapterContext context)
{
- var tlsStream = new TlsStream(context.ConnectionStream, _options.CertificatePath, _options.PrivateKeyPath, _serverProtocols);
+ var tlsStream = new TlsStream(context.ConnectionStream, _options.CertificatePath, _options.Password, _serverProtocols);
try
{
diff --git a/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs b/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs
index 88d107ffdd..220bd47d9c 100644
--- a/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs
+++ b/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs
@@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
{
public string CertificatePath { get; set; } = string.Empty;
- public string PrivateKeyPath { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
public HttpProtocols Protocols { get; set; }
}
diff --git a/src/Kestrel.Tls/TlsStream.cs b/src/Kestrel.Tls/TlsStream.cs
index 0b1b583167..497317bab7 100644
--- a/src/Kestrel.Tls/TlsStream.cs
+++ b/src/Kestrel.Tls/TlsStream.cs
@@ -9,11 +9,15 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.Kestrel.Tls
{
public class TlsStream : Stream
{
+ // Error code that indicates that a handshake failed because unencrypted HTTP was sent
+ private const int SSL_ERROR_HTTP_REQUEST = 336130204;
+
private static unsafe OpenSsl.alpn_select_cb_t _alpnSelectCallback = AlpnSelectCallback;
private readonly Stream _innerStream;
@@ -36,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
OpenSsl.OpenSSL_add_all_algorithms();
}
- public TlsStream(Stream innerStream, string certificatePath, string privateKeyPath, IEnumerable protocols)
+ public TlsStream(Stream innerStream, string certificatePath, string password, IEnumerable protocols)
{
_innerStream = innerStream;
_protocols = ToWireFormat(protocols);
@@ -49,18 +53,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
throw new Exception("Unable to create SSL context.");
}
+ if(OpenSsl.SSL_CTX_Set_Pfx(_ctx, certificatePath, password) != 1)
+ {
+ throw new InvalidOperationException("Unable to load PFX");
+ }
+
OpenSsl.SSL_CTX_set_ecdh_auto(_ctx, 1);
-
- if (OpenSsl.SSL_CTX_use_certificate_file(_ctx, certificatePath, 1) != 1)
- {
- throw new Exception("Unable to load certificate file.");
- }
-
- if (OpenSsl.SSL_CTX_use_PrivateKey_file(_ctx, privateKeyPath, 1) != 1)
- {
- throw new Exception("Unable to load private key file.");
- }
-
+
OpenSsl.SSL_CTX_set_alpn_select_cb(_ctx, _alpnSelectCallback, GCHandle.ToIntPtr(_protocolsHandle));
_ssl = OpenSsl.SSL_new(_ctx);
@@ -181,9 +180,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
{
var error = OpenSsl.SSL_get_error(_ssl, ret);
- if (error != 2)
+ if (error == 1)
{
- throw new IOException($"TLS handshake failed: {nameof(OpenSsl.SSL_do_handshake)} error {error}.");
+ // SSL error, get it from the OpenSSL error queue
+ error = OpenSsl.ERR_get_error();
+ if (error == SSL_ERROR_HTTP_REQUEST)
+ {
+ throw new InvalidOperationException("Unencrypted HTTP traffic was sent to an HTTPS endpoint");
+ }
+ var errorString = OpenSsl.ERR_error_string(error);
+ throw new IOException($"TLS handshake failed: {nameof(OpenSsl.SSL_do_handshake)} error {error}. {errorString}");
}
}