Class ClientCertificatePinning
Centralised TLS certificate pinning for outbound UnityWebRequest
traffic (launcher version probe, patch download, login-server discovery,
renewal endpoints, etc.).
Pins are SHA-256 hashes of the SubjectPublicKeyInfo (SPKI) DER
encoding, base64-encoded — the same format used by HPKP / RFC 7469. SPKI
pinning is preferred over full-certificate pinning because it survives
certificate renewals as long as the underlying key pair is reused, and is
preferred over CA pinning because it cannot be bypassed by any CA in the
trust store.
Unity's CertificateHandler.ValidateCertificate(byte[]) only exposes
the leaf certificate, so this implementation deliberately ignores chain
validity and instead requires that the leaf's SPKI match a known-good
pin. Always configure at least two pins (active + backup) so an emergency
key rotation does not require a client patch.
IL2CPP / AOT platforms (WebGL, iOS, consoles):
BouncyCastle relies on runtime reflection for algorithm lookup and type
activation. Without linker instructions, the managed-code linker will
strip the types it needs. Ensure Assets/link.xml includes:
<assembly fullname="Org.BouncyCastle" preserve="all"/>.
public static class ClientCertificatePinning
- Inheritance
-
ClientCertificatePinning
- Inherited Members
Properties
HasPins
True when at least one pin has been registered. Callers can use this to short-circuit a network request before it is dispatched.
public static bool HasPins { get; }
Property Value
Methods
ComputeSpkiSha256Base64(byte[])
Compute the base64-encoded SHA-256 of the certificate's
SubjectPublicKeyInfo. Exposed so build tooling can derive pin
values from a PEM/DER cert without depending on OpenSSL.
public static string ComputeSpkiSha256Base64(byte[] certificateDer)
Parameters
certificateDerbyte[]
Returns
Configure(IEnumerable<string>, bool)
Replace the active pin set. Pins are SHA-256(SPKI) base64 strings.
Passing null or an empty collection clears the pin set.
Restricted to editor / development builds. Release builds must use SetInitialPins(string[]) and TryAugmentPins(string[]) instead.
public static void Configure(IEnumerable<string> newPins, bool allowOnEmpty = false)
Parameters
newPinsIEnumerable<string>The new pin list. Whitespace and empty entries are ignored.
allowOnEmptyboolWhen
trueand the pin set is empty, ValidateCertificate(byte[]) will fall back to temporal validity only instead of rejecting the certificate outright. Use only for editor / development builds.
SetInitialPins(string[])
Set the initial pin set at bootstrap. Must be called before any network requests are dispatched. In release builds, an empty or null pin set causes all TLS connections to be rejected (fail-closed).
Unlike Configure(IEnumerable<string>, bool), this method cannot be used to clear pins once they have been set — it is a one-way door.
public static void SetInitialPins(string[] initialPins)
Parameters
initialPinsstring[]The compile-time pin set from GeneratedPinSet.
TryAugmentPins(string[])
Add pins from a verified API manifest. Only ADDS to the existing set — never removes. A compromised API response can inject bogus pins but cannot disable the compile-time pins already configured.
This is safe to call at any time. Thread-safe.
public static void TryAugmentPins(string[] newPins)
Parameters
newPinsstring[]Pins to add to the active set.
ValidateCertificate(byte[])
Validate a leaf certificate (DER-encoded) against the configured pin set. Performs:
- DER → Org.BouncyCastle.X509.X509Certificate parse via BouncyCastle.
- NotBefore / NotAfter temporal check (UTC).
- SHA-256(SPKI) computation and constant-time comparison against every configured pin.
public static bool ValidateCertificate(byte[] certificateDer)
Parameters
certificateDerbyte[]Raw DER bytes from the server.
Returns
- bool
truewhen the certificate is accepted.