📁 Sangam Dhakal - Portfolio v2.0 - Netscape Navigator 4.0
_
□
×

📚 Interview question

How do HTTPS and TLS work? Walk through the TLS 1.3 handshake and certificate validation, and explain how you would fix "The remote certificate is invalid" errors in a .NET service without disabling validation.

TLS goals, the TLS 1.3 handshake, certificate chains and validation, SNI and ALPN, mutual TLS, and safe diagnosis of certificate errors in .NET.

Senior Security Advanced certificates https networking security tls troubleshooting

🎯 Key points

  • TLS provides confidentiality, integrity and server authentication; ephemeral ECDHE key exchange gives forward secrecy, so a stolen server key cannot decrypt old traffic.
  • The TLS 1.3 handshake takes one round trip: SNI selects the certificate, ALPN negotiates h2, and CertificateVerify proves the server holds the private key; 0-RTT resumption is replayable, so only for idempotent requests.
  • Validation checks the chain to a trusted root, validity dates, the hostname against the SAN, key usage, and optionally revocation (OCSP or CRL).
  • Servers must send intermediates; browsers can hide a missing chain by fetching it via AIA, so 'works in the browser' proves little.
  • Diagnose with SslPolicyErrors and X509ChainStatus (UntrustedRoot, PartialChain, NotTimeValid, name mismatch), openssl s_client -showcerts and curl -v.
  • Trust private CAs explicitly (the image trust store, or a per-client CustomTrustStore with CustomRootTrust) and never return true from the validation callback.
  • Mutual TLS adds client certificates so both sides prove identity, which is the standard for zero-trust service-to-service traffic, often handled by a service mesh or gateway.
  • Certificate failures are outages, and retry storms of failed handshakes add CPU load and latency, so automate renewal (ACME, Key Vault, cert-manager), monitor expiry on every hop, and reuse connections to keep handshake cost low.

⏱ 30-second answer

HTTPS is HTTP inside TLS. TLS gives confidentiality (encryption), integrity (tamper detection) and server authentication (certificates).

TLS 1.3 handshake (one round trip): the client sends supported ciphers, a key share, SNI (hostname) and ALPN (h2); the server replies with its key share, certificate chain and a signature proving it owns the private key. Both derive session keys via ECDHE (forward secrecy).

The client validates: chain to a trusted root, not expired, hostname matches the SAN, and optionally revocation.

For "remote certificate is invalid": read the SslPolicyErrors / chain status, inspect with openssl s_client -showcerts, then fix the real cause (expired cert, missing intermediate, wrong hostname, or a private/corporate root CA not trusted in the container). Never return true from ServerCertificateCustomValidationCallback in production.

🗣 Full answer (senior)

TLS 1.3 handshake

Client                                                   Server
  │── ClientHello: versions, ciphers, key_share,          │
  │   SNI=api.example.com, ALPN=[h2, http/1.1] ──────────►│
  │                                                       │
  │◄── ServerHello: chosen cipher, key_share ──────────────│  both sides now derive keys
  │◄── {EncryptedExtensions: ALPN=h2}                    │
  │◄── {Certificate: leaf + intermediates}               │
  │◄── {CertificateVerify: signature with private key}   │
  │◄── {Finished}                                        │
  │   client validates chain, hostname, expiry            │
  │── {Finished} + first HTTP request (encrypted) ─────────►│

TLS 1.2 needed two round trips; TLS 1.3 needs one, and resumption can allow 0-RTT (replay-prone, so only for idempotent requests).

Certificate validation

Check Common failure
Chain builds to a trusted root Server sends only the leaf; private/corporate CA not in the trust store
Validity dates Expired certificate (or wrong clock on the client)
Hostname matches SAN Calling by IP or internal alias not in the certificate
Key usage / EKU Certificate not issued for server authentication
Revocation (OCSP/CRL) Revocation server unreachable from a locked-down network

