Recently, I was reviewing evidence for a control assessment. The control in question was ISM-0472:
When using DH for agreeing on encryption session keys, a modulus of at least 2048 bits is used, preferably 3072 bits.
The evidence I’d been handed was a screenshot of a server’s TLS certificate, showing a 2048-bit RSA public key, similar to the one below:
At first glance it might look right, but it wasn’t evidence for ISM-0472 at all. Both things involve the word “modulus” and the number 2048, which is why this mix-up is so easy to make until you’ve sat down and pulled the two apart. So let’s pull them apart.
Two different jobs, two different keys
A TLS handshake actually needs to do two separate jobs:
1. Prove identity. The server needs to convince you it really is who it says it is. This is what the TLS certificate, like the screenshot above, is for. It contains a public key (often RSA, sometimes EC) that the server uses to sign parts of the handshake, proving it holds the matching private key.
2. Agree on a session key. Separately, the client and server need to agree on a fresh secret to encrypt the actual traffic for the session. If they’re using Diffie-Hellman (DH) to do this, that process involves its own key material, also known as a “temp key” or ephemeral key. Crucially, this has nothing to do with the certificate.
These are two different keys, generated for two different purposes. Importantly, there is no relationship between their sizes. A server can hand you a 4096-bit certificate for authentication and still negotiate a 1024-bit DH group for the session key.
That mismatch is, in fact, close to the basis of a real vulnerability class (Logjam, 2015), where servers supporting legacy export-grade DHE ciphers could be forced down to a 512-bit DH group, and where widespread reuse of the same standard primes meant one precomputation could compromise many servers at once. A strong certificate never told you any of that.
So the screenshot showed the RSA modulus of the certificate’s public key. Good evidence of authentication strength, but ISM-0472 is asking about the modulus used to derive the session key. In other words, the key used to encrypt the session traffic between the server and the client. Same word, same number, completely different thing. The evidence didn’t touch the control it was meant to support.
What “modulus” actually refers to in each case
It’s worth being precise here about what the term “modulus” actually means.
Certificate / RSA modulus: this is the large number n that forms part of an RSA public key (n = p × q, two large primes). Its bit-length is what people mean when they say “2048-bit certificate” or “2048-bit key.” It’s baked into the cert and doesn’t change per session.
DH modulus: this is the large prime p that defines the finite field the Diffie-Hellman exchange operates over. When DH is ephemeral (DHE), this prime is chosen or negotiated fresh, often for each session, and it's this value, not anything in the certificate, that ISM-0472 is actually asking about.
Seeing the difference yourself
The cleanest way to internalise this is to force a handshake and look at both values side by side. You’ll need a Linux box with OpenSSL to follow along. This is entirely self-contained, so no need to go hunting for a public server that still does classic DHE (more on why that’s hard, shortly).
1. Generate a cert and a DH group, then start a test server:
# The following command generates a self-signed cert with a 2048-bit RSA key.
# This is the “authentication” key.
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \
-days 7 -nodes -subj "/CN=test"
# The following command generates a 2048-bit DH parameter file.
# This defines the DH modulus.
openssl dhparam -out dhparam2048.pem 2048
# The following starts a bare TLS server, forcing TLS 1.2 + DHE so nothing
# negotiates around it.
openssl s_server -accept 4433 -cert cert.pem -key key.pem \
-dhparam dhparam2048.pem -no_tls1_3 -cipher 'DHE'2. In a second terminal, connect and inspect both values:
openssl s_client -connect localhost:4433 -tls1_2 -cipher 'DHE' \
< /dev/null 2>/dev/null | grep -E "Temp Key|Server public key"You’ll see something like:
Peer Temp Key: DH, 2048 bits
Server public key is 2048 bitTwo lines, two different keys, coincidentally matching. Now change just one of them and regenerate the DH params at 1024 bits and point the server at that file instead, and reconnect:
# The following command generates a 1024-bit DH parameter file.
openssl dhparam -out dhparam1024.pem 1024
# The following command restarts the TLS server
# but with the 1024-bit DH parameter file.
openssl s_server -accept 4433 -cert cert.pem -key key.pem \
-dhparam dhparam1024.pem -no_tls1_3 -cipher 'DHE:@SECLEVEL=0'
# Note that you will need to force the weak key with `-cipher 'DHE:@SECLEVEL=0'`Back in the other terminal (e.g., the “client side”), re-run the query:
openssl s_client -connect localhost:4433 -tls1_2 -cipher 'DHE:@SECLEVEL=0' \
< /dev/null 2>/dev/null | grep -E "Temp Key|Server public key"
# Note you need to force the weak cipher here too.You will see:
Peer Temp Key: DH, 1024 bits
Server public key is 2048 bitSame certificate. Same authentication strength. But the session key is now being negotiated over a modulus half the size. This demonstrates the gap the original evidence missed; nothing about the cert tells you what’s happening on that second line.
Where TLS 1.3 and ECDHE fit in
If you try to reproduce this against a real-world site instead of your own test server, you’ll almost certainly fail to find classic DH at all, and that’s the scenario worth paying attention to.
Most modern servers prefer elliptic-curve Diffie-Hellman (ECDHE) over classic finite-field DHE. It’s the same idea in terms of negotiating an ephemeral key exchange for the session, but the mathematics runs on an elliptic curve rather than a large prime field. Instead of a “modulus,” you’ll see something like:
Server Temp Key: X25519, 253 bitsThat 253-bit figure isn’t directly comparable to a DH bit-length because elliptic curve math is far more efficient per bit. X25519 at 253 bits offers strength roughly on par with a 3000+ bit finite-field DH group. It’s arguably better than what ISM-0472 asks for. But it’s also a different mechanism entirely, so strictly speaking it falls outside ISM-0472’s scope (there’s likely a separate control for elliptic-curve parameters instead).
There’s a catch though: a server can support both. Under TLS 1.2, a server’s configuration can list ECDHE cipher suites and older DHE cipher suites side by side.
A default scan of the handshake will show ECDHE negotiated, because it’s preferred. But that doesn’t mean DHE isn’t still sitting in the config underneath, pointing at whatever DH parameters were set up years ago and never revisited. If nothing modern ever picks that path, nobody ever notices it as potentially weak. An assessor who only captures the default handshake and sees ECDHE could reasonably, but wrongly, conclude there’s no DH modulus to worry about at all.
You can prove this to yourself with the same test server, configured to allow both:
# Reuse the earlier cert, but point at the 2048-bit DH param file this time.
# Note we are using the 2048-bit DH param here instead
# of a 1024-bit to avoid the SECLEVEL quirk.
# Start the server allowing BOTH ECDHE and DHE cipher suites:
# no restriction to DHE only
openssl s_server -accept 4433 -cert cert.pem -key key.pem \
-dhparam dhparam2048.pem -no_tls1_3 -cipher 'ECDHE:DHE:@SECLEVEL=1'Connect with an unrestricted client first. This is what a routine scan captures by default:
openssl s_client -connect localhost:4433 -tls1_2 < /dev/null 2>/dev/null \
| grep "Temp Key"Peer Temp Key: X25519, 253 bitsLooks fine. Nothing to flag. Now force DHE specifically, to check what’s sitting underneath:
openssl s_client -connect localhost:4433 -tls1_2 -cipher 'DHE' \
< /dev/null 2>/dev/null | grep "Temp Key"Peer Temp Key: DH, 2048 bitsSame server, same configuration, but two entirely different results depending only on which cipher suite gets negotiated. The first connection alone would have signed off on ISM-0472 as not applicable, but the second demonstrates that it does apply.
What about TLS 1.3?
TLS 1.3 removes this risk entirely, which is worth knowing about because it changed how DH is allowed to work. A TLS 1.2 server could hand the client any arbitrary DH prime it liked, which is how weak, sometimes reused, primes ended up in production undetected in the first place. TLS 1.3 only permits DH via a small set of pre-vetted groups, all 2048 bits or larger, so weaker moduli are architecturally impossible under TLS 1.3.
If a TLS 1.3 handshake negotiates DH at all, it’s already 0472-compliant by construction. The catch is that almost nobody implements it, as most stacks only wire up ECDHE for TLS 1.3, so a TLS 1.3 connection using classic DH at all is rare in practice. It’s TLS 1.2, still very much alive for backward compatibility, where the real risk sits.
The practical takeaway for an assessment
Put together, this gives you a decision path for actually testing this control properly:
Enumerate everything the server supports, not just what a modern client negotiates by default. A tool like
nmap —script ssl-enum-cipherswill show you the full cipher suite list, including any DHE suites that exist purely for backward compatibility but never get chosen in practice.If no DHE suites exist at all and the server only does ECDHE, then ISM-0472 doesn’t apply to the system; there’s no DH modulus in play anywhere.
If DHE suites are present alongside ECDHE, don’t assume the default handshake tells the whole story. Force DHE explicitly (as shown above) and check what modulus size it actually falls back to. This is precisely the scenario just demonstrated. ECDHE is shown by default, but a DH group is still configured and reachable underneath.
Even a “pass” here can come with a stronger recommendation. If DHE is present and checks out at 2048-bit or higher, that's technically compliant as far as ISM-0472 is concerned. But if every real client already supports ECDHE, nothing is actually relying on that DHE fallback, so the tighter finding is often “disable DHE entirely” rather than “leave it, it's compliant.”
None of this is really about DH being unusual or obscure. It's a reminder that “a strong key is present” and “the specific key this control asks about is strong” are two different claims, and it's worth knowing which one your evidence is actually making.


