RFC 10015: Deprecating Weak TLS Key Exchange Methods — Forward Secrecy Standards

RFC 10015 (published July 2026) formally deprecates three weak TLS key exchange methods across TLS 1.2 and DTLS 1.2. The deprecation targets methods that lack forward secrecy or are vulnerable to known attacks, pushing the industry toward stronger cryptographic practices.

For infrastructure teams, this RFC marks a critical inflection point: legacy TLS configurations must be upgraded, compliance frameworks are tightening, and tools need configuration updates. This guide explains what’s changing, why, and how to audit and migrate your TLS infrastructure.

TL;DR

Deprecated MethodWhy DeprecatedAction RequiredTimeline
Non-ephemeral FFDHNo forward secrecy; Raccoon attackRemove supportImmediate
Ephemeral FFDHEVulnerable to timing attacks and weak DH implementationsMigrate to ECDHEImmediate
RSA key exchangeNo forward secrecy; Bleichenbacher attacksUse ECDHE or ML-DSAImmediate

Bottom line: Remove all three methods from production. Use ECDHE (classical) or ML-DSA (post-quantum) instead.

What RFC 10015 Deprecates and Why

1. Non-Ephemeral Finite Field Diffie-Hellman (FFDH)

What it is: A key exchange where the server’s DH parameters are static (reused across sessions).

Why it’s weak:

  • No forward secrecy: If the server’s private key is compromised, an attacker can decrypt all past sessions using the static DH parameters
  • Raccoon attack (CVE-2020-1968): Timing side-channels in DH implementations allow attackers to recover session keys through cache-timing attacks

Example vulnerable configuration:

# TLS_DHE_RSA_WITH_AES_128_CBC_SHA (cipher suite using non-ephemeral FFDH)
# ❌ Deprecated in RFC 10015

2. Ephemeral Finite Field Diffie-Hellman (FFDHE)

What it is: A key exchange where DH parameters change per-session (ephemeral) but use finite field (non-elliptic curve) math.

Why it’s weak:

  • Parameter reuse attacks: Weak DH implementations can leak information across sessions
  • Timing side-channels: Similar to Raccoon attack, cache-timing can reveal DH secrets
  • Implementation vulnerabilities: FFDH requires careful coding; most implementations have exploitable flaws

Known attacks:

  • Logjam (CVE-2015-4000): Downgrade attacks on weak 512-bit DH groups
  • DLOG795: Computational DH attacks on 768-bit groups
  • SUBGROUPS: Small subgroup attacks on improperly validated DH parameters

Example vulnerable configuration:

# TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (FFDHE-based, ephemeral but still weak)
# ❌ Deprecated in RFC 10015

3. RSA Key Exchange

What it is: A key exchange where the client encrypts a random value with the server’s RSA public key. The server decrypts it to derive the session key.

Why it’s weak:

  • No forward secrecy: Compromising the server’s RSA private key exposes all past sessions
  • Bleichenbacher attack (CVE-1998-0160 and variants): Padding oracle attacks allow attackers to decrypt session keys or forge new ones
    • ROBOT (2017): Practical Bleichenbacher attacks on modern TLS implementations
    • NEW-BLEI (2023): Updated Bleichenbacher variants
    • DROWN (2016): Downgrade attacks on systems supporting both SSLv2 and TLS

Example vulnerable configuration:

# TLS_RSA_WITH_AES_128_CBC_SHA (RSA key exchange)
# ❌ Deprecated in RFC 10015

Why Forward Secrecy Matters

All three deprecated methods lack forward secrecy: if a server’s private key is stolen, an attacker can retroactively decrypt all past sessions.

Forward Secrecy vs. No Forward Secrecy

Without Forward Secrecy (RSA, Non-ephemeral FFDH):

Timeline:
├─ Jan 2024: Client connects, exchanges key with RSA
│  Encrypted session recorded by attacker

├─ Aug 2026: Server compromised, private key stolen
│  (years later!)

├─ Attacker now decrypts Jan 2024 session retroactively
│  (sensitive financial data, medical records, etc. exposed)

└─ User never knew they were vulnerable

With Forward Secrecy (ECDHE, ML-DSA):

Timeline:
├─ Jan 2024: Client connects, derives ephemeral key (ECDHE)
│  Encrypted session recorded by attacker
│  Session key deleted from client & server memory

├─ Aug 2026: Server compromised, private key stolen

├─ Attacker can decrypt NEW sessions using stolen key
│  BUT cannot decrypt Jan 2024 session (ephemeral key is gone)

└─ Jan 2024 session remains private

For compliance and security, all TLS deployments must use forward-secret key exchanges.

Affected Cipher Suites

RFC 10015 provides comprehensive lists of affected cipher suites. Here are examples:

RSA Key Exchange (All Deprecated)

TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA

FFDH (Non-Ephemeral, All Deprecated)

