Skip to main content

mTLS Client Certificates

Many high-security APIs require mutual TLS (mTLS) authentication where both the client and server present certificates to verify identity. KnoxCall supports storing client certificates as secrets and automatically using them for API requests.

What is mTLS?

Standard TLS (HTTPS):
Client → Server: "Hello"
Server → Client: "Here's my certificate"
Client: Verifies server certificate ✅
Connection established
Mutual TLS (mTLS):
Client → Server: "Hello"
Server → Client: "Here's my certificate"
Client: Verifies server certificate ✅
Server → Client: "Now show me YOUR certificate"
Client → Server: "Here's my client certificate"
Server: Verifies client certificate ✅
Connection established

When to Use mTLS

Common use cases:
  • 🏦 Banking APIs (PSD2, Open Banking)
  • 🏛️ Government services (IRS, HMRC, tax authorities)
  • 🏢 Enterprise B2B APIs (high-security partners)
  • 💳 Payment processors (some card networks)
  • 🔐 Zero-trust architectures (service mesh, internal APIs)
Example providers requiring mTLS:
  • Plaid (some endpoints)
  • TrueLayer (Open Banking)
  • Yodlee
  • UK HMRC APIs
  • German Tax Authority APIs
  • Some AWS services

Certificate Formats

Supported Formats

KnoxCall accepts certificates in PEM format (text-based):
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKJ5cqZ3xj8VMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
... (more lines) ...
-----END CERTIFICATE-----

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1+fWIcPm15j9zB/2cdY9SZ4O4nM4F4SfbmA9aM8jl3qv8E
... (more lines) ...
-----END PRIVATE KEY-----

Certificate Components

A complete mTLS setup includes:
  1. Client Certificate (cert.pem)
    • Proves your identity to the server
    • Signed by a Certificate Authority (CA)
    • Contains public key
  2. Private Key (key.pem)
    • Paired with certificate
    • Kept secret (never share!)
    • Used to sign requests
  3. Passphrase (optional)
    • Protects the private key
    • Required if key is encrypted
    • Stored encrypted in KnoxCall
  4. CA Certificate (optional)
    • Verifies server’s certificate
    • Usually not needed (uses system trust store)

Format Conversion

If your certificate is in a different format: From PKCS#12 (.pfx, .p12) to PEM:
# Extract certificate
openssl pkcs12 -in cert.pfx -clcerts -nokeys -out cert.pem

# Extract private key
openssl pkcs12 -in cert.pfx -nocerts -out key.pem

# Remove passphrase (optional)
openssl rsa -in key.pem -out key-nopass.pem
From DER to PEM:
# Convert certificate
openssl x509 -inform DER -in cert.der -out cert.pem

# Convert key
openssl rsa -inform DER -in key.der -out key.pem
Combine certificate and key:
cat cert.pem key.pem > combined.pem

Creating a Certificate Secret

Step 1: Prepare Your Certificate

Combine your certificate and private key into a single file:
# combined.pem
-----BEGIN CERTIFICATE-----
... your certificate ...
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
... your private key ...
-----END PRIVATE KEY-----
Important:
  • Certificate must come first
  • Private key must come second
  • Include the -----BEGIN/END----- markers
  • No extra spaces or characters

Step 2: Create Secret in KnoxCall

  1. Navigate to SecretsCreate Secret
  2. Fill in details:
Name: bank_api_certificate
Type: Certificate (mTLS)
Description: Client certificate for Bank XYZ API
  1. Upload certificate:
    • Paste combined PEM content (cert + key)
    • Or upload .pem file
  2. Optional: Add passphrase
    • If your private key is encrypted
    • Enter passphrase (stored encrypted)
  3. Click Create

Step 3: Verify Certificate

After creation, KnoxCall shows:
✅ Certificate validated
📜 Subject: CN=YourCompany, O=YourOrg, C=US
📅 Valid from: 2024-01-01
📅 Expires: 2026-01-01
🔑 Private key: Present
🔐 Passphrase: Yes (encrypted)

Using Certificates in Routes

Basic Usage

