// Copyright (c) 2025 Vuplex Inc. All rights reserved. // // Licensed under the Vuplex Commercial Software Library License, you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // https://vuplex.com/commercial-library-license // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System; using System.Linq; using UnityEngine; using Vuplex.WebView.Internal; namespace Vuplex.WebView { /// /// An X.509 certificate used for StandaloneClientCertificateRequestedEventArgs. /// public class StandaloneX509Certificate { internal StandaloneX509Certificate(MessageCertificate cert) { ID = cert.ID; Issuer = new StandaloneX509CertificatePrincipal(cert.Issuer); Subject = new StandaloneX509CertificatePrincipal(cert.Subject); if (cert.ValidStart != 0) { ValidStart = DateTime.FromFileTimeUtc(cert.ValidStart * 10); } if (cert.ValidExpiry != 0) { ValidExpiry = DateTime.FromFileTimeUtc(cert.ValidExpiry * 10); } } /// /// An internal ID that 3D WebView uses to identify the certificate. /// public readonly int ID; /// /// The issuer of the X.509 certificate. /// public readonly StandaloneX509CertificatePrincipal Issuer; /// /// The subject of the X.509 certificate. For HTTPS server /// certificates this represents the web server. The common name of the /// subject should match the host name of the web server. /// public readonly StandaloneX509CertificatePrincipal Subject; /// /// The DateTime before which the X.509 certificate is invalid, /// or DateTime.MinValue if no date was specified. /// public readonly DateTime ValidStart; /// /// The DateTime after which the X.509 certificate is invalid, /// or DateTime.MinValue if no date was specified. /// public readonly DateTime ValidExpiry; public override string ToString() { return $"(StandaloneX509Certificate)\nID = {ID},\nValidStart = {ValidStart},\nValidExpiry = {ValidExpiry},\nSubject = {Subject},\nIssuer = {Issuer}"; } } /// /// The Issuer or Subject field of StandaloneX509Certificate. /// public class StandaloneX509CertificatePrincipal { internal StandaloneX509CertificatePrincipal(MessageCertificatePrincipal principal) { DisplayName = principal.DisplayName; CommonName = principal.CommonName; LocalityName = principal.LocalityName; StateOrProvinceName = principal.StateOrProvinceName; CountryName = principal.CountryName; } /// /// A name that can be used to represent the issuer. Chromium tries in this /// order: Common Name (CN), Organization Name (O) and Organizational Unit /// Name (OU) and returns the first non-empty one found. /// public readonly string DisplayName; /// /// The Common Name (CN) of the issuer or subject. /// public readonly string CommonName; /// /// The locality name. /// public readonly string LocalityName; /// /// The state or province name. /// public readonly string StateOrProvinceName; /// /// The country name. /// public readonly string CountryName; public override string ToString() { return $"DisplayName = {DisplayName}, CommonName = {CommonName}, LocalityName = {LocalityName}, StateOrProvinceName = {StateOrProvinceName}, CountryName = {CountryName}"; } } /// /// Event args for StandaloneWebView.ClientCertificateRequested. /// [Serializable] public class StandaloneClientCertificateRequestedEventArgs : EventArgs { private StandaloneClientCertificateRequestedEventArgs(CertificateRequestedMessage message, Action selectCallback) { Certificates = message.Certificates.ToList().Select(c => new StandaloneX509Certificate(c)).ToArray(); Host = message.Host; Port = message.Port; IsProxy = message.IsProxy; Select = selectCallback; } /// /// The list of certificates to choose from. This list has already been pruned by /// Chromium so that it only contains certificates from issuers that the /// server trusts. /// public readonly StandaloneX509Certificate[] Certificates; /// /// The hostname of the SSL server. /// public readonly string Host; /// /// The port of the SSL server. /// public readonly int Port; /// /// Indicates whether the host is an HTTPS proxy or the origin server. /// public readonly bool IsProxy; /// /// The callback to invoke to select a certificate. The certificate parameter /// can either be one of the certificates from the Certificates array or `null` /// to continue without a certificate. /// public readonly Action Select; public override string ToString() { var certificatesString = Certificates.Length == 0 ? "[]" : $"[\n{String.Join(", ", Certificates.ToList().Select(c => c.ToString()))}\n]"; return $"(StandaloneClientCertificateRequestedEventArgs)\nHost = {Host},\nPort = {Port},\nIsProxy = {IsProxy},\nCertificates = {certificatesString}"; } internal static StandaloneClientCertificateRequestedEventArgs FromMessageJson(string serializedMessage, Action selectCallback) { var message = JsonUtility.FromJson(serializedMessage); return new StandaloneClientCertificateRequestedEventArgs(message, selectCallback); } } } namespace Vuplex.WebView.Internal { [Serializable] class CertificateRequestedMessage { public MessageCertificate[] Certificates; public string Host; public int Port; public bool IsProxy; } [Serializable] public class MessageCertificate { public int ID; public MessageCertificatePrincipal Issuer; public MessageCertificatePrincipal Subject; public long ValidStart; public long ValidExpiry; } [Serializable] public class MessageCertificatePrincipal { public string DisplayName; public string CommonName; public string LocalityName; public string StateOrProvinceName; public string CountryName; } }