In .NET you see SslPolicyErrors.RemoteCertificateChainErrors, RemoteCertificateNameMismatch or RemoteCertificateNotAvailable, and X509ChainStatus details like UntrustedRoot, PartialChain or NotTimeValid.

Production example

A service worked on developer laptops but failed in its Linux container with AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot when calling an internal API. The API used a certificate from the company's private CA. Windows laptops trusted that root through Group Policy; the Debian-based container did not. A developer's proposed fix was ServerCertificateCustomValidationCallback = (_, _, _, _) => true, which would have allowed any man-in-the-middle. Instead we added the root CA to the image (COPY corp-root.crt /usr/local/share/ca-certificates/ + update-ca-certificates) and later moved the internal API behind a certificate from a CA trusted everywhere.

🔍 What the interviewer is checking

Checks real understanding of TLS and the discipline to fix certificate issues safely.

Green flags

  • Explains confidentiality, integrity and authentication, and forward secrecy.
  • Describes the TLS 1.3 handshake, SNI and ALPN.
  • Knows the chain of trust and the four main validation checks.
  • Uses openssl/curl and fixes trust properly; rejects disabling validation.

Red flags

  • Suggests accepting any certificate.
  • Thinks the certificate encrypts the data directly.

📚 Learn in depth

1. What each part of TLS protects

Goal Mechanism
Confidentiality Symmetric encryption (AES-GCM, ChaCha20-Poly1305) with session keys
Integrity AEAD tags on each record
Server authentication Certificate chain + signature with the server's private key
Forward secrecy Ephemeral (EC)DHE keys: stealing the server key later does not decrypt old traffic
Client authentication (optional) Mutual TLS: client presents a certificate too

2. The chain of trust

Root CA (in OS trust store, self-signed, long-lived)
   │ signs
   ▼
Intermediate CA (sent by the server)
   │ signs
   ▼
Leaf certificate: CN/SAN = api.example.com, valid 2026-09-01 → 2026-11-30

Servers must send the leaf and intermediates. Some clients fetch missing intermediates via the AIA URL, others do not, so "works in the browser" is not proof.

3. SNI and ALPN

  • SNI: the hostname in ClientHello, so one IP/load balancer can serve many certificates.
  • ALPN: negotiates h2 vs http/1.1 inside the handshake. HTTP/2 over TLS needs it.

4. Where TLS terminates

client ─TLS─► CDN/WAF ─TLS─► load balancer ─(TLS or plain)─► Kestrel

Each hop has its own certificate and expiry date. Know which hop presents which certificate, and monitor all of them.