Reference the certificate in your route’s advanced configuration: Route Configuration:
{
  "certificate_secret_id": "{{secret:bank_api_certificate}}"
}
KnoxCall automatically:
  • Loads the certificate and private key
  • Decrypts the passphrase if present
  • Creates mTLS agent for the request
  • Presents certificate during TLS handshake

With OAuth2

Some APIs require both OAuth2 and mTLS:
{
  "oauth2_secret_id": "{{secret:bank_oauth}}",
  "certificate_secret_id": "{{secret:bank_api_certificate}}"
}
Flow:
  1. KnoxCall performs OAuth2 token exchange (with mTLS if provider requires it)
  2. Subsequent API requests use both OAuth2 token and mTLS certificate

Environment-Specific Certificates

Use different certificates for production vs sandbox: Create certificate secret:
Secret: bank_api_certificate

Environments:
  production:
    cert: -----BEGIN CERTIFICATE----- (prod cert)
    key: -----BEGIN PRIVATE KEY----- (prod key)
    passphrase: prod-passphrase

  sandbox:
    cert: -----BEGIN CERTIFICATE----- (sandbox cert)
    key: -----BEGIN PRIVATE KEY----- (sandbox key)
    passphrase: sandbox-passphrase
Route configuration (same for all environments):
{
  "certificate_secret_id": "{{secret:bank_api_certificate}}"
}
  • Production route → uses production certificate
  • Sandbox route → uses sandbox certificate
  • Hard fail if environment missing

Real-World Examples

Example 1: Open Banking (TrueLayer)

Requirement: PSD2-compliant Open Banking requires eIDAS certificates Setup:
# You receive from CA:
- transport_cert.pem (eIDAS transport certificate)
- transport_key.pem (private key)

# Combine:
cat transport_cert.pem transport_key.pem > truelayer_mtls.pem
Create secret:
Name: truelayer_mtls_cert
Type: Certificate
Content: (paste truelayer_mtls.pem)
Route config:
{
  "target_url": "https://api.truelayer.com",
  "certificate_secret_id": "{{secret:truelayer_mtls_cert}}",
  "oauth2_secret_id": "{{secret:truelayer_oauth}}"
}

Example 2: Bank XYZ Direct API

Requirement: Corporate banking API with mTLS only (no OAuth2) Create secret:
Name: bankxyz_client_cert
Type: Certificate
Content:
  -----BEGIN CERTIFICATE-----
  ... (client cert) ...
  -----END CERTIFICATE-----
  -----BEGIN ENCRYPTED PRIVATE KEY-----
  ... (encrypted key) ...
  -----END ENCRYPTED PRIVATE KEY-----
Passphrase: your-key-passphrase
Route config:
{
  "target_url": "https://api.bankxyz.com",
  "certificate_secret_id": "{{secret:bankxyz_client_cert}}",
  "headers": {
    "API-Key": "{{secret:bankxyz_api_key}}"
  }
}

Example 3: Government Tax API

Requirement: HMRC API requires mTLS + OAuth2 + API key Secrets:
1. hmrc_mtls_cert (Certificate type)
2. hmrc_oauth (OAuth2 type)
3. hmrc_vendor_key (String type)
Route config:
{
  "target_url": "https://api.service.hmrc.gov.uk",
  "certificate_secret_id": "{{secret:hmrc_mtls_cert}}",
  "oauth2_secret_id": "{{secret:hmrc_oauth}}",
  "headers": {
    "Gov-Vendor-Version": "1.0",
    "Gov-Vendor-Public-Key": "{{secret:hmrc_vendor_key}}"
  }
}

Security Best Practices

✅ Do This

  1. Use passphrase-protected keys
    • Extra layer of security
    • Prevents key theft if certificate leaked
  2. Set certificate expiry reminders
    • Certificates expire (typically 1-2 years)
    • Set calendar reminder 30 days before expiry
  3. Store backups securely
    • Keep original .pfx or .p12 file in secure vault
    • Don’t email certificates
  4. Use environment-specific certificates
    • Separate certificates for prod/sandbox
    • Prevents accidental production API usage in dev
  5. Revoke old certificates
    • When renewing, revoke old certificate with CA
    • Remove from KnoxCall after grace period

