Secure Connection Mode#

CloudXR Framework supports secure connections to CloudXR Runtime servers using TLS encryption and optional client token authentication. This document describes the available server configurations and how to connect securely from your client application.

Overview#

In secure connection mode, CloudXR Framework uses:

  • TLS/SSL encryption for the signaling connection

  • Client token authentication (optional) to verify the client’s identity with the server

  • Certificate validation to ensure the client is connecting to the intended server

Server Setup Options#

There are three ways to configure the server for secure connections:

Server Setup

Description

Connection Types

Use Case

Runtime API with TLS

Configure TLS directly using Runtime Management API properties (certificate-pem, key-pem, client-token)

localSecure, remoteSecure

Full control, custom deployments

HTTPS Reverse Proxy

Run Runtime in non-secure mode behind an HTTPS reverse proxy with CA-signed certificate

remoteSecure

Cloud/production deployments

Stream Manager

Use Stream Manager to manage Runtime with auto-generated self-signed certificate

localSecure only

Local/LAN development

Option 1: Runtime API with TLS#

Configure TLS directly using the Runtime Management API. This gives you full control over certificate and authentication configuration.

Required properties:

  • certificate-pem: Path to your X.509 certificate file

  • key-pem: Path to your private key file

  • client-token (optional): Preshared token for client authentication

Example using your own certificate:

// Set TLS certificate and key
nv_cxr_service_set_string_property(service, "certificate-pem", "/path/to/cert.pem");
nv_cxr_service_set_string_property(service, "key-pem", "/path/to/key.pem");

// Optionally set client token for authentication
nv_cxr_service_set_string_property(service, "client-token", "your-secret-token");

// Start the service
nv_cxr_service_start(service);

This approach works for both localSecure (with self-signed certificate and fingerprint validation) and remoteSecure (with CA-signed certificate and system trust validation) connection types.

Option 2: HTTPS Reverse Proxy#

Run CloudXR Runtime in non-secure mode (with no TLS configuration), and place an HTTPS reverse proxy in front of it. The proxy handles TLS termination using a CA-signed certificate.

This is the recommended approach for cloud and production deployments. Clients use a remoteSecure connection with system trust validation. No fingerprint distribution is required.

Architecture:

Client (remoteSecure) →
wss://cloudxr.example.com:48322
Proxy →
ws://localhost:48010
Runtime

Requirements:

  • A public domain name (e.g., cloudxr.example.com)

  • A CA-signed X.509 certificate from a trusted Certificate Authority

  • A reverse proxy (e.g., nginx, HAProxy) configured for WebSocket forwarding

Proxy configuration example (nginx):

