Table of Contents

Class ClientCertificatePinning

Namespace
FishMMO.Client.Security
Assembly
FishMMO.Client.dll

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

bool

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

certificateDer byte[]

Returns

string

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

newPins IEnumerable<string>

The new pin list. Whitespace and empty entries are ignored.

allowOnEmpty bool

When true and 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

initialPins string[]

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

newPins string[]

Pins to add to the active set.

ValidateCertificate(byte[])

Validate a leaf certificate (DER-encoded) against the configured pin set. Performs:

  1. DER → Org.BouncyCastle.X509.X509Certificate parse via BouncyCastle.
  2. NotBefore / NotAfter temporal check (UTC).
  3. SHA-256(SPKI) computation and constant-time comparison against every configured pin.
public static bool ValidateCertificate(byte[] certificateDer)

Parameters

certificateDer byte[]

Raw DER bytes from the server.

Returns

bool

true when the certificate is accepted.