❌ Avoid This

  1. Don’t share private keys
    • Each system should have unique certificate
    • Never email or commit to git
  2. Don’t use unencrypted keys
    • Always use passphrase-protected keys
    • Even if “just for testing”
  3. Don’t ignore expiry warnings
    • Expired certificate = API downtime
    • Plan renewal 60 days ahead
  4. Don’t hardcode passphrases
    • Use KnoxCall’s encrypted passphrase field
    • Not environment variables

Certificate Management

Checking Expiry

View expiry in secret detail page:
Certificate: bank_api_certificate
├─ Subject: CN=YourCompany
├─ Issuer: CN=Certificate Authority
├─ Valid from: 2024-01-01 00:00:00 UTC
├─ Expires: 2026-01-01 00:00:00 UTC
├─ Days remaining: 347
└─ Status: ✅ Valid
Expiry warnings:
  • 90 days: 🟡 Warning (plan renewal)
  • 30 days: 🟠 Urgent (renew now)
  • Expired: 🔴 Invalid (API calls will fail)

Renewing Certificates

Process:
  1. Obtain new certificate from CA
  2. Create new secret with different name:
    Old: bank_api_certificate
    New: bank_api_certificate_2025
    
  3. Test in sandbox first
  4. Update routes to use new secret:
    {
      "certificate_secret_id": "{{secret:bank_api_certificate_2025}}"
    }
    
  5. Deploy to production
  6. Monitor for 48 hours
  7. Delete old secret after grace period
Zero-downtime renewal:
  • Old certificate valid until expiry
  • New certificate active before expiry
  • Update routes during low-traffic period

Rotating Certificates

When to rotate:
  • Scheduled renewal (before expiry)
  • Security incident (suspected compromise)
  • Compliance requirement (quarterly rotation)
  • Organization change (company rename)
Rotation checklist:
  • Generate new CSR (Certificate Signing Request)
  • Submit to CA
  • Receive and validate new certificate
  • Upload to KnoxCall as new secret
  • Test in sandbox environment
  • Update production routes
  • Monitor API calls for 48 hours
  • Revoke old certificate with CA
  • Delete old secret from KnoxCall

Troubleshooting

”Certificate validation failed”

Problem: KnoxCall rejects your certificate during upload Common causes:
  1. Incorrect format
    ❌ Wrong: binary .pfx file
    ✅ Right: PEM text format
    
  2. Missing private key
    ❌ Wrong: only certificate, no key
    ✅ Right: certificate + private key
    
  3. Mismatched cert and key
    Cert and key don't form a pair
    → Ensure they were generated together
    
  4. Expired certificate
    Certificate already expired
    → Check expiry date before upload
    
Solution:
# Verify certificate
openssl x509 -in cert.pem -text -noout

# Verify key
openssl rsa -in key.pem -check

# Verify they match
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
# (hashes should match)

“mTLS handshake failed”

Problem: API request fails with TLS error Possible causes:
  1. Wrong certificate uploaded
    • Production cert used in sandbox (or vice versa)
    • Solution: Check environment-specific values
  2. Passphrase incorrect
    • Private key is encrypted but passphrase wrong
    • Solution: Re-upload with correct passphrase
  3. Server doesn’t trust your certificate
    • Certificate not from approved CA
    • Solution: Obtain certificate from API provider’s required CA
  4. Certificate expired
    • Check expiry in secret detail page
    • Solution: Renew certificate
  5. Hostname mismatch
    • Certificate’s CN doesn’t match request hostname
    • Solution: Obtain cert with correct subject name
Debug steps:
# Test mTLS locally
curl --cert cert.pem \
     --key key.pem \
     https://api.example.com/test

# If it works locally but fails in KnoxCall:
# → Check passphrase, environment config

“Cannot decrypt private key”

Problem: KnoxCall can’t load your certificate for requests Causes:
  1. Passphrase wrong or missing
    • Encrypted key needs passphrase
    • Solution: Update secret with correct passphrase
  2. Corrupted key
    • Key data damaged during upload
    • Solution: Re-upload certificate
  3. Unsupported encryption
    • Very old key encryption algorithms
    • Solution: Re-encrypt key with modern algorithm:
      openssl rsa -in old_key.pem -out new_key.pem
      

