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); } } } }
public static void AddCertificatesToNativeChain(UnityTls.unitytls_x509list *nativeCertificateChain, X509CertificateCollection certificates, UnityTls.unitytls_errorstate *errorState) { foreach (var certificate in certificates) { AddCertificateToNativeChain(nativeCertificateChain, certificate, errorState); } }
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; }
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; } }
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); }