# How v2rayN Handles SSL Certificate Management with CertPemManager

> Discover how v2rayN's CertPemManager fetches X.509 certificates, enforces CA pinning, and exports PEM for secure proxy configurations. Learn about its SSL certificate management.

- Repository: [2dust/v2rayN](https://github.com/2dust/v2rayN)
- Tags: internals
- Published: 2026-02-27

---

**v2rayN uses the `CertPemManager` class to fetch X.509 certificates from remote servers, enforce strict CA pinning against a whitelist of trusted root thumbprints, and export valid certificates in PEM format for secure proxy configurations.**

v2rayN, the popular Windows GUI client for V2Ray, implements a robust SSL certificate management system to prevent man-in-the-middle attacks during proxy connections. At the core of this system lies the `CertPemManager` class located in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs), which handles everything from TCP/TLS handshakes to certificate chain validation and PEM encoding.

## The CertPemManager Architecture

The `CertPemManager` operates as a singleton that encapsulates certificate retrieval, validation, and conversion logic. It combines low-level socket operations with high-level X509 chain verification to ensure that only certificates issued by explicitly trusted Certificate Authorities are accepted.

### Trusted CA Thumbprint Pinning

At the heart of the validation logic is a hard-coded whitelist of trusted root CA thumbprints. The `TrustedCaThumbprints` property (defined in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs) at lines 18-44) contains a `HashSet<string>` of SHA-256 thumbprints for known root certificates.

This implementation enables **certificate pinning** at the CA level. When connecting to a remote server, v2rayN does not rely solely on the Windows certificate store; instead, it computes the thumbprint of the root certificate in the chain and verifies its presence in this whitelist. If the thumbprint is not found, the connection is rejected, effectively blocking certificates issued by untrusted or rogue authorities.

### Establishing TCP/TLS Connections

The certificate retrieval process begins with establishing a raw TCP connection upgraded to TLS. The `GetCertPemAsync` and `GetCertChainPemAsync` methods handle this workflow (connection logic at lines 10-27):

1. **URL Parsing**: The target URL is parsed using `Utils.ParseUrl` to extract the host and port components.
2. **TCP Connection**: A `TcpClient` connects to the target host (defaulting to port 443) with a configurable timeout enforced by a `CancellationTokenSource`.
3. **TLS Handshake**: An `SslStream` is created over the TCP socket, configured with `SslClientAuthenticationOptions` that specify the target host and the custom validation callback.

This low-level approach gives v2rayN fine-grained control over the TLS handshake process, allowing it to intercept the server certificate before any application data is transmitted.

## Certificate Validation and Chain Verification

Once the TLS handshake completes, the custom validation logic takes over to enforce the CA pinning policy.

### The ValidateServerCertificate Callback

The `ValidateServerCertificate` method (implemented in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs) at lines 30-40) serves as the `RemoteCertificateValidationCallback` for the `SslStream`. This method performs several critical checks:

- **Null Certificate Check**: Immediately rejects connections where no certificate is presented.
- **Name Mismatch Detection**: Checks for `SslPolicyErrors.RemoteCertificateNameMismatch` to ensure the certificate matches the target hostname.
- **Chain Building**: Constructs an `X509Chain` with revocation checking enabled to build the full certificate hierarchy.

### Building and Verifying the X509 Chain

The validation process constructs an `X509Chain` object to establish the certificate's lineage. After successfully building the chain with `certChain.Build(cert2)`, the logic identifies the root certificate (the final element in the chain).

The root certificate's SHA-256 thumbprint is computed using `rootCert.GetCertHashString(HashAlgorithmName.SHA256)` and compared against the `TrustedCaThumbprints` whitelist. The connection is accepted only if the thumbprint exists in the whitelist, creating a strict **CA pinning** mechanism that prevents connections to servers using certificates from unknown authorities.

## PEM Export and Certificate Retrieval

After validation, v2rayN converts the certificates to standard PEM format for storage or transmission to the underlying V2Ray core.

### Converting Certificates to PEM Format

The `ExportCertToPem` method (located in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs) at lines 42-46) handles the conversion from `X509Certificate2` to PEM string:

1. Exports the certificate in DER format using `Export(X509ContentType.Cert)`
2. Base64-encodes the DER bytes
3. Wraps the Base64 string in standard PEM delimiters (`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`)

This produces a standard PEM-encoded certificate that can be used directly in V2Ray configurations or saved to disk.

### Retrieving Full Certificate Chains

For scenarios requiring the complete trust path, `GetCertChainPemAsync` (source at lines 80-86) builds the full `X509Chain` and returns a list of PEM strings representing each certificate in the hierarchy:

```csharp
var (chainPemList, error) = await CertPemManager.Instance
    .GetCertChainPemAsync("https://example.com", "example.com");

if (error == null)
{
    // Normalise each PEM entry (remove line-breaks inside Base64)
    var normalised = chainPemList.Select(CertPemManager.ParsePemChain).SelectMany(c => c);
    var fullChain = CertPemManager.ConcatenatePemChain(normalised);
    Console.WriteLine(fullChain);
}
else
{
    Console.WriteLine($"Chain retrieval error: {error}");
}

```

