# HTTPS/TLS Setup Guide - OpenHarbor.MCP.Client **Purpose**: Secure HTTPS/TLS configuration for MCP client connections **Audience**: Application developers, system integrators **Last Updated**: 2025-10-19 **Version**: 1.0.0 --- ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [Client Configuration](#client-configuration) 4. [Certificate Validation](#certificate-validation) 5. [Authentication](#authentication) 6. [Error Handling](#error-handling) 7. [Best Practices](#best-practices) 8. [Troubleshooting](#troubleshooting) --- ## Overview OpenHarbor.MCP.Client supports HTTPS connections to MCP servers with full TLS certificate validation, custom certificate handling, and API key authentication. **Security Features:** - Encrypted communication (TLS 1.2+) - Server certificate validation - Custom certificate authority support - Client certificate authentication (mutual TLS) - API key authentication via headers **Default Behavior:** - HTTPS enabled by default when URL starts with `https://` - Certificate validation enabled (can be customized) - Connection pooling for performance - Timeout configuration (default: 30 seconds) --- ## Prerequisites ### Development - .NET 8.0 SDK - Access to MCP server with HTTPS enabled - API key for authentication (if required) ### Production - Valid server certificates - Network connectivity to server - Firewall allows outbound HTTPS (port 443) --- ## Client Configuration ### Basic HTTPS Connection **Example 1: Simple HTTPS Client** ```csharp using OpenHarbor.MCP.Client; // Connect to HTTPS server var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", // HTTPS URL ApiKey = "your-api-key-here" // Authentication }; using var connection = new HttpServerConnection(config); // Use connection var tools = await connection.ListToolsAsync(); ``` **Configuration Options:** ```csharp public class HttpServerConnectionConfig { /// /// Server URL (must start with https:// for secure connection) /// public string ServerUrl { get; set; } = "https://localhost:5051"; /// /// API key for authentication (sent via X-API-Key header) /// public string? ApiKey { get; set; } /// /// Request timeout (default: 30 seconds) /// public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); /// /// Maximum connections per server (default: 10) /// public int MaxConnectionsPerServer { get; set; } = 10; /// /// Custom certificate validation callback /// public Func? ServerCertificateCustomValidation { get; set; } /// /// Client certificate for mutual TLS (optional) /// public X509Certificate2? ClientCertificate { get; set; } } ``` ### Example 2: Development with Self-Signed Certificate For development servers using self-signed certificates: ```csharp using System.Net.Security; using System.Security.Cryptography.X509Certificates; var config = new HttpServerConnectionConfig { ServerUrl = "https://localhost:5051", ApiKey = "dev-api-key", // WARNING: Only use in development! ServerCertificateCustomValidation = (request, cert, chain, errors) => { // Accept any certificate (INSECURE - development only!) return true; } }; using var connection = new HttpServerConnection(config); ``` **Security Warning**: Never use `ServerCertificateCustomValidation = (_, _, _, _) => true` in production! ### Example 3: Custom Certificate Authority For servers using internal/corporate CA: ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://internal-mcp.corp.local", ApiKey = "api-key", ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (errors == SslPolicyErrors.None) return true; // Custom CA validation if (errors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors)) { // Load corporate CA certificate var corpCaCert = new X509Certificate2("corp-ca.crt"); // Build chain with custom CA var customChain = new X509Chain(); customChain.ChainPolicy.ExtraStore.Add(corpCaCert); customChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; bool isValid = customChain.Build(cert); customChain.Dispose(); return isValid; } return false; } }; ``` ### Example 4: Production Configuration ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY"), Timeout = TimeSpan.FromSeconds(60), MaxConnectionsPerServer = 20, // Default certificate validation (recommended for production) ServerCertificateCustomValidation = null // Use system trust store }; using var connection = new HttpServerConnection(config); ``` --- ## Certificate Validation ### Default Validation By default, the client uses .NET's built-in certificate validation: 1. **Certificate is trusted** (issued by known CA) 2. **Certificate is valid** (not expired, not yet valid) 3. **Certificate matches hostname** (CN or SAN matches server URL) ### Custom Validation Scenarios **Scenario 1: Accept Specific Self-Signed Certificate** ```csharp var expectedThumbprint = "A1B2C3D4E5F6..."; // Certificate thumbprint ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (cert == null) return false; // Verify thumbprint matches expected return cert.Thumbprint.Equals(expectedThumbprint, StringComparison.OrdinalIgnoreCase); } ``` **Scenario 2: Log and Validate** ```csharp ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (errors == SslPolicyErrors.None) return true; // Log validation errors Console.WriteLine($"Certificate validation error: {errors}"); if (cert != null) { Console.WriteLine($"Subject: {cert.Subject}"); Console.WriteLine($"Issuer: {cert.Issuer}"); Console.WriteLine($"Valid from: {cert.NotBefore} to {cert.NotAfter}"); } // Reject in production, accept in development return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; }; ``` **Scenario 3: Certificate Pinning** For high-security requirements, pin specific certificates: ```csharp private static readonly HashSet PinnedCertificates = new() { "A1B2C3D4E5F6...", // Primary server cert "B2C3D4E5F6A1..." // Backup server cert }; ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (cert == null) return false; // Only accept pinned certificates return PinnedCertificates.Contains(cert.Thumbprint); }; ``` --- ## Authentication ### API Key Authentication **Method 1: Configuration Object** ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", ApiKey = "your-api-key" // Sent as X-API-Key header }; ``` **Method 2: Environment Variable** ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY") }; ``` **Method 3: Azure Key Vault / Secrets Manager** ```csharp // Example with Azure Key Vault var keyVaultClient = new SecretClient(new Uri("https://your-vault.vault.azure.net"), new DefaultAzureCredential()); var apiKeySecret = await keyVaultClient.GetSecretAsync("mcp-api-key"); var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", ApiKey = apiKeySecret.Value.Value }; ``` ### Mutual TLS (Client Certificates) For bidirectional authentication: ```csharp // Load client certificate var clientCert = new X509Certificate2("client-cert.pfx", "password"); var config = new HttpServerConnectionConfig { ServerUrl = "https://mcp.example.com", ClientCertificate = clientCert, // Present to server during TLS handshake ApiKey = "api-key" // Additional API key authentication }; using var connection = new HttpServerConnection(config); ``` **Certificate Requirements:** - Must include private key (PFX/PKCS#12 format) - Must be valid (not expired) - Server must be configured to accept client certificates --- ## Error Handling ### Common HTTPS Errors **Error 1: Certificate Validation Failed** ``` System.Net.Http.HttpRequestException: The SSL connection could not be established Inner Exception: The remote certificate is invalid according to the validation procedure. ``` **Solution:** 1. Verify server certificate is valid 2. Check certificate is issued by trusted CA 3. Ensure hostname matches certificate CN/SAN 4. For development, use custom validation (see examples above) **Error 2: Connection Timed Out** ``` System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout ``` **Solution:** 1. Increase timeout value 2. Check network connectivity 3. Verify firewall allows HTTPS traffic ```csharp var config = new HttpServerConnectionConfig { Timeout = TimeSpan.FromSeconds(120) // Increase timeout }; ``` **Error 3: Hostname Mismatch** ``` The remote certificate is invalid: the name on the certificate does not match the hostname ``` **Solution:** 1. Use correct hostname in ServerUrl (match certificate CN/SAN) 2. Update certificate to include correct hostname 3. Use IP address if certificate has IP SAN **Error 4: Expired Certificate** ``` The remote certificate is invalid: the certificate has expired ``` **Solution:** 1. Renew server certificate 2. For temporary workaround (development only): ```csharp ServerCertificateCustomValidation = (request, cert, chain, errors) => { // Check if only error is expiration if (errors == SslPolicyErrors.RemoteCertificateChainErrors) { return chain.ChainStatus.All(s => s.Status == X509ChainStatusFlags.NotTimeValid); } return errors == SslPolicyErrors.None; }; ``` ### Graceful Degradation Handle HTTPS errors gracefully: ```csharp try { using var connection = new HttpServerConnection(config); var tools = await connection.ListToolsAsync(); } catch (HttpRequestException ex) when (ex.InnerException is System.Security.Authentication.AuthenticationException) { // TLS/certificate error Console.WriteLine($"HTTPS connection failed: {ex.Message}"); Console.WriteLine("Check server certificate configuration"); // Optionally: retry with HTTP (if server supports it) } catch (TaskCanceledException ex) { // Timeout Console.WriteLine($"Connection timed out after {config.Timeout.TotalSeconds}s"); // Optionally: retry with longer timeout } ``` --- ## Best Practices ### 1. Never Disable Certificate Validation in Production ```csharp // ❌ WRONG (insecure) ServerCertificateCustomValidation = (_, _, _, _) => true; // ✅ CORRECT (secure) ServerCertificateCustomValidation = null; // Use default validation ``` ### 2. Store API Keys Securely ```csharp // ❌ WRONG (hardcoded) ApiKey = "sk_live_1234567890abcdef"; // ✅ CORRECT (environment variable) ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY"); // ✅ BETTER (secrets manager) ApiKey = await secretsManager.GetSecretAsync("mcp-api-key"); ``` ### 3. Use Connection Pooling ```csharp // ❌ WRONG (creates new connection every time) foreach (var request in requests) { using var connection = new HttpServerConnection(config); await connection.ExecuteToolAsync("tool", args); } // ✅ CORRECT (reuse connection) using var connection = new HttpServerConnection(config); foreach (var request in requests) { await connection.ExecuteToolAsync("tool", args); } ``` ### 4. Handle Errors Appropriately ```csharp try { var result = await connection.ExecuteToolAsync("tool", args); } catch (HttpRequestException ex) { // Log error, retry, or fail gracefully _logger.LogError(ex, "MCP tool execution failed"); throw; } ``` ### 5. Configure Timeouts ```csharp // Short-lived operations Timeout = TimeSpan.FromSeconds(10); // Long-running operations Timeout = TimeSpan.FromMinutes(5); // Default (reasonable for most cases) Timeout = TimeSpan.FromSeconds(30); ``` --- ## Troubleshooting ### Issue: "The remote certificate is invalid" **Diagnosis:** ```bash # Check server certificate echo | openssl s_client -connect mcp.example.com:443 -servername mcp.example.com 2>/dev/null | openssl x509 -noout -text ``` **Common Causes:** 1. Self-signed certificate without custom validation 2. Expired certificate 3. Hostname mismatch 4. Untrusted CA **Solution:** - Ensure server has valid certificate from trusted CA - Or implement custom validation for development ### Issue: Connection Hangs/Timeout **Diagnosis:** ```bash # Test connectivity telnet mcp.example.com 443 # Test with curl curl -v https://mcp.example.com/health ``` **Common Causes:** 1. Firewall blocking port 443 2. Server not responding 3. Network connectivity issue **Solution:** - Verify firewall rules - Check server is running - Increase timeout if server is slow ### Issue: Client Certificate Not Accepted **Diagnosis:** Check server logs for certificate validation errors. **Common Causes:** 1. Certificate expired 2. Certificate not in PFX format 3. Private key missing 4. Server not configured for mutual TLS **Solution:** ```csharp // Verify certificate has private key var clientCert = new X509Certificate2("client-cert.pfx", "password"); Console.WriteLine($"Has private key: {clientCert.HasPrivateKey}"); // Check expiration Console.WriteLine($"Valid from: {clientCert.NotBefore}"); Console.WriteLine($"Valid to: {clientCert.NotAfter}"); ``` ### Issue: Slow HTTPS Connections **Diagnosis:** Measure connection time: ```csharp var stopwatch = Stopwatch.StartNew(); using var connection = new HttpServerConnection(config); stopwatch.Stop(); Console.WriteLine($"Connection established in {stopwatch.ElapsedMilliseconds}ms"); ``` **Common Causes:** 1. DNS resolution slow 2. TLS handshake slow 3. Server overloaded **Solution:** - Use connection pooling (reuse connections) - Increase MaxConnectionsPerServer - Cache DNS (use IP address directly) --- ## Configuration Examples ### Development Environment ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://localhost:5051", ApiKey = "dev-api-key", Timeout = TimeSpan.FromSeconds(30), // Accept self-signed cert (development only) ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (errors == SslPolicyErrors.None) return true; // Log warning in development Console.WriteLine($"[DEV] Accepting certificate with errors: {errors}"); return true; } }; ``` ### Staging Environment ```csharp var config = new HttpServerConnectionConfig { ServerUrl = "https://staging-mcp.example.com", ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY_STAGING"), Timeout = TimeSpan.FromSeconds(60), MaxConnectionsPerServer = 10, // Validate cert but allow specific staging cert ServerCertificateCustomValidation = (request, cert, chain, errors) => { if (errors == SslPolicyErrors.None) return true; // Accept specific staging certificate var stagingThumbprint = Environment.GetEnvironmentVariable("STAGING_CERT_THUMBPRINT"); return cert?.Thumbprint == stagingThumbprint; } }; ``` ### Production Environment ```csharp var config = new HttpServerConnectionConfig { ServerUrl = Environment.GetEnvironmentVariable("MCP_SERVER_URL"), ApiKey = await GetApiKeyFromSecretManagerAsync(), Timeout = TimeSpan.FromSeconds(120), MaxConnectionsPerServer = 50, // Use default certificate validation (strict) ServerCertificateCustomValidation = null }; ``` --- ## Security Checklist Before deploying to production: - [ ] Server URL uses HTTPS (`https://...`) - [ ] Certificate validation is enabled (no custom bypass) - [ ] API key is stored securely (environment variable or secrets manager) - [ ] Timeout is configured appropriately for use case - [ ] Error handling is implemented - [ ] Connection is disposed properly (using statement) - [ ] Client certificate (if used) is stored securely - [ ] Certificate expiry monitoring is configured - [ ] Firewall allows outbound HTTPS (port 443) - [ ] TLS 1.2+ is enforced (no SSL3, TLS 1.0, TLS 1.1) --- ## References **OpenHarbor.MCP Documentation:** - [Client README](../../README.md) - [API Reference](../../docs/api/client-api.md) - [Integration Guide](../../docs/integration-guide.md) **.NET Documentation:** - [HttpClient Security](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) - [X509 Certificates](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2) - [SSL/TLS Best Practices](https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls) **Security Resources:** - [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html) - [Certificate Pinning Guide](https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning) --- **Document Version**: 1.0.0 **Last Updated**: 2025-10-19 **Maintained By**: Svrnty Development Team **Related**: [Server HTTPS Setup](../../OpenHarbor.MCP.Server/docs/deployment/https-setup.md)