[btls] Create a native BTLS key handle for X509CertificateImplBtls.PrivateKey
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Tue, 10 Jan 2017 20:40:58 +0000 (21:40 +0100)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Tue, 10 Jan 2017 21:33:49 +0000 (22:33 +0100)
When setting X509CertificateImplBtls.PrivateKey we'd previously just
update the fallback instance. This causes problems if other code
internal to System.dll like MonoBtlsContext.SetPrivateCertificate()
tries to fetch the NativePrivateKey since it'd be null at that point.

This scenario happens e.g. with HttpListener, it supports a special
directory in ~/.config/.mono/httplistener which contains <port>.cer
and <port>.pvk files (public and private key) that will be used
as the server certificate. In that code we're reading the private
key from the file and assigning it to X509CertificateImplBtls.PrivateKey
without ever having a native BTLS handle.

The fix is to create such a native BTLS handle from the managed
private key when needed.

Note that we can't simply always do this e.g. in the PrivateKey
setter because the setter will be used with instances that
don't actually contain a private key. We can't throw in those
cases so instead we do it only when NativePrivateKey is accessed.

To make the code clearer and easier to read, `privateKey` was
renamed to `nativePrivateKey` in X509CertificateImplBtls.

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=46602

mcs/class/System/Mono.Btls/MonoBtlsKey.cs
mcs/class/System/Mono.Btls/X509CertificateImplBtls.cs
mono/btls/btls-key.c
mono/btls/btls-key.h

index f0cc3764d6835b6cad2866ff1702ef4d8ce3d52a..4ba8f16baa0f57757236d8a92ade0c7fb884f565 100644 (file)
@@ -48,6 +48,10 @@ namespace Mono.Btls
                        }
                }
 
+
+               [DllImport (BTLS_DYLIB)]
+               extern static IntPtr mono_btls_key_new ();
+
                [DllImport (BTLS_DYLIB)]
                extern static void mono_btls_key_free (IntPtr handle);
 
@@ -63,6 +67,9 @@ namespace Mono.Btls
                [DllImport (BTLS_DYLIB)]
                extern static int mono_btls_key_is_rsa (IntPtr handle);
 
+               [DllImport (BTLS_DYLIB)]
+               extern static int mono_btls_key_assign_rsa_private_key (IntPtr handle, byte[] der, int der_length);
+
                new internal BoringKeyHandle Handle {
                        get { return (BoringKeyHandle)base.Handle; }
                }
@@ -99,6 +106,18 @@ namespace Mono.Btls
                        CheckError (copy != IntPtr.Zero);
                        return new MonoBtlsKey (new BoringKeyHandle (copy));
                }
+
+               public static MonoBtlsKey CreateFromRSAPrivateKey (System.Security.Cryptography.RSA privateKey)
+               {
+                       var keyData = Mono.Security.Cryptography.PKCS8.PrivateKeyInfo.Encode (privateKey);
+                       var key = new MonoBtlsKey (new BoringKeyHandle (mono_btls_key_new ()));
+
+                       var ret = mono_btls_key_assign_rsa_private_key (key.Handle.DangerousGetHandle (), keyData, keyData.Length);
+                       if (ret == 0)
+                               throw new MonoBtlsException ("Assigning private key failed.");
+
+                       return key;
+               }
        }
 }
 #endif
index 47b5548caf0511eb9daf7d3eb71df1b64deb5204..eb920677d011e77052c6c029014f4ef97f27a375 100644 (file)
@@ -47,7 +47,7 @@ namespace Mono.Btls
        class X509CertificateImplBtls : X509Certificate2Impl
        {
                MonoBtlsX509 x509;
-               MonoBtlsKey privateKey;
+               MonoBtlsKey nativePrivateKey;
                X500DistinguishedName subjectName;
                X500DistinguishedName issuerName;
                X509CertificateImplCollection intermediateCerts;
@@ -70,7 +70,8 @@ namespace Mono.Btls
                {
                        disallowFallback = other.disallowFallback;
                        x509 = other.x509 != null ? other.x509.Copy () : null;
-                       privateKey = other.privateKey != null ? other.privateKey.Copy () : null;
+                       nativePrivateKey = other.nativePrivateKey != null ? other.nativePrivateKey.Copy () : null;
+                       fallback = other.fallback != null ? (X509Certificate2Impl)other.fallback.Clone () : null;
                        if (other.intermediateCerts != null)
                                intermediateCerts = other.intermediateCerts.Clone ();
                }
@@ -104,7 +105,13 @@ namespace Mono.Btls
                internal MonoBtlsKey NativePrivateKey {
                        get {
                                ThrowIfContextInvalid ();
-                               return privateKey;
+                               if (nativePrivateKey == null && FallbackImpl.HasPrivateKey) {
+                                       var key = FallbackImpl.PrivateKey as RSA;
+                                       if (key == null)
+                                               throw new NotSupportedException ("Currently only supports RSA private keys.");
+                                       nativePrivateKey = MonoBtlsKey.CreateFromRSAPrivateKey (key);
+                               }
+                               return nativePrivateKey;
                        }
                }
 
@@ -270,7 +277,7 @@ namespace Mono.Btls
                }
 
                public override bool HasPrivateKey {
-                       get { return privateKey != null; }
+                       get { return nativePrivateKey != null || FallbackImpl.HasPrivateKey; }
                }
 
                public override X500DistinguishedName IssuerName {
@@ -290,12 +297,15 @@ namespace Mono.Btls
 
                public override AsymmetricAlgorithm PrivateKey {
                        get {
-                               if (privateKey == null || !privateKey.IsRsa)
-                                       return null;
-                               var bytes = privateKey.GetBytes (true);
+                               if (nativePrivateKey == null || !nativePrivateKey.IsRsa)
+                                       return FallbackImpl.PrivateKey;
+                               var bytes = nativePrivateKey.GetBytes (true);
                                return PKCS8.PrivateKeyInfo.DecodeRSA (bytes);
                        }
-                       set { FallbackImpl.PrivateKey = value; }
+                       set {
+                               nativePrivateKey = null;
+                               FallbackImpl.PrivateKey = value;
+                       }
                }
 
                public override PublicKey PublicKey {
@@ -400,7 +410,7 @@ namespace Mono.Btls
 
                                x509 = pkcs12.GetCertificate (0);
                                if (pkcs12.HasPrivateKey)
-                                       privateKey = pkcs12.GetPrivateKey ();
+                                       nativePrivateKey = pkcs12.GetPrivateKey ();
                                if (pkcs12.Count > 1) {
                                        intermediateCerts = new X509CertificateImplCollection ();
                                        for (int i = 0; i < pkcs12.Count; i++) {
@@ -477,9 +487,8 @@ namespace Mono.Btls
                                x509.Dispose ();
                                x509 = null;
                        }
-                       if (privateKey != null) {
-                               privateKey = null;
-                               privateKey = null;
+                       if (nativePrivateKey != null) {
+                               nativePrivateKey = null;
                        }
                        subjectName = null;
                        issuerName = null;
index 80ea9ef29cdcd6ae1afb2aef960719996068cbac..17168153b112240fbbb1dabb8c79d863fb7e40ac 100644 (file)
@@ -8,6 +8,12 @@
 
 #include <btls-key.h>
 
+MONO_API EVP_PKEY *
+mono_btls_key_new ()
+{
+       return EVP_PKEY_new ();
+}
+
 MONO_API void
 mono_btls_key_free (EVP_PKEY *pkey)
 {
@@ -32,6 +38,19 @@ mono_btls_key_is_rsa (EVP_PKEY *pkey)
        return pkey->type == EVP_PKEY_RSA;
 }
 
+MONO_API int
+mono_btls_key_assign_rsa_private_key (EVP_PKEY *pkey, uint8_t **der_data, size_t der_length)
+{
+       RSA *rsa;
+       int ret;
+
+       rsa = RSA_private_key_from_bytes (der_data, der_length);
+       if (!rsa)
+               return 0;
+
+       return EVP_PKEY_assign_RSA (pkey, rsa);
+}
+
 MONO_API int
 mono_btls_key_get_bytes (EVP_PKEY *pkey, uint8_t **buffer, int *size, int include_private_bits)
 {
index 85ee5b2f58a63eae86490c0ff3cfa60f204289db..a637399a291d1d0c64ddbbe35abfdd4d4fdda89e 100644 (file)
@@ -13,6 +13,9 @@
 #include <btls-ssl.h>
 #include <btls-x509.h>
 
+EVP_PKEY *
+mono_btls_key_new ();
+
 void
 mono_btls_key_free (EVP_PKEY *pkey);