TLS_DH_DSS_WITH_AES_128_CBC_SHA
TLS_DH_RSA_WITH_AES_128_CBC_SHA
TLS_DH_DSS_WITH_AES_256_CBC_SHA
TLS_DH_RSA_WITH_AES_256_CBC_SHA

FFDHE (Ephemeral, All Deprecated)

TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA

What’s Safe to Use Instead

Deprecated MethodReplacement (Classical)Replacement (Post-Quantum)
RSA Key ExchangeECDHE (NIST curves)ML-DSA (FIPS 204)
Non-ephemeral FFDHECDHEML-DSA
Ephemeral FFDHEECDHEML-DSA

Strong Cipher Suites (Go 1.27+, OpenSSL 3.0+)

# Recommended for TLS 1.2
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384

# Recommended for TLS 1.3 (no DH required)
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256

Auditing Your TLS Infrastructure

Step 1: Identify Which Protocols You’re Using

# Using OpenSSL, check your server's supported ciphers
openssl s_client -connect example.com:443 -cipher ALL -tls1_2 2>/dev/null | grep "Cipher"

# Example output (check for deprecated suites)
# Cipher: ECDHE-RSA-AES256-GCM-SHA384  ✅ Safe
# Cipher: DHE-RSA-AES256-GCM-SHA384    ❌ FFDHE (deprecated)
# Cipher: RSA-AES256-GCM-SHA384        ❌ RSA key exchange (deprecated)

Step 2: Audit Go Applications

package main

import (
	"crypto/tls"
	"fmt"
)

func main() {
	// Check default cipher suites (Go 1.27+)
	config := &tls.Config{}
	suites := config.CipherSuites()

	fmt.Println("Supported cipher suites:")
	for _, suite := range suites {
		fmt.Printf("- %s\n", tls.CipherSuiteName(suite.ID))
	}

	// Check if deprecated suites are still available
	// (Should not be)
	deprecatedSuites := []uint16{
		0x0001,  // TLS_RSA_WITH_NULL_MD5
		0x0004,  // TLS_RSA_WITH_RC4_128_MD5
		0x0009,  // TLS_RSA_WITH_DES_CBC_SHA
		0x001B,  // TLS_DH_DSS_WITH_AES_128_CBC_SHA
		0x0020,  // TLS_DHE_DSS_WITH_AES_128_CBC_SHA
		0x0033,  // TLS_DHE_RSA_WITH_AES_128_CBC_SHA
	}

	for _, suite := range deprecatedSuites {
		for _, enabled := range suites {
			if enabled.ID == suite {
				fmt.Printf("⚠️  DEPRECATED SUITE FOUND: %s\n", tls.CipherSuiteName(suite))
			}
		}
	}
}

Step 3: Audit Nginx

# Check your nginx.conf
ssl_protocols TLSv1.2 TLSv1.3;

# Check ciphers (list only safe suites)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';

# Disable deprecated suites explicitly
ssl_ciphers '!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!DHE:!RSA';

Step 4: Audit Java Applications

// Check Java SSL configuration
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
SSLSocketFactory factory = sslContext.getSocketFactory();

// Java's default settings vary by version
// Check java.security for TLS configuration
// Print enabled protocols
System.out.println("Enabled protocols: " + Arrays.toString(factory.getSupportedProtocols()));

// Recommended configuration (Java 11+)
System.setProperty("jdk.tls.client.protocols", "TLSv1.2,TLSv1.3");
System.setProperty("jdk.tls.disabledAlgorithms", 
    "SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, DH, DSS, RSA");

Migration Checklist

For Development Teams

  • Review all TLS configurations (nginx, Apache, HAProxy, etc.)
  • Remove RSA key exchange from all cipher suite lists
  • Remove FFDH and FFDHE from all cipher suite lists
  • Ensure ECDHE is enabled for all TLS 1.2 deployments
  • Upgrade TLS libraries to latest versions (OpenSSL 3.0+, Go 1.27+, etc.)
  • Test with tools like nmap --script ssl-enum-ciphers to verify
  • Update internal TLS documentation

For Infrastructure Teams

  • Audit load balancers (AWS ELB, Azure LB, etc.)

    # AWS: Check security group TLS policies
    aws elbv2 describe-ssl-policies --region us-east-1 | jq '.SslPolicies[] | select(.SslProtocols | contains(["TLSv1.2"]))'
    
  • Update WAF rules to reject deprecated cipher suites

  • Configure TLS monitoring to detect deprecated suite negotiation

  • Plan gradual rollout: monitor, test, deploy, verify

For Security/Compliance Teams

  • Update security baselines to require ECDHE or ML-DSA
  • Document deprecation timeline for audit logs
  • Update compliance mappings (SOC 2, PCI-DSS, etc.)
  • Create incident response plan for systems still using deprecated suites

Real-World Example: Migrating a Microservice

Before (Go 1.26 - Vulnerable):

package main

import (
	"crypto/tls"
	"net/http"
	"log"
)