server {
    listen 48322 ssl;
    server_name cloudxr.example.com;

    ssl_certificate /etc/ssl/certs/cloudxr.example.com.crt;
    ssl_certificate_key /etc/ssl/private/cloudxr.example.com.key;

    location / {
        proxy_pass http://localhost:48010;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Note

CloudXR.js uses a similar proxy approach for HTTPS hosting. Refer to WebSocket Proxy Setup for additional deployment examples including Docker and Kubernetes configurations.

Option 3: Stream Manager (Local Only)#

The Stream Manager automatically generates a self-signed certificate and manages client tokens. This is the simplest setup for local/LAN environments.

Important

Stream Manager uses a self-signed certificate, which requires fingerprint-based validation on the client. This only works with localSecure connections, and is not suitable for remote/cloud deployments.

  1. Start the Stream Manager.

    Enter this command:

    NvStreamManager.exe
    
  2. Set the client ID and get an auth token.

    Enter this command at the Stream Manager’s command prompt (“>”):

    SetClientId MyAVPClient
    

    The Stream Manager displays this console output:

    Setting client ID: MyAVPClient
    Client ID set successfully
    Token: <server_generated_auth_token>
    

    Save the auth token for your client application.

  3. Get the certificate fingerprint.

    Enter this command at the Stream Manager’s command prompt:

    GetCryptoKeyFingerprint 2
    

    The Stream Manager displays this console output:

    Getting cryptographic key fingerprint for algorithm: 2
    Cryptographic key fingerprint: <fingerprint>
    

    <fingerprint> is a 64-digit hexadecimal number. Share it with your client application through a trusted channel.

    The parameter (‘2’ in the command above) specifies the hash algorithm:

    • 0 - MD5

    • 1 - SHA-1

    • 2 - SHA-256 (recommended)

    • 3 - SHA-512

  4. Start CloudXR Runtime.

    Enter this command at the Stream Manager’s command prompt:

    StartCxrService 6.0.0
    

    The Stream Manager displays this console output:

    Starting CloudXR service with version: 6.0.0
    CloudXR service start request successful
    

Client Connection Types#

CloudXR Framework provides two secure connection types:

Local Secure Connection (localSecure): For LAN connections using self-signed certificates with fingerprint validation

  • Works with: Runtime API with TLS, Stream Manager

  • Certificate validation: Fingerprint-based

  • Use case: Development, testing, local deployments

Remote Secure Connection (remoteSecure): For cloud connections using CA-signed certificates with system trust validation

  • Works with: Runtime API with TLS, HTTPS Reverse Proxy

  • Certificate validation: System trust store

  • Use case: Cloud, production deployments

Configuring the Client#

Both connection types require a certificateValidationHandler callback that receives an URLAuthenticationChallenge and returns a tuple of URLSession.AuthChallengeDisposition and optional URLCredential.

Local Secure Connection#

Use ConnectionType.localSecure for secure connections using self-signed certificates (with Runtime API or Stream Manager). This requires fingerprint-based certificate validation:

import CloudXRKit
import CryptoKit
import Security

// The client token obtained from server
let clientToken = "AUTH_TOKEN_GENERATED_BY_SERVER"

// The expected certificate fingerprint (SHA-256)
let expectedFingerprint = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

var config = CloudXRKit.Config()
config.connectionType = .localSecure(
    ip: "192.168.1.100",
    clientToken: clientToken,
    certificateValidationHandler: { challenge in
        // Validate using fingerprint for self-signed certificates
        guard let serverTrust = challenge.protectionSpace.serverTrust,
              let certificateChain = SecTrustCopyCertificateChain(serverTrust),
              CFArrayGetCount(certificateChain) > 0 else {
            return (.cancelAuthenticationChallenge, nil)
        }

        let certificate = unsafeBitCast(
            CFArrayGetValueAtIndex(certificateChain, 0),
            to: SecCertificate.self
        )

        let certificateData = SecCertificateCopyData(certificate) as Data
        let fingerprint = SHA256.hash(data: certificateData)
            .map { String(format: "%02x", $0) }
            .joined()

        if fingerprint.lowercased() == expectedFingerprint.lowercased() {
            return (.useCredential, URLCredential(trust: serverTrust))
        } else {
            return (.cancelAuthenticationChallenge, nil)
        }
    }
)

cxrSession.configure(config: config)
try await cxrSession.connect()

Remote Secure Connection#

Use ConnectionType.remoteSecure for secure connections using CA-signed certificates (with Runtime API or HTTPS Reverse Proxy). This uses the operating system’s certificate trust store:

import CloudXRKit

var config = CloudXRKit.Config()
config.connectionType = .remoteSecure(
    host: "cloudxr.example.com",  // Your public domain with CA-signed certificate
    signalingHeaders: [
        "Authorization": "Bearer your-auth-token",
        "X-Custom-Header": "custom-value"
    ],
    certificateValidationHandler: { challenge in
        // Validate using system trust store for CA-signed certificates
        guard let serverTrust = challenge.protectionSpace.serverTrust else {
            return (.cancelAuthenticationChallenge, nil)
        }

        var error: CFError?
        if SecTrustEvaluateWithError(serverTrust, &error) {
            return (.useCredential, URLCredential(trust: serverTrust))
        } else {
            return (.cancelAuthenticationChallenge, nil)
        }
    }
)

cxrSession.configure(config: config)
try await cxrSession.connect()

Trust All Certificates (Development Only)#

Warning

Use this approach only during development and testing. Never use it in production.

certificateValidationHandler: { challenge in
    guard let serverTrust = challenge.protectionSpace.serverTrust else {
        return (.cancelAuthenticationChallenge, nil)
    }
    return (.useCredential, URLCredential(trust: serverTrust))
}

Session States#

When using secure connection mode, the session transitions through additional authentication states:

  1. initialized: The initial state

  2. authenticating: Validating credentials with server

  3. authenticated: Credentials validated successfully

  4. connecting: Establishing streaming connection

  5. connected: Streaming active

You can monitor these states:

switch cxrSession.state {
case .initialized:
    print("Ready to connect")
case .authenticating:
    print("Validating credentials...")
case .authenticated:
    print("Credentials validated")
case .connecting:
    print("Establishing stream...")
case .connected:
    print("Streaming!")
case .disconnected(let result):
    switch result {
    case .success:
        print("Disconnected normally")
    case .failure(let error):
        print("Error: \(error)")
    }
default:
    break
}

Error Handling#

Secure connections can fail due to:

  • Invalid client token: The token doesn’t match the server’s expected value.

  • Certificate validation failure: The certificate fingerprint doesn’t match.

  • Network errors: The client is unable to reach the server.

  • TLS handshake failure: TLS versions or cipher suites are incompatible.

Handle these errors appropriately:

do {
    try await cxrSession.connect()
} catch let error as StreamingError {
    switch error {
    case .connectionFailed:
        print("Failed to connect - check server address and token")
    case .authenticationFailed:
        print("Authentication failed - verify client token")
    default:
        print("Streaming error: \(error)")
    }
} catch {
    print("Unexpected error: \(error)")
}

Best Practices#

For Local Secure Connections (LAN/Development):

  1. Store credentials securely: Use Keychain to store client tokens, not UserDefaults.

  2. Use SHA-256 fingerprints: SHA-256 provides strong security without being too cumbersome.

  3. Distribute fingerprints securely: Share certificate fingerprints through a trusted channel, not over the network.

  4. Rotate tokens periodically: Generate new client tokens for each session or deployment.

  5. Use Stream Manager: Stream Manager simplifies certificate and token management for local deployments.

For Remote Secure Connections (Cloud/Production):

  1. Use CA-signed certificates: Deploy with certificates from a trusted Certificate Authority.

  2. Use HTTPS reverse proxy: HTTPS reverse proxy handles TLS termination centrally, and is recommended for cloud deployments.

  3. Use a public domain: Configure a proper domain name for your streaming endpoint.

  4. Use system trust validation: Leverage the OS certificate trust store instead of using fingerprint pinning.

General:

  1. Handle authentication states: Show appropriate information in the UI during the authentication phase.

  2. Implement proper error handling: Provide clear feedback for authentication and certificate errors.

Network Requirements#

Secure connections use the following ports:

  • TCP 48322: Secure a signaling connection (TLS). It replaces port 48010 used in standard mode.

  • UDP 47998-48002, 48005, 48008, and 48012: Use for media streaming (video, input, and audio).

Note

When using secure connection mode, port 48010 is not used. All signaling traffic goes through port 48322 with TLS encryption.

Ensure that these ports are open in your firewall. Refer to Network Setup for complete network configuration.

Troubleshooting#

Connection fails with “authentication failed”:

  • Verify that the client token matches the server’s configured value.

  • Ensure that Stream Manager is running before starting the CloudXR service.

  • For the Runtime API, verify that the client-token property is set correctly.

  • Check that the token is transmitted correctly, with no extra whitespace or encoding issues.

Certificate validation fails:

  • For fingerprint validation: - Verify that the client’s fingerprint was copied correctly, with no extra whitespace.

    • Ensure that you’re using the same hash algorithm on client and server (SHA-256 recommended).

    • Ensure that the server certificate was regenerated (get new fingerprint).

  • For system trust validation: - Verify that the server’s CA-signed certificate is valid and not expired. - Ensure that the certificate chain is complete.

TLS handshake fails:

  • Verify that the server supports TLS 1.2 or later/

  • Check that the server logs for TLS-related errors.

  • Ensure that the firewall allows TLS traffic on port 48322.

  • For Runtime API: Verify that certificate and key file paths are correct and readable.

See Also#