Ejemplo n.º 1
0
 public void SetCertificate(SecIdentity identify, IEnumerable <SecCertificate> certificates)
 {
     using (var array = Bundle(identify, certificates)) {
         var result = SSLSetCertificate(Handle, array.Handle);
         CheckStatusAndThrow(result);
     }
 }
Ejemplo n.º 2
0
        CFArray Bundle(SecIdentity identity, IEnumerable <SecCertificate> certificates)
        {
            if (identity == null)
            {
                throw new ArgumentNullException("identity");
            }
            int i = 0;

            int n = 0;

            if (certificates != null)
            {
                foreach (var obj in certificates)
                {
                    n++;
                }
            }

            var ptrs = new IntPtr [n + 1];

            ptrs [0] = identity.Handle;
            foreach (var certificate in certificates)
            {
                ptrs [++i] = certificate.Handle;
            }
            return(CFArray.CreateArray(ptrs));
        }
Ejemplo n.º 3
0
        public override void StartHandshake()
        {
            Debug("StartHandshake: {0}", IsServer);

            if (Interlocked.CompareExchange(ref handshakeStarted, 1, 1) != 0)
            {
                throw new InvalidOperationException();
            }

            InitializeConnection();

            SetSessionOption(SslSessionOption.BreakOnCertRequested, true);
            SetSessionOption(SslSessionOption.BreakOnClientAuth, true);
            SetSessionOption(SslSessionOption.BreakOnServerAuth, true);

            if (IsServer)
            {
                SecCertificate[] intermediateCerts;
                serverIdentity = AppleCertificateHelper.GetIdentity(LocalServerCertificate, out intermediateCerts);
                if (serverIdentity == null)
                {
                    throw new SSA.AuthenticationException("Unable to get server certificate from keychain.");
                }

                SetCertificate(serverIdentity, intermediateCerts);
                for (int i = 0; i < intermediateCerts.Length; i++)
                {
                    intermediateCerts [i].Dispose();
                }
            }
        }
Ejemplo n.º 4
0
        static INativeObject [] QueryAsReference(CFDictionary query, out SecStatusCode result)
        {
            if (query == null)
            {
                result = SecStatusCode.Param;
                return(null);
            }

            IntPtr ptr;

            result = SecItem.SecItemCopyMatching(query.Handle, out ptr);
            if (result == SecStatusCode.Success && ptr != IntPtr.Zero)
            {
                var array = CFArray.ArrayFromHandle <INativeObject> (ptr, p => {
                    IntPtr cfType = CFType.GetTypeID(p);
                    if (cfType == SecCertificate.GetTypeID())
                    {
                        return(new SecCertificate(p, true));
                    }
                    if (cfType == SecKey.GetTypeID())
                    {
                        return(new SecKey(p, true));
                    }
                    if (cfType == SecIdentity.GetTypeID())
                    {
                        return(new SecIdentity(p, true));
                    }
                    throw new Exception(String.Format("Unexpected type: 0x{0:x}", cfType));
                });
                return(array);
            }
            return(null);
        }
Ejemplo n.º 5
0
        public override bool ProcessHandshake()
        {
            if (handshakeFinished)
            {
                throw new NotSupportedException("Handshake already finished.");
            }

            while (true)
            {
                lastException = null;
                var status = SSLHandshake(Handle);
                Debug("Handshake: {0} - {0:x}", status);

                CheckStatusAndThrow(status, SslStatus.WouldBlock, SslStatus.PeerAuthCompleted, SslStatus.PeerClientCertRequested);

                if (status == SslStatus.PeerAuthCompleted)
                {
                    RequirePeerTrust();
                }
                else if (status == SslStatus.PeerClientCertRequested)
                {
                    RequirePeerTrust();
                    if (remoteCertificate == null)
                    {
                        throw new TlsException(AlertDescription.InternalError, "Cannot request client certificate before receiving one from the server.");
                    }
                    localClientCertificate = SelectClientCertificate(remoteCertificate, null);
                    if (localClientCertificate == null)
                    {
                        continue;
                    }
                    clientIdentity = AppleCertificateHelper.GetIdentity(localClientCertificate);
                    if (clientIdentity == null)
                    {
                        throw new TlsException(AlertDescription.CertificateUnknown);
                    }
                    SetCertificate(clientIdentity, new SecCertificate [0]);
                }
                else if (status == SslStatus.WouldBlock)
                {
                    return(false);
                }
                else if (status == SslStatus.Success)
                {
                    handshakeFinished = true;
                    return(true);
                }
            }
        }
