internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle?certHandle, SafeEvpPKeyHandle?certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions) { SafeSslHandle?context = null; // Always use SSLv23_method, regardless of protocols. It supports negotiating to the highest // mutually supported version and can thus handle any of the set protocols, and we then use // SetProtocolOptions to ensure we only allow the ones requested. using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method)) { if (innerContext.IsInvalid) { throw CreateSslException(SR.net_allocate_ssl_context_failed); } if (!Interop.Ssl.Tls13Supported) { if (protocols != SslProtocols.None && CipherSuitesPolicyPal.WantsTls13(protocols)) { protocols = protocols & (~SslProtocols.Tls13); } } else if (CipherSuitesPolicyPal.WantsTls13(protocols) && CipherSuitesPolicyPal.ShouldOptOutOfTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy)) { if (protocols == SslProtocols.None) { // we are using default settings but cipher suites policy says that TLS 1.3 // is not compatible with our settings (i.e. we requested no encryption or disabled // all TLS 1.3 cipher suites) protocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; } else { // user explicitly asks for TLS 1.3 but their policy is not compatible with TLS 1.3 throw new SslException( SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } } if (CipherSuitesPolicyPal.ShouldOptOutOfLowerThanTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy)) { if (!CipherSuitesPolicyPal.WantsTls13(protocols)) { // We cannot provide neither TLS 1.3 or non TLS 1.3, user disabled all cipher suites throw new SslException( SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } protocols = SslProtocols.Tls13; } // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just // create the handle, it's rooted by the using, no one else has a reference to it, etc. Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols); // Sets policy and security level if (!Ssl.SetEncryptionPolicy(innerContext, policy)) { throw new SslException( SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet // shutdown (we aren't negotiating for session close to enable later session // restoration). // // If you find yourself wanting to remove this line to enable bidirectional // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect(). // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html Ssl.SslCtxSetQuietShutdown(innerContext); byte[]? cipherList = CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy); Debug.Assert(cipherList == null || (cipherList.Length >= 1 && cipherList[cipherList.Length - 1] == 0)); byte[]? cipherSuites = CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy); Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0)); unsafe { fixed(byte *cipherListStr = cipherList) fixed(byte *cipherSuitesStr = cipherSuites) { if (!Ssl.SetCiphers(innerContext, cipherListStr, cipherSuitesStr)) { Crypto.ErrClearError(); throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } } } bool hasCertificateAndKey = certHandle != null && !certHandle.IsInvalid && certKeyHandle != null && !certKeyHandle.IsInvalid; if (hasCertificateAndKey) { SetSslCertificate(innerContext, certHandle !, certKeyHandle !); } if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) { Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate); } GCHandle alpnHandle = default; try { if (sslAuthenticationOptions.ApplicationProtocols != null) { if (sslAuthenticationOptions.IsServer) { alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols); Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle)); } else { if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0) { throw CreateSslException(SR.net_alpn_config_failed); } } } context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer); Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); if (context.IsInvalid) { context.Dispose(); throw CreateSslException(SR.net_allocate_ssl_context_failed); } if (!sslAuthenticationOptions.IsServer) { // The IdnMapping converts unicode input into the IDNA punycode sequence. string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !); // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors. if (!Ssl.SslSetTlsExtHostName(context, punyCode)) { Crypto.ErrClearError(); } } if (hasCertificateAndKey) { bool hasCertReference = false; try { certHandle !.DangerousAddRef(ref hasCertReference); using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) { X509Chain?chain = null; try { chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false); if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) { throw CreateSslException(SR.net_ssl_use_cert_failed); } } finally { if (chain != null) { int elementsCount = chain.ChainElements.Count; for (int i = 0; i < elementsCount; i++) { chain.ChainElements[i].Certificate !.Dispose(); } chain.Dispose(); } } } } finally { if (hasCertReference) { certHandle !.DangerousRelease(); } } } context.AlpnHandle = alpnHandle; } catch { if (alpnHandle.IsAllocated) { alpnHandle.Free(); } throw; } } return(context); }
private static void SetCertificate(SafeSslHandle sslContext, X509Certificate2 certificate) { Debug.Assert(sslContext != null, "sslContext != null"); Debug.Assert(certificate != null, "certificate != null"); Debug.Assert(certificate.HasPrivateKey, "certificate.HasPrivateKey"); X509Chain chain = TLSCertificateExtensions.BuildNewChain( certificate, includeClientApplicationPolicy: false) !; using (chain) { X509ChainElementCollection elements = chain.ChainElements; // We need to leave off the EE (first) and root (last) certificate from the intermediates. X509Certificate2[] intermediateCerts = elements.Count < 3 ? Array.Empty <X509Certificate2>() : new X509Certificate2[elements.Count - 2]; // Build an array which is [ // SecIdentityRef for EE cert, // SecCertificateRef for intermed0, // SecCertificateREf for intermed1, // ... // ] IntPtr[] ptrs = new IntPtr[intermediateCerts.Length + 1]; for (int i = 0; i < intermediateCerts.Length; i++) { X509Certificate2 intermediateCert = elements[i + 1].Certificate !; if (intermediateCert.HasPrivateKey) { // In the unlikely event that we get a certificate with a private key from // a chain, clear it to the certificate. // // The current value of intermediateCert is still in elements, which will // get Disposed at the end of this method. The new value will be // in the intermediate certs array, which also gets serially Disposed. intermediateCert = new X509Certificate2(intermediateCert.RawData); } intermediateCerts[i] = intermediateCert; ptrs[i + 1] = intermediateCert.Handle; } ptrs[0] = certificate.Handle; Interop.AppleCrypto.SslSetCertificate(sslContext, ptrs); // The X509Chain created all new certs for us, so Dispose them. // And since the intermediateCerts could have been new instances, Dispose them, too for (int i = 0; i < elements.Count; i++) { elements[i].Certificate !.Dispose(); if (i < intermediateCerts.Length) { intermediateCerts[i].Dispose(); } } } }
internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions) { SafeSslHandle context = null; // Always use SSLv23_method, regardless of protocols. It supports negotiating to the highest // mutually supported version and can thus handle any of the set protocols, and we then use // SetProtocolOptions to ensure we only allow the ones requested. using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method)) { if (innerContext.IsInvalid) { throw CreateSslException(SR.net_allocate_ssl_context_failed); } // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just // create the handle, it's rooted by the using, no one else has a reference to it, etc. Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols); // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet // shutdown (we aren't negotiating for session close to enable later session // restoration). // // If you find yourself wanting to remove this line to enable bidirectional // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect(). // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html Ssl.SslCtxSetQuietShutdown(innerContext); if (!Ssl.SetEncryptionPolicy(innerContext, policy)) { throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } bool hasCertificateAndKey = certHandle != null && !certHandle.IsInvalid && certKeyHandle != null && !certKeyHandle.IsInvalid; if (hasCertificateAndKey) { SetSslCertificate(innerContext, certHandle, certKeyHandle); } if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) { Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate); } GCHandle alpnHandle = default; try { if (sslAuthenticationOptions.ApplicationProtocols != null) { if (sslAuthenticationOptions.IsServer) { alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols); Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle)); } else { if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0) { throw CreateSslException(SR.net_alpn_config_failed); } } } context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer); Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); if (context.IsInvalid) { context.Dispose(); throw CreateSslException(SR.net_allocate_ssl_context_failed); } if (!sslAuthenticationOptions.IsServer) { // The IdnMapping converts unicode input into the IDNA punycode sequence. string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost); // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors. Ssl.SslSetTlsExtHostName(context, punyCode); } if (hasCertificateAndKey) { bool hasCertReference = false; try { certHandle.DangerousAddRef(ref hasCertReference); using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) { using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false)) { if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) { throw CreateSslException(SR.net_ssl_use_cert_failed); } } } } finally { if (hasCertReference) { certHandle.DangerousRelease(); } } } context.AlpnHandle = alpnHandle; } catch { if (alpnHandle.IsAllocated) { alpnHandle.Free(); } throw; } } return(context); }
internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, bool isServer, bool remoteCertRequired) { SafeSslHandle context = null; IntPtr method = GetSslMethod(protocols); using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(method)) { if (innerContext.IsInvalid) { throw CreateSslException(SR.net_allocate_ssl_context_failed); } // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just // create the handle, it's rooted by the using, no one else has a reference to it, etc. Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols); // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet // shutdown (we aren't negotiating for session close to enable later session // restoration). // // If you find yourself wanting to remove this line to enable bidirectional // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect(). // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html Ssl.SslCtxSetQuietShutdown(innerContext); if (!Ssl.SetEncryptionPolicy(innerContext, policy)) { throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } bool hasCertificateAndKey = certHandle != null && !certHandle.IsInvalid && certKeyHandle != null && !certKeyHandle.IsInvalid; if (hasCertificateAndKey) { SetSslCertificate(innerContext, certHandle, certKeyHandle); } if (remoteCertRequired) { Debug.Assert(isServer, "isServer flag should be true"); Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate); //update the client CA list UpdateCAListFromRootStore(innerContext); } context = SafeSslHandle.Create(innerContext, isServer); Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); if (context.IsInvalid) { context.Dispose(); throw CreateSslException(SR.net_allocate_ssl_context_failed); } if (hasCertificateAndKey) { bool hasCertReference = false; try { certHandle.DangerousAddRef(ref hasCertReference); using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) { using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false)) { if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) { throw CreateSslException(SR.net_ssl_use_cert_failed); } } } } finally { if (hasCertReference) { certHandle.DangerousRelease(); } } } } return(context); }