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 Method | Why Deprecated | Action Required | Timeline |
|---|---|---|---|
| Non-ephemeral FFDH | No forward secrecy; Raccoon attack | Remove support | Immediate |
| Ephemeral FFDHE | Vulnerable to timing attacks and weak DH implementations | Migrate to ECDHE | Immediate |
| RSA key exchange | No forward secrecy; Bleichenbacher attacks | Use ECDHE or ML-DSA | Immediate |
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
Recommended Replacements
| Deprecated Method | Replacement (Classical) | Replacement (Post-Quantum) |
|---|---|---|
| RSA Key Exchange | ECDHE (NIST curves) | ML-DSA (FIPS 204) |
| Non-ephemeral FFDH | ECDHE | ML-DSA |
| Ephemeral FFDHE | ECDHE | ML-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-ciphersto 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)
- Visit https://www.ssllabs.com/ssltest/
- Enter your domain
- 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:
| Phase | Timeline | Action |
|---|---|---|
| Deprecation formal | July 2026 (now) | Start audit, plan migration |
| Vendor support ending | 2027-2028 | Expect libraries to disable by default |
| Compliance mandates | 2027-2028 | PCI-DSS, SOC 2 requirements tighten |
| Forced removal | 2028-2029 | Major 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:
- Audit your TLS configurations now
- Remove RSA, FFDH, and FFDHE cipher suites
- Enable ECDHE for TLS 1.2 or migrate to TLS 1.3
- Test thoroughly before rolling out
- 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:
- RFC 10015 — Official RFC text
- Raccoon Attack (CVE-2020-1968) — Timing side-channel attack on DH
- ROBOT Attack (2017) — Bleichenbacher attacks on modern TLS
- DROWN Attack (2016) — SSLv2 downgrade attacks
- NIST ML-DSA (FIPS 204) — Post-quantum signature standard