internal SslEngine( Communicator communicator, X509Certificate2Collection?certificates, X509Certificate2Collection?caCertificates, LocalCertificateSelectionCallback?certificateSelectionCallback, RemoteCertificateValidationCallback?certificateValidationCallback, IPasswordCallback?passwordCallback) { _logger = communicator.Logger; SecurityTraceLevel = communicator.GetPropertyAsInt("IceSSL.Trace.Security") ?? 0; _trustManager = new SslTrustManager(communicator); CertificateSelectionCallback = certificateSelectionCallback; RemoteCertificateValidationCallback = certificateValidationCallback; PasswordCallback = passwordCallback; Certs = certificates; CaCerts = caCertificates; // Check for a default directory. We look in this directory for files mentioned in the configuration. _defaultDir = communicator.GetProperty("IceSSL.DefaultDir") ?? ""; string?certStoreLocation = communicator.GetProperty("IceSSL.CertStoreLocation"); if (certStoreLocation != null && CertificateSelectionCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.CertStoreLocation' is incompatible with the certificate selection callback"); } certStoreLocation ??= "CurrentUser"; StoreLocation storeLocation; if (certStoreLocation == "CurrentUser") { storeLocation = StoreLocation.CurrentUser; } else if (certStoreLocation == "LocalMachine") { storeLocation = StoreLocation.LocalMachine; } else { _logger.Warning($"Invalid IceSSL.CertStoreLocation value `{certStoreLocation}' adjusted to `CurrentUser'"); storeLocation = StoreLocation.CurrentUser; } UseMachineContext = certStoreLocation == "LocalMachine"; // Protocols selects which protocols to enable SslProtocols = ParseProtocols(communicator.GetPropertyAsList("IceSSL.Protocols")); // VerifyDepthMax establishes the maximum length of a peer's certificate chain, including the peer's // certificate. A value of 0 means there is no maximum. int?verifyDepthMax = communicator.GetPropertyAsInt("IceSSL.VerifyDepthMax"); if (verifyDepthMax != null && RemoteCertificateValidationCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.VerifyDepthMax' check is incompatible with the custom remote certificate validation callback"); } _verifyDepthMax = verifyDepthMax ?? 3; // CheckCRL determines whether the certificate revocation list is checked, and how strictly. CheckCRL = communicator.GetPropertyAsInt("IceSSL.CheckCRL") ?? 0; // If the user hasn't supplied a certificate collection, we need to examine the property settings. if (Certs == null) { // If IceSSL.CertFile is defined, load a certificate from a file and add it to the collection. // TODO: tracing? string?certFile = communicator.GetProperty("IceSSL.CertFile"); if (certFile != null && CertificateSelectionCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.CertFile' is incompatible with the certificate selection callback"); } string?passwordStr = communicator.GetProperty("IceSSL.Password"); if (passwordStr != null && CertificateSelectionCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.Password' is incompatible with the certificate selection callback"); } string?findCert = communicator.GetProperty("IceSSL.FindCert"); if (findCert != null && CertificateSelectionCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.FindCert' is incompatible with the certificate selection callback"); } Certs = new X509Certificate2Collection(); const string findPrefix = "IceSSL.FindCert."; Dictionary <string, string> findCertProps = communicator.GetProperties(forPrefix: findPrefix); if (certFile != null) { if (!CheckPath(ref certFile)) { throw new FileNotFoundException($"certificate file not found: `{certFile}'", certFile); } SecureString?password = null; if (passwordStr != null) { password = CreateSecureString(passwordStr); } else if (PasswordCallback != null) { password = PasswordCallback(certFile); } try { X509Certificate2 cert; X509KeyStorageFlags importFlags; if (UseMachineContext) { importFlags = X509KeyStorageFlags.MachineKeySet; } else { importFlags = X509KeyStorageFlags.UserKeySet; } if (password != null) { cert = new X509Certificate2(certFile, password, importFlags); } else { cert = new X509Certificate2(certFile, "", importFlags); } Certs.Add(cert); } catch (CryptographicException ex) { throw new InvalidConfigurationException( $"error while attempting to load certificate from `{certFile}'", ex); } } else if (findCert != null) { string certStore = communicator.GetProperty("IceSSL.CertStore") ?? "My"; Certs.AddRange(FindCertificates("IceSSL.FindCert", storeLocation, certStore, findCert)); if (Certs.Count == 0) { throw new InvalidConfigurationException("no certificates found"); } } } if (CaCerts == null) { string?certAuthFile = communicator.GetProperty("IceSSL.CAs"); if (certAuthFile != null && RemoteCertificateValidationCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.CAs' is incompatible with the custom remote certificate validation callback"); } bool?usePlatformCAs = communicator.GetPropertyAsBool("IceSSL.UsePlatformCAs"); if (usePlatformCAs != null && RemoteCertificateValidationCallback != null) { throw new InvalidConfigurationException( "the property `IceSSL.UsePlatformCAs' is incompatible with the custom remote certificate validation callback"); } if (RemoteCertificateValidationCallback == null) { if (certAuthFile != null || !(usePlatformCAs ?? false)) { CaCerts = new X509Certificate2Collection(); } if (certAuthFile != null) { if (!CheckPath(ref certAuthFile)) { throw new FileNotFoundException("CA certificate file not found: `{certAuthFile}'", certAuthFile); } try { using FileStream fs = File.OpenRead(certAuthFile); byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); string strbuf = ""; try { strbuf = System.Text.Encoding.UTF8.GetString(data); } catch (Exception) { // Ignore } if (strbuf.Length == data.Length) { int size, startpos, endpos = 0; bool first = true; while (true) { startpos = strbuf.IndexOf("-----BEGIN CERTIFICATE-----", endpos); if (startpos != -1) { endpos = strbuf.IndexOf("-----END CERTIFICATE-----", startpos); size = endpos - startpos + "-----END CERTIFICATE-----".Length; } else if (first) { startpos = 0; endpos = strbuf.Length; size = strbuf.Length; } else { break; } byte[] cert = new byte[size]; Buffer.BlockCopy(data, startpos, cert, 0, size); CaCerts !.Import(cert); first = false; } } else { CaCerts !.Import(data); } } catch (Exception ex) { throw new InvalidConfigurationException( $"error while attempting to load CA certificate from {certAuthFile}", ex); } } } } }
internal SslEngine( Communicator communicator, TlsClientOptions?tlsClientOptions, TlsServerOptions?tlsServerOptions) { _logger = communicator.Logger; SecurityTraceLevel = communicator.GetPropertyAsInt("IceSSL.Trace.Security") ?? 0; SslTrustManager = new SslTrustManager(communicator); TlsClientOptions = new TlsClientOptions(); TlsServerOptions = new TlsServerOptions(); TlsClientOptions.EnabledSslProtocols = tlsClientOptions?.EnabledSslProtocols ?? ParseProtocols(communicator.GetPropertyAsList("IceSSL.Protocols")); TlsServerOptions.EnabledSslProtocols = tlsServerOptions?.EnabledSslProtocols ?? ParseProtocols(communicator.GetPropertyAsList("IceSSL.Protocols")); TlsServerOptions.RequireClientCertificate = tlsServerOptions?.RequireClientCertificate ?? true; // Check for a default directory. We look in this directory for files mentioned in the configuration. string defaultDir = communicator.GetProperty("IceSSL.DefaultDir") ?? ""; X509Certificate2Collection?certificates = null; // If IceSSL.CertFile is defined, load a certificate from a file and add it to the collection. if (communicator.GetProperty("IceSSL.CertFile") is string certificateFile) { if (!CheckPath(defaultDir, ref certificateFile)) { throw new FileNotFoundException( $"certificate file not found: `{certificateFile}'", certificateFile); } certificates = new X509Certificate2Collection(); try { X509KeyStorageFlags importFlags; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { importFlags = X509KeyStorageFlags.EphemeralKeySet; } else { importFlags = tlsClientOptions?.UseMachineContex ?? tlsServerOptions?.UseMachineContex ?? false ? X509KeyStorageFlags.MachineKeySet : X509KeyStorageFlags.UserKeySet; } certificates.Add(communicator.GetProperty("IceSSL.Password") is string password ? new X509Certificate2(certificateFile, password, importFlags) : new X509Certificate2(certificateFile, "", importFlags)); } catch (CryptographicException ex) { throw new InvalidConfigurationException( $"error while attempting to load certificate from `{certificateFile}'", ex); } } TlsClientOptions.ClientCertificates = tlsClientOptions?.ClientCertificates ?? certificates; TlsServerOptions.ServerCertificate = tlsServerOptions?.ServerCertificate ?? certificates?[0]; TlsClientOptions.ClientCertificateSelectionCallback = tlsClientOptions?.ClientCertificateSelectionCallback; X509Certificate2Collection?caCertificates = null; if (communicator.GetProperty("IceSSL.CAs") is string certAuthFile) { if (!CheckPath(defaultDir, ref certAuthFile)) { throw new FileNotFoundException($"CA certificate file not found: `{certAuthFile}'", certAuthFile); } try { using FileStream fs = File.OpenRead(certAuthFile); byte[] data = new byte[fs.Length]; fs.Read(data, 0, data.Length); string strbuf = ""; try { strbuf = System.Text.Encoding.UTF8.GetString(data); } catch (Exception) { // Ignore } string beginCertificateMark = "-----BEGIN CERTIFICATE-----"; string endCertificateMark = "-----END CERTIFICATE-----"; caCertificates = new X509Certificate2Collection(); if (strbuf.Length == data.Length) { int size, startpos, endpos = 0; bool first = true; while (true) { startpos = strbuf.IndexOf(beginCertificateMark, endpos); if (startpos != -1) { endpos = strbuf.IndexOf(endCertificateMark, startpos); if (endpos == -1) { throw new FormatException( $"end certificate mark `{endCertificateMark}' not found"); } size = endpos - startpos + endCertificateMark.Length; } else if (first) { startpos = 0; endpos = strbuf.Length; size = strbuf.Length; } else { break; } byte[] cert = new byte[size]; Buffer.BlockCopy(data, startpos, cert, 0, size); caCertificates.Import(cert); first = false; } } else { caCertificates.Import(data); } } catch (Exception ex) { throw new InvalidConfigurationException( $"error while attempting to load CA certificate from `{certAuthFile}'", ex); } } if (tlsClientOptions?.ServerCertificateValidationCallback == null) { TlsClientOptions.ServerCertificateCertificateAuthorities = tlsClientOptions?.ServerCertificateCertificateAuthorities ?? caCertificates; } else { TlsClientOptions.ServerCertificateValidationCallback = tlsClientOptions.ServerCertificateValidationCallback; } if (tlsServerOptions?.ClientCertificateValidationCallback == null) { TlsServerOptions.ClientCertificateCertificateAuthorities = tlsServerOptions?.ClientCertificateCertificateAuthorities ?? caCertificates; } else { TlsServerOptions.ClientCertificateValidationCallback = tlsServerOptions.ClientCertificateValidationCallback; } }