5. .NET specifics

  • HttpClient uses the OS trust store (Windows certificate store, OpenSSL's store on Linux, Keychain on macOS).
  • SslClientAuthenticationOptions.CertificateChainPolicy (or X509ChainPolicy with CustomTrustStore and TrustMode = CustomRootTrust) lets you trust a specific private root for one client without trusting it machine-wide.
  • Kestrel loads certificates from files, the store, or configuration (Kestrel:Certificates:Default) and can reload them; many teams terminate TLS at the ingress instead.
  • Public certificate lifetimes are getting shorter (90 days for Let's Encrypt today, with the industry moving towards much shorter maximums), so automated renewal is mandatory.

6. Diagnosis toolkit

openssl s_client -connect api.internal:443 -servername api.internal -showcerts </dev/null
openssl x509 -in leaf.pem -noout -subject -issuer -dates -ext subjectAltName
curl -v https://api.internal/healthz       # shows the verify result

7. Try it yourself

  1. Run openssl s_client against a public site and identify leaf, intermediate and root.
  2. Use badssl.com subdomains (expired., wrong.host., untrusted-root., incomplete-chain.) from a .NET console app and print SslPolicyErrors and chain statuses in a logging-only validation callback.
  3. Create a private CA with openssl, issue a server cert, and make a .NET client trust only that root using CustomTrustStore.

💻 Code

// Log the real reason for a TLS failure (diagnostics only: still returns the default decision)
var handler = new SocketsHttpHandler
{
    SslOptions = new SslClientAuthenticationOptions
    {
        RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
        {
            if (errors != SslPolicyErrors.None)
            {
                logger.LogError("TLS validation failed: {Errors}; subject {Subject}; chain {Chain}",
                    errors, cert?.Subject,
                    string.Join(", ", chain?.ChainStatus.Select(s => s.Status) ?? []));
            }
            return errors == SslPolicyErrors.None;   // never 'return true' unconditionally
        }
    }
};
// Trust a specific private root CA for ONE client, without changing the machine trust store
var corpRoot = X509CertificateLoader.LoadCertificateFromFile("/etc/certs/corp-root.crt");
var policy = new X509ChainPolicy
{
    TrustMode = X509ChainTrustMode.CustomRootTrust,
    RevocationMode = X509RevocationMode.NoCheck // set according to your PKI's revocation support
};
policy.CustomTrustStore.Add(corpRoot);

var internalHandler = new SocketsHttpHandler
{
    SslOptions = new SslClientAuthenticationOptions { CertificateChainPolicy = policy }
};
var internalClient = new HttpClient(internalHandler) { BaseAddress = new Uri("https://ledger.corp.internal/") };
# Or trust the corporate root in the container image (affects all clients in the image)
FROM mcr.microsoft.com/dotnet/aspnet:10.0
USER root
COPY corp-root.crt /usr/local/share/ca-certificates/corp-root.crt
RUN update-ca-certificates
USER $APP_UID

⚠ Common mistakes

  • Mistake: "Fixing" certificate errors with ServerCertificateCustomValidationCallback = (...) => true or DangerousAcceptAnyServerCertificateValidator. It turns off authentication, so anyone on the path can impersonate the server and read or change traffic. Instead: find the actual SslPolicyErrors and chain status, then fix the certificate, the chain or the trust store.
  • Mistake: Assuming "it works in my browser" means the server is configured correctly. Browsers cache and fetch missing intermediates; other clients may not. Instead: check with openssl s_client -showcerts that the server sends the full chain.
  • Mistake: Calling services by IP or by an alias not in the certificate. Hostname validation fails with a name mismatch. Instead: use the name in the certificate's SAN, or issue a certificate that includes the names you use.
  • Mistake: Managing certificate renewal by calendar reminder. Certificates expire on a Saturday and take production down. Instead: automate issuance and renewal (ACME, Key Vault, cert-manager) and alert well before expiry on every hop.
  • Mistake: Thinking HTTPS means the data is safe end to end. TLS often terminates at a load balancer or CDN, and the next hop may be plain HTTP. Instead: know where TLS terminates and re-encrypt internal hops where required.

🏛 Senior insight: trade-offs

TLS problems are usually PKI operations problems, not cryptography problems: expiry, incomplete chains, trust stores that differ between environments, and names that do not match. The senior response is to automate certificate lifecycle, monitor expiry on every hop (CDN, gateway, ingress, service), and make trust explicit, ideally per client with CustomTrustStore for private CAs, rather than widening machine trust.

I treat any disabled validation as a security incident in code review, because it silently removes authentication. Where both sides need identity (service-to-service in zero-trust networks), I use mutual TLS, often through a service mesh so applications do not manage certificates themselves. And I remember the performance side: TLS 1.3 and connection reuse keep handshake cost low.

🔗 Follow-ups the interviewer may ask

  1. When: Candidate mentions TLS termination at a proxy Why: Probes what the app sees behind a reverse proxy
    How to answer
    • A forward proxy sits in front of clients (corporate egress proxy): clients send requests through it to the internet. For HTTPS it tunnels with CONNECT. HttpClient picks it up from system settings or HTTP_PROXY/HTTPS_PROXY/NO_PROXY.
    • A reverse proxy sits in front of servers (nginx, Envoy, YARP, App Gateway, ingress): it terminates TLS, routes, load balances, caches and adds headers.

    Behind a reverse proxy, the app sees the proxy's IP and often plain http. The proxy passes the originals in X-Forwarded-For and X-Forwarded-Proto, but ASP.NET Core only applies them via the ForwardedHeaders middleware, and by default only trusts proxies on loopback. So UseHttpsRedirection sees http, redirects to https, the proxy forwards as http again: a loop.

    Fix: UseForwardedHeaders first, configured with the proxy's IPs or network in KnownProxies/known networks. Do not trust all sources, or clients can spoof their IP and scheme.

  2. When: Candidate cannot place TLS in the overall request flow Why: Checks the end-to-end picture
    How to answer
    1. DNS: resolve api.example.com to an IP address (OS cache, then a resolver).
    2. IP routing: packets go from my machine through routers (and usually NAT) to that IP.
    3. TCP: connect to port 443 with a three-way handshake (SYN, SYN-ACK, ACK). One round trip.
    4. TLS: negotiate keys and verify the server's certificate for api.example.com (TLS 1.3: one more round trip).
    5. HTTP: send GET /orders/42 with headers; often a load balancer or reverse proxy terminates TLS and forwards to an app instance.
    6. Response comes back on the same connection, which HttpClient keeps alive and reuses for the next request, skipping steps 1–4.

    On a cold connection, DNS + TCP + TLS can cost more than the request itself, which is why connection reuse matters.

  3. When: Candidate talks about certificates at several hops Why: Moves to designing the full network path
    How to answer

    Layered from outside in:

    1. DNS with low TTL for the public name.
    2. Global edge / CDN + WAF (Front Door, CloudFront + AWS WAF, Cloudflare): anycast entry, TLS termination close to users, caching, DDoS and OWASP rules, rate limiting.
    3. Regional L7 gateway / ingress (App Gateway, ALB, ingress controller): routing, re-encrypted TLS, health probes; only accepts traffic from the edge.
    4. App tier in private subnets (AKS/ECS/App Service with VNet integration): no public IPs, NSGs/security groups and NetworkPolicies limit east-west traffic.
    5. Data tier via private endpoints (SQL, storage, Key Vault) with private DNS zones; public access disabled.
    6. Controlled egress through a NAT gateway or firewall with allow-lists.

    Plus observability at every hop (flow logs, edge logs, traces with traceparent), and automated certificates and network rules as code.

🛠 Real-world problems: how you handle them

UntrustedRoot in Linux containers for an internal API using a private CA
Situation
[Billing service] on .NET [10] moving from Windows VMs to [Linux containers], calling an internal [ledger API] secured with a certificate from the company's private CA.
Signals
`AuthenticationException: The remote certificate is invalid ... UntrustedRoot` on every call from containers; laptops and old VMs worked.
Investigation
Logged `SslPolicyErrors` and chain statuses; `openssl s_client -showcerts` showed a chain ending in the corporate root. Windows machines trusted it through Group Policy; the Debian image did not.
Resolution
Rejected a pull request that disabled validation. Added the corporate root to the image with `update-ca-certificates`; later switched to a per-client `CustomTrustStore` so only the ledger client trusts that root.
Prevention
Base image includes approved internal roots; certificate expiry monitoring for all internal endpoints; code analysis rule flagging custom validation callbacks that return true.

How to tell it (STAR)

**S:** After moving [billing] to Linux containers, every call to the internal [ledger API] failed with a certificate error. **T:** I had to restore it quickly without weakening security. **A:** The chain status said UntrustedRoot, and `openssl` showed our private CA, which Windows trusted via policy but the container did not. I blocked a quick fix that accepted any certificate, added the corporate root to the image, and later scoped trust to just the ledger client. **R:** Calls recovered the same day, and the analyser rule has since stopped [two] attempts to disable validation.

↩ Usually asked after

More in Networking Basics

Last updated 23 Oct 2026.

🌐 Sangam's Webring

⏪ Previous 🏠 Main Next ⏩
💬 Chat with me