// 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;
}
}