Each certificate in the chain is processed through `ExportCertToPem`, resulting in a list where the first element is typically the leaf certificate and the last is the root CA certificate.

Both `GetCertPemAsync` (lines 34-36) and `GetCertChainPemAsync` implement comprehensive error handling, catching `OperationCanceledException` for timeouts and general exceptions for connection failures, logging all errors via `Logging.SaveLog`.

## Utility Methods for Certificate Processing

The `CertPemManager` provides several static utility methods for manipulating PEM-encoded certificate data (lines 54-73 and 84-92).

### Parsing and Normalizing PEM Chains

When handling concatenated certificate strings, `ParsePemChain` and `ConcatenatePemChain` ensure proper formatting:

- **ParsePemChain**: Splits concatenated PEM certificates into individual certificate strings, normalizing whitespace and ensuring clean extraction of each certificate block.
- **ConcatenatePemChain**: Combines multiple PEM certificates into a single string with proper newline separation, ensuring exactly one newline between certificates for compatibility with standard TLS libraries.

These methods are essential when processing certificate chains retrieved from servers or when constructing multi-certificate PEM files for V2Ray configurations.

### Computing SHA-256 Thumbprints

The `GetCertSha256Thumbprint` method computes the SHA-256 fingerprint of a PEM-encoded certificate:

```csharp
// Example: Retrieve leaf certificate and compute its thumbprint
var (leafPem, err) = await CertPemManager.Instance
                     .GetCertPemAsync("https://example.com", "example.com");

if (err == null)
{
    string thumbprint = CertPemManager.GetCertSha256Thumbprint(leafPem, includeColon: true);
    Console.WriteLine($"SHA-256 thumbprint: {thumbprint}");
}

```

This utility decodes the PEM certificate, computes the SHA-256 hash, and optionally formats the result with colons for readability (e.g., `AB:CD:EF:...`). This is particularly useful for verifying certificate identities against known good fingerprints or for logging purposes.

## Summary

- **CertPemManager** in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs) provides centralized SSL certificate retrieval and validation for v2rayN.
- **CA Pinning**: The implementation uses a hard-coded whitelist of trusted root CA SHA-256 thumbprints (`TrustedCaThumbprints`) to strictly validate certificate chains, rejecting any certificates not anchored by known authorities.
- **Low-level TLS Control**: Methods like `GetCertPemAsync` and `GetCertChainPemAsync` manually establish TCP connections and `SslStream` objects to intercept certificates before data transmission.
- **PEM Conversion**: The `ExportCertToPem` method converts `X509Certificate2` objects to standard PEM format using DER encoding and Base64 wrapping.
- **Chain Processing**: Utility methods `ParsePemChain`, `ConcatenatePemChain`, and `GetCertSha256Thumbprint` provide robust handling of certificate chains and fingerprint computation.

## Frequently Asked Questions

### How does v2rayN prevent man-in-the-middle attacks during certificate retrieval?

v2rayN prevents MITM attacks through strict **CA pinning** implemented in the `ValidateServerCertificate` callback. Rather than relying on the operating system's certificate store, v2rayN computes the SHA-256 thumbprint of the root certificate in the chain and verifies its presence in the `TrustedCaThumbprints` whitelist (lines 18-44 in [`CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/CertPemManager.cs)). If the root CA is not in the whitelist, the connection is immediately rejected, effectively blocking certificates issued by rogue or compromised authorities.

### What is the difference between GetCertPemAsync and GetCertChainPemAsync in v2rayN?

`GetCertPemAsync` retrieves only the **leaf certificate** (the end-entity certificate presented by the server) and returns it as a single PEM-encoded string (source at lines 34-36). In contrast, `GetCertChainPemAsync` builds the complete `X509Chain` and returns a **list of PEM strings** representing every certificate in the hierarchy from leaf to root (source at lines 80-86). The chain method is essential when you need to validate the entire trust path or configure V2Ray with a complete certificate bundle.

### How does CertPemManager convert X509 certificates to PEM format?

The `ExportCertToPem` method (located at lines 42-46 in [`CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/CertPemManager.cs)) performs the conversion by first exporting the `X509Certificate2` object to DER format using `Export(X509ContentType.Cert)`. It then Base64-encodes the resulting byte array and wraps it with standard PEM delimiters (`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`). This produces a RFC-compliant PEM string that can be used directly in V2Ray configurations or saved to disk.

### Where does v2rayN store its trusted root CA thumbprints for certificate pinning?

v2rayN stores trusted root CA SHA-256 thumbprints in the `TrustedCaThumbprints` property of the `CertPemManager` class, located in [`v2rayN/ServiceLib/Manager/CertPemManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/CertPemManager.cs) at lines 18-44. This is implemented as a hard-coded `HashSet<string>` containing hexadecimal thumbprints of known root CAs. This whitelist approach ensures that v2rayN only accepts certificates from explicitly trusted authorities, regardless of the Windows certificate store configuration.