예제 #1
0
        public static void AddCertificateToNativeChain(UnityTls.unitytls_x509list *nativeCertificateChain,
                                                       X509Certificate certificate, UnityTls.unitytls_errorstate *errorState)
        {
            var certDer = certificate.GetRawCertData();

            fixed(byte *certDerPtr = certDer)
            {
                UnityTls.NativeInterface.unitytls_x509list_append_der(nativeCertificateChain, certDerPtr,
                                                                      (size_t)certDer.Length, errorState);
            }

            var certificateImpl2 = certificate.Impl as X509Certificate2Impl;

            if (certificate is X509Certificate2 cert2 && cert2.Impl != null)
            {
                var intermediates = certificateImpl2.IntermediateCertificates;
                if (intermediates != null && intermediates.Count > 0)
                {
                    for (var i = 0; i < intermediates.Count; ++i)
                    {
                        AddCertificateToNativeChain(nativeCertificateChain, new X509Certificate(intermediates[i]),
                                                    errorState);
                    }
                }
            }
        }
예제 #2
0
 public static void AddCertificatesToNativeChain(UnityTls.unitytls_x509list *nativeCertificateChain,
                                                 X509CertificateCollection certificates, UnityTls.unitytls_errorstate *errorState)
 {
     foreach (var certificate in certificates)
     {
         AddCertificateToNativeChain(nativeCertificateChain, certificate, errorState);
     }
 }
예제 #3
0
        public UnityTlsContext(
            MobileAuthenticatedStream parent,
            MonoSslAuthenticationOptions opts)
            : base(parent, opts)
        {
            // Need GCHandle to get a consistent pointer to this instance
            handle = GCHandle.Alloc(this);

            var errorState = UnityTls.NativeInterface.unitytls_errorstate_create();

            // Map selected protocols as best as we can.
            var protocolRange = new UnityTls.unitytls_tlsctx_protocolrange
            {
                min = UnityTlsConversions.GetMinProtocol(EnabledProtocols),
                max = UnityTlsConversions.GetMaxProtocol(EnabledProtocols)
            };

            readCallback  = ReadCallback;
            writeCallback = WriteCallback;
            var callbacks = new UnityTls.unitytls_tlsctx_callbacks
            {
                write = writeCallback,
                read  = readCallback,
                data  = (void *)(IntPtr)handle
            };

            if (opts.ServerMode)
            {
                ExtractNativeKeyAndChainFromManagedCertificate(opts.ServerCertificate, &errorState, out var serverCerts,
                                                               out var serverPrivateKey);
                try
                {
                    var serverCertsRef = UnityTls.NativeInterface.unitytls_x509list_get_ref(serverCerts, &errorState);
                    var serverKeyRef   = UnityTls.NativeInterface.unitytls_key_get_ref(serverPrivateKey, &errorState);
                    Unity.Debug.CheckAndThrow(errorState, "Failed to parse server key/certificate");

                    tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_server(protocolRange, callbacks,
                                                                                        serverCertsRef.handle, serverKeyRef.handle, &errorState);

                    if (opts.ClientCertificateRequired)
                    {
                        UnityTls.unitytls_x509list *clientAuthCAList = null;
                        try
                        {
                            clientAuthCAList = UnityTls.NativeInterface.unitytls_x509list_create(&errorState);
                            var clientAuthCAListRef =
                                UnityTls.NativeInterface.unitytls_x509list_get_ref(clientAuthCAList, &errorState);
                            UnityTls.NativeInterface.unitytls_tlsctx_server_require_client_authentication(tlsContext,
                                                                                                          clientAuthCAListRef, &errorState);
                        }
                        finally
                        {
                            UnityTls.NativeInterface.unitytls_x509list_free(clientAuthCAList);
                        }
                    }
                }
                finally
                {
                    UnityTls.NativeInterface.unitytls_x509list_free(serverCerts);
                    UnityTls.NativeInterface.unitytls_key_free(serverPrivateKey);
                }
            }
            else
            {
                var targetHostUtf8 = Encoding.UTF8.GetBytes(opts.TargetHost);
                fixed(byte *targetHostUtf8Ptr = targetHostUtf8)
                {
                    tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_client(protocolRange, callbacks,
                                                                                        targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, &errorState);
                }

                certificateCallback = CertificateCallback;
                UnityTls.NativeInterface.unitytls_tlsctx_set_certificate_callback(tlsContext, certificateCallback,
                                                                                  (void *)(IntPtr)handle, &errorState);
            }

            verifyCallback = VerifyCallback;
            UnityTls.NativeInterface.unitytls_tlsctx_set_x509verify_callback(tlsContext, verifyCallback,
                                                                             (void *)(IntPtr)handle, &errorState);

            Unity.Debug.CheckAndThrow(errorState, "Failed to create UnityTls context");

#pragma warning disable CS0162 // Disable unreachable code warning
            if (ActivateTracing)
            {
                traceCallback = TraceCallback;
                UnityTls.NativeInterface.unitytls_tlsctx_set_trace_callback(tlsContext, traceCallback, null,
                                                                            &errorState);
                Unity.Debug.CheckAndThrow(errorState, "Failed to set trace callback");
            }
#pragma warning restore CS0162 // Reenable unreachable code warning.

            hasContext = true;
        }