func main() {
	// Go 1.26 defaults include FFDHE and other deprecated suites
	// (depending on system OpenSSL)
	server := &http.Server{
		Addr:    ":8443",
		Handler: http.HandlerFunc(handleRequest),
		// TLSConfig not specified — uses Go defaults
		// ⚠️ May include deprecated cipher suites
	}

	log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello"))
}

After (Go 1.27 - Compliant):

package main

import (
	"crypto/tls"
	"net/http"
	"log"
)

func main() {
	// Explicitly configure safe cipher suites
	tlsConfig := &tls.Config{
		// Only allow TLS 1.2+
		MinVersion: tls.VersionTLS12,
		MaxVersion: tls.VersionTLS13,

		// Explicitly set strong cipher suites (no RSA, FFDH, FFDHE)
		CipherSuites: []uint16{
			// TLS 1.3 suites (checked first)
			tls.TLS_AES_128_GCM_SHA256,
			tls.TLS_AES_256_GCM_SHA384,
			tls.TLS_CHACHA20_POLY1305_SHA256,

			// TLS 1.2 ECDHE suites (ephemeral, forward-secret)
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		},

		// Require forward secrecy
		PreferServerCipherSuites: true,

		// Modern curves only
		CurvePreferences: []tls.CurveID{
			tls.CurveP256,
			tls.CurveP384,
		},
	}

	server := &http.Server{
		Addr:      ":8443",
		Handler:   http.HandlerFunc(handleRequest),
		TLSConfig: tlsConfig,
	}

	log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
	// Verify TLS version in request
	if r.TLS.Version == tls.VersionTLS13 {
		w.Header().Set("X-TLS-Version", "1.3")
	} else if r.TLS.Version == tls.VersionTLS12 {
		w.Header().Set("X-TLS-Version", "1.2")
	}
	w.Write([]byte("Hello from secure TLS"))
}

Testing Your Changes

Using testssl.sh

# Download and run comprehensive TLS audit
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
./testssl.sh/testssl.sh --full https://example.com

# Look for:
# ✅ ECDHE suites enabled
# ✅ Forward secrecy supported
# ❌ No RSA key exchange
# ❌ No FFDH/FFDHE

Using ssllabs.com (External)

  1. Visit https://www.ssllabs.com/ssltest/
  2. Enter your domain
  3. Check for:
    • “Key Exchange”: Should show “ECDHE”
    • “Forward Secrecy”: Should show “Yes”
    • “Weak ciphers”: Should be empty

Automated Testing in CI/CD

#!/bin/bash
# tls-compliance-check.sh

SERVER="$1"
INSECURE_SUITES=(
    "RSA"           # RSA key exchange
    "DHE"           # FFDHE
    "DH_"           # FFDH
    "anon"          # Anonymous (no forward secrecy)
)

echo "Checking TLS compliance for $SERVER"

for suite in "${INSECURE_SUITES[@]}"; do
    if openssl s_client -connect "$SERVER:443" -cipher "$suite" 2>/dev/null | grep -q "Cipher"; then
        echo "❌ FOUND DEPRECATED SUITE: $suite"
        exit 1
    fi
done

echo "✅ TLS compliance check passed"
exit 0

Timeline and Compliance

RFC 10015 Publication (July 2026)

Deprecation is now formal. Industry adoption timeline:

PhaseTimelineAction
Deprecation formalJuly 2026 (now)Start audit, plan migration
Vendor support ending2027-2028Expect libraries to disable by default
Compliance mandates2027-2028PCI-DSS, SOC 2 requirements tighten
Forced removal2028-2029Major browsers/servers disable

Recommendation: Complete migration by end of 2027 to avoid compliance violations.

Common Questions

Q: My system still uses RSA for other purposes (signing). Does RFC 10015 affect this?

A: No. RFC 10015 targets only RSA as a TLS key exchange method, not RSA signatures or certificates. You can keep RSA certificates; just use ECDHE key exchange with them.

Q: Do I need to support ML-DSA now?

A: No, it’s optional. Classical ECDHE is sufficient for compliance. ML-DSA is recommended for future-proofing against quantum threats.

Q: What about TLS 1.3? Is it affected?

A: TLS 1.3 doesn’t support RSA key exchange or FFDH at all, so it’s already compliant. Only TLS 1.2 and DTLS 1.2 are affected.

Q: How do I know if clients will break after migration?

A: Monitor connections rejected for cipher suite negotiation mismatch. Most modern clients (browsers, libraries) already prefer ECDHE; legacy clients are rare.

Conclusion

RFC 10015 formalizes what security experts have known for years: RSA and FFDH/FFDHE key exchanges are cryptographically broken. The industry is moving to ECDHE and post-quantum alternatives.

Action items:

  1. Audit your TLS configurations now
  2. Remove RSA, FFDH, and FFDHE cipher suites
  3. Enable ECDHE for TLS 1.2 or migrate to TLS 1.3
  4. Test thoroughly before rolling out
  5. Monitor for compliance violations

By end of 2027, RFC 10015 compliance will be table stakes. Teams that move early gain confidence and reduce risk of last-minute pressure.


Sources: