From 41abe63c1008a65ec290134f22658438ad77f3a9 Mon Sep 17 00:00:00 2001 From: Tim Seaward Date: Tue, 7 Nov 2017 22:49:02 +0000 Subject: [PATCH] Open ssl pfx (#2150) --- src/Kestrel.Tls/ListenOptionsTlsExtensions.cs | 4 +- src/Kestrel.Tls/OpenSsl.cs | 62 +++++++++++++++++++ src/Kestrel.Tls/TlsConnectionAdapter.cs | 6 +- .../TlsConnectionAdapterOptions.cs | 2 +- src/Kestrel.Tls/TlsStream.cs | 19 +++--- 5 files changed, 75 insertions(+), 18 deletions(-) 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..7c209a299e 100644 --- a/src/Kestrel.Tls/OpenSsl.cs +++ b/src/Kestrel.Tls/OpenSsl.cs @@ -49,6 +49,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_set1_chain(ctx, 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); @@ -263,6 +298,33 @@ 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 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_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..af45dcbe39 100644 --- a/src/Kestrel.Tls/TlsStream.cs +++ b/src/Kestrel.Tls/TlsStream.cs @@ -36,7 +36,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 +49,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);