예제 #4
0
        private static void ExtractNativeKeyAndChainFromManagedCertificate(X509Certificate cert,
                                                                           UnityTls.unitytls_errorstate *errorState, out UnityTls.unitytls_x509list *nativeCertChain,
                                                                           out UnityTls.unitytls_key *nativeKey)
        {
            if (cert == null)
            {
                throw new ArgumentNullException("cert");
            }
            var cert2 = cert as X509Certificate2;

            if (cert2 == null || cert2.PrivateKey == null)
            {
                throw new ArgumentException("Certificate does not have a private key", "cert");
            }

            nativeCertChain = null;
            nativeKey       = null;
            try
            {
                nativeCertChain = UnityTls.NativeInterface.unitytls_x509list_create(errorState);
                CertHelper.AddCertificateToNativeChain(nativeCertChain, cert2, errorState);

                var privateKeyDer = PKCS8.PrivateKeyInfo.Encode(cert2.PrivateKey);
                fixed(byte *privateKeyDerPtr = privateKeyDer)
                {
                    nativeKey = UnityTls.NativeInterface.unitytls_key_parse_der(privateKeyDerPtr,
                                                                                (size_t)privateKeyDer.Length, null, (size_t)0, errorState);
                }
            }
            catch
            {
                UnityTls.NativeInterface.unitytls_x509list_free(nativeCertChain);
                UnityTls.NativeInterface.unitytls_key_free(nativeKey);
                throw;
            }
        }
예제 #5
0
        public override bool ValidateCertificate(ChainValidationHelper validator, string targetHost, bool serverMode,
                                                 X509CertificateCollection certificates, bool wantsChain, ref X509Chain chain, ref SslPolicyErrors errors,
                                                 ref int status11)
        {
            var errorState = UnityTls.NativeInterface.unitytls_errorstate_create();

            var unityTlsChainImpl = chain.Impl as X509ChainImplUnityTls;

            if (unityTlsChainImpl == null)
            {
                if (certificates == null || certificates.Count == 0)
                {
                    errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
                    return(false);
                }

                if (wantsChain)
                {
                    chain = SystemCertificateValidator.CreateX509Chain(certificates);
                }
            }
            else
            {
                var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509(unityTlsChainImpl.NativeCertificateChain,
                                                                               (size_t)0, &errorState);
                if (cert.handle == UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE)
                {
                    errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
                    return(false);
                }
            }

            // fixup targetHost name by removing port
            if (!string.IsNullOrEmpty(targetHost))
            {
                var pos = targetHost.IndexOf(':');
                if (pos > 0)
                {
                    targetHost = targetHost.Substring(0, pos);
                }
            }

            // convert cert to native or extract from unityTlsChainImpl.
            var result = UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_NOT_DONE;

            UnityTls.unitytls_x509list *certificatesNative = null;
            try
            {
                // Things the validator provides that we might want to make use of here:
                //validator.Settings.CheckCertificateName				// not used by mono?
                //validator.Settings.CheckCertificateRevocationStatus	// not used by mono?
                //validator.Settings.CertificateValidationTime
                //validator.Settings.CertificateSearchPaths				// currently only used by MonoBtlsProvider

                UnityTls.unitytls_x509list_ref certificatesNativeRef;
                if (unityTlsChainImpl == null)
                {
                    certificatesNative = UnityTls.NativeInterface.unitytls_x509list_create(&errorState);
                    CertHelper.AddCertificatesToNativeChain(certificatesNative, certificates, &errorState);
                    certificatesNativeRef =
                        UnityTls.NativeInterface.unitytls_x509list_get_ref(certificatesNative, &errorState);
                }
                else
                {
                    certificatesNativeRef = unityTlsChainImpl.NativeCertificateChain;
                }

                var targetHostUtf8 = Encoding.UTF8.GetBytes(targetHost);

                if (validator.Settings.TrustAnchors != null)
                {
                    UnityTls.unitytls_x509list *trustCAnative = null;
                    try
                    {
                        trustCAnative = UnityTls.NativeInterface.unitytls_x509list_create(&errorState);
                        CertHelper.AddCertificatesToNativeChain(trustCAnative, validator.Settings.TrustAnchors,
                                                                &errorState);
                        var trustCAnativeRef =
                            UnityTls.NativeInterface.unitytls_x509list_get_ref(trustCAnative, &errorState);

                        fixed(byte *targetHostUtf8Ptr = targetHostUtf8)
                        {
                            result = UnityTls.NativeInterface.unitytls_x509verify_explicit_ca(certificatesNativeRef,
                                                                                              trustCAnativeRef, targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null,
                                                                                              &errorState);
                        }
                    }
                    finally
                    {
                        UnityTls.NativeInterface.unitytls_x509list_free(trustCAnative);
                    }
                }
                else
                {
                    fixed(byte *targetHostUtf8Ptr = targetHostUtf8)
                    {
                        result = UnityTls.NativeInterface.unitytls_x509verify_default_ca(certificatesNativeRef,
                                                                                         targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, null, null, &errorState);
                    }
                }
            }
            finally
            {
                UnityTls.NativeInterface.unitytls_x509list_free(certificatesNative);
            }

            errors = UnityTlsConversions.VerifyResultToPolicyErrror(result);
            // There should be a status per certificate, but once again we're following closely the BTLS implementation
            // https://github.com/mono/mono/blob/1553889bc54f87060158febca7e6b8b9910975f8/mcs/class/System/Mono.Btls/MonoBtlsProvider.cs#L180
            // which also provides only a single status for the entire chain.
            // It is notoriously tricky to implement in OpenSSL to get a status for all invididual certificates without finishing the handshake in the process.
            // This is partially the reason why unitytls_x509verify_X doesn't expose it (TODO!) and likely the reason Mono's BTLS impl ignores this.
            unityTlsChainImpl?.AddStatus(UnityTlsConversions.VerifyResultToChainStatus(result));
            return(result == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS &&
                   errorState.code == UnityTls.unitytls_error_code.UNITYTLS_SUCCESS);
        }