Ejemplo n.º 6
0
        public override void StartHandshake()
        {
            Debug("StartHandshake: {0}", IsServer);

            if (Interlocked.CompareExchange(ref handshakeStarted, 1, 1) != 0)
            {
                throw new InvalidOperationException();
            }

            InitializeConnection();

            /*
             * SecureTransport is bugged OS X 10.5.8+ - renegotiation after
             * calling SetCertificate() will not work.
             *
             * We also cannot change options after the handshake has started,
             * so if you want to request a client certificate, it will happen
             * both during the initial handshake and during renegotiation.
             *
             * You may check 'SslStream.IsAuthenticated' (which will be false
             * during the initial handshake) from within your
             * 'LocalCertificateSelectionCallback' and return null to have the
             * callback invoked again during renegotiation.
             *
             * However, the first time your selection callback returns a client
             * certificate, that certificate will be used for the rest of the
             * session.
             */

            SetSessionOption(SslSessionOption.BreakOnCertRequested, true);
            SetSessionOption(SslSessionOption.BreakOnClientAuth, true);
            SetSessionOption(SslSessionOption.BreakOnServerAuth, true);

            if (IsServer)
            {
                SecCertificate[] intermediateCerts;
                serverIdentity = AppleCertificateHelper.GetIdentity(LocalServerCertificate, out intermediateCerts);
                if (serverIdentity == null)
                {
                    throw new SSA.AuthenticationException("Unable to get server certificate from keychain.");
                }

                SetCertificate(serverIdentity, intermediateCerts);
                for (int i = 0; i < intermediateCerts.Length; i++)
                {
                    intermediateCerts [i].Dispose();
                }
            }
        }
Ejemplo n.º 7
0
        void ClientCertificateRequested()
        {
            EvaluateTrust();
            var acceptableIssuers = CopyDistinguishedNames();

            localClientCertificate = SelectClientCertificate(acceptableIssuers);
            if (localClientCertificate == null)
            {
                return;
            }
            clientIdentity = AppleCertificateHelper.GetIdentity(localClientCertificate);
            if (clientIdentity == null)
            {
                throw new TlsException(AlertDescription.CertificateUnknown);
            }
            SetCertificate(clientIdentity, new SecCertificate [0]);
        }
Ejemplo n.º 8
0
        static public SecIdentity ItemImport(X509Certificate2 certificate)
        {
            if (!certificate.HasPrivateKey)
            {
                throw new NotSupportedException();
            }

            using (var key = ImportPrivateKey(certificate))
                using (var cert = new SecCertificate(certificate)) {
                    var identity = SecIdentityCreate(IntPtr.Zero, cert.Handle, key.Handle);
                    if (CFType.GetTypeID(identity) != SecIdentity.GetTypeID())
                    {
                        throw new InvalidOperationException();
                    }

                    return(new SecIdentity(identity, true));
                }
        }
Ejemplo n.º 9
0
        public static SecIdentity GetIdentity(X509Certificate certificate)
        {
            /*
             * If we got an 'X509Certificate2', then we require it to have a private key
             * and import it.
             */
            var certificate2 = certificate as X509Certificate2;

            if (certificate2 != null)
            {
                return(SecIdentity.Import(certificate2));
            }

            /*
             * Otherwise, we require the private key to be in the keychain.
             */
            using (var secCert = new SecCertificate(certificate)) {
                return(SecKeyChain.FindIdentity(secCert, true));
            }
        }