“Private key not found in secret”

Problem: Certificate uploaded but key missing Solution: Ensure your PEM file contains both:
-----BEGIN CERTIFICATE-----
... certificate ...
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----  ← Must be present!
... private key ...
-----END PRIVATE KEY-----
If you have them separately:
cat cert.pem key.pem > combined.pem
# Then upload combined.pem

Advanced Configuration

Custom CA Certificates

If the API server uses a custom CA:
{
  "certificate_secret_id": "{{secret:client_cert}}",
  "ca_bundle_secret_id": "{{secret:custom_ca_bundle}}"
}
Create custom_ca_bundle secret (String type):
-----BEGIN CERTIFICATE-----
... CA certificate ...
-----END CERTIFICATE-----

SNI (Server Name Indication)

For servers requiring specific SNI:
{
  "certificate_secret_id": "{{secret:client_cert}}",
  "tls_servername": "api.specific-domain.com"
}
For testing only (never in production):
{
  "certificate_secret_id": "{{secret:client_cert}}",
  "tls_reject_unauthorized": false
}
⚠️ Warning: This disables server certificate validation and makes you vulnerable to man-in-the-middle attacks. Only use in isolated test environments.

Compliance & Auditing

Audit Trail

All certificate operations are logged:
SELECT
  operation, -- 'create', 'update', 'delete', 'access'
  secret_id,
  environment_name,
  performed_by,
  performed_at,
  ip_address
FROM secret_audit_log
WHERE secret_type = 'certificate'
ORDER BY performed_at DESC;

Certificate Access Logs

Every time a certificate is loaded for a request:
SELECT
  route_id,
  environment_name,
  certificate_secret_id,
  accessed_at,
  client_ip
FROM route_request_log
WHERE certificate_used = true;

Compliance Requirements

PCI DSS:
  • ✅ Certificates encrypted at rest
  • ✅ Private keys never logged
  • ✅ Access audit trail
  • ✅ Expiry monitoring
SOC 2:
  • ✅ Certificate lifecycle management
  • ✅ Change tracking
  • ✅ Access controls
  • ✅ Secure deletion
PSD2 (Open Banking):
  • ✅ eIDAS certificate support
  • ✅ Qualified signature certificates
  • ✅ Environment segregation

API Reference

Create Certificate Secret

POST /admin/secrets

{
  "name": "client_certificate",
  "secret_type": "certificate",
  "certificate_pem": "-----BEGIN CERTIFICATE-----...",
  "private_key_pem": "-----BEGIN PRIVATE KEY-----...",
  "passphrase": "optional-key-passphrase"
}

Update Certificate (Environment-Specific)

PUT /admin/secrets/{id}/environments/{environment}

{
  "certificate_pem": "-----BEGIN CERTIFICATE-----...",
  "private_key_pem": "-----BEGIN PRIVATE KEY-----...",
  "passphrase": "optional-passphrase"
}

Get Certificate Info (values never returned)

GET /admin/secrets/{id}

Response:
{
  "id": "sec_abc123",
  "name": "client_certificate",
  "secret_type": "certificate",
  "metadata": {
    "subject": "CN=YourCompany, O=YourOrg, C=US",
    "issuer": "CN=Certificate Authority",
    "valid_from": "2024-01-01T00:00:00Z",
    "valid_until": "2026-01-01T00:00:00Z",
    "days_remaining": 347,
    "has_private_key": true,
    "has_passphrase": true
  }
}

Next Steps

OAuth2 + mTLS

Combine OAuth2 with mTLS authentication

Secret Management

Learn about secret types and management

Environment Secrets

Use different certificates per environment

Certificate Rotation

Best practices for certificate renewal

📊 Guide Info

  • Level: Advanced
  • Time: 20 minutes
  • Prerequisites: Understanding of PKI and TLS

🏷️ Tags

mtls, certificates, security, pki, banking, authentication