Ejemplo n.º 10
0
        public static INativeObject[] QueryAsReference(SecRecord query, int max, out SecStatusCode result)
        {
            if (query == null)
            {
                result = SecStatusCode.Param;
                return(null);
            }

            using (var copy = query.queryDict.MutableCopy()) {
                copy.SetValue(CFBoolean.True.Handle, SecItem.ReturnRef);
                SetLimit(copy, max);

                IntPtr ptr;
                result = SecItem.SecItemCopyMatching(copy.Handle, out ptr);
                if ((result == SecStatusCode.Success) && (ptr != IntPtr.Zero))
                {
                    var array = CFArray.ArrayFromHandle <INativeObject> (ptr, p => {
                        IntPtr cfType = CFType.GetTypeID(p);
                        if (cfType == SecCertificate.GetTypeID())
                        {
                            return(new SecCertificate(p, true));
                        }
                        else if (cfType == SecKey.GetTypeID())
                        {
                            return(new SecKey(p, true));
                        }
                        else if (cfType == SecIdentity.GetTypeID())
                        {
                            return(new SecIdentity(p, true));
                        }
                        else
                        {
                            throw new Exception(String.Format("Unexpected type: 0x{0:x}", cfType));
                        }
                    });
                    return(array);
                }
                return(null);
            }
        }
Ejemplo n.º 11
0
 protected override void Dispose(bool disposing)
 {
     try {
         if (disposed)
         {
             return;
         }
         if (disposing)
         {
             disposed = true;
             if (serverIdentity != null)
             {
                 serverIdentity.Dispose();
                 serverIdentity = null;
             }
             if (clientIdentity != null)
             {
                 clientIdentity.Dispose();
                 clientIdentity = null;
             }
             if (remoteCertificate != null)
             {
                 remoteCertificate.Dispose();
                 remoteCertificate = null;
             }
         }
     } finally {
         disposed = true;
         if (context != IntPtr.Zero)
         {
             CFObject.CFRelease(context);
             context = IntPtr.Zero;
         }
         base.Dispose(disposing);
     }
 }
Ejemplo n.º 12
0
        public static SecIdentity GetIdentity(X509Certificate certificate)
        {
            /*
             * If we got an 'X509Certificate2', then we require it to have a private key
             * and import it.
             */
            var certificate2 = certificate as X509Certificate2;

            if (certificate2 != null)
#if MONOTOUCH
            { return(SecIdentity.Import(certificate2)); }
#else
            { return(SecImportExport.ItemImport(certificate2)); }
#endif

            /*
             * Reading Certificates from the Mac Keychain
             * ==========================================
             *
             * Reading the private key from the keychain is a new feature introduced with
             * AppleTls on XamMac and iOS. On Desktop Mono, this new feature has several
             * known issues and it also did not received any testing yet. We go back to the old
             * way of doing things, which is to explicitly provide an X509Certificate2 with a
             * private key.
             *
             * Keychain Dialog Popups
             * ======================
             *
             * When using Xamarin.Mac or Xamarin.iOS, we try to search the keychain
             * for the certificate and private key.
             *
             * On Xamarin.iOS, this is easy because each app has its own keychain.
             *
             * On Xamarin.Mac, the .app package needs to be trusted via code-sign
             * to get permission to access the user's keychain. [FIXME: I still have to
             * research how to actually do that.] Without this, you will get a popup
             * message each time, asking you whether you want to allow the app to access
             * the keychain, but you can make these go away by selecting "Trust always".
             *
             * On Desktop Mono, this is problematic because selecting "Trust always"
             * give the 'mono' binary (and thus everything you'll ever run with Mono)
             * permission to retrieve the private key from the keychain.
             *
             * This code would also trigger constant keychain popup messages,
             * which could only be suppressed by granting full trust. It also makes it
             * impossible to run Mono in headless mode.
             *
             * SecIdentityCreate
             * =================
             *
             * To avoid these problems, we are currently using an undocumented API
             * called SecIdentityRef() to avoid using the Mac keychain whenever a
             * X509Certificate2 with a private key is used.
             *
             * On iOS and XamMac, you can still provide the X509Certificate without
             * a private key - in this case, a keychain search will be performed (and you
             * may get a popup message on XamMac).
             */

#if MOBILE
            using (var secCert = new SecCertificate(certificate)) {
                return(SecKeyChain.FindIdentity(secCert, true));
            }
#else
            return(null);
#endif
        }