Overview
TLS configuration often works perfectly until an administrator tightens the cipher configuration.
Perhaps an old cipher is removed from a reverse proxy, web server, load balancer, or application server. Modern browsers continue working, but an older monitoring agent, Java application, API client, or network appliance suddenly fails to connect.
The server logs may contain an error such as:
no shared cipher
or:
handshake failure
The openssl ciphers command is one of the best tools for investigating that problem.
It can show:
- which cipher suites OpenSSL recognizes;
- which cipher suites are actually supported under the current security policy;
- which TLS 1.2 cipher suites match a proposed configuration;
- which TLS 1.3 cipher suites are enabled;
- the order in which cipher suites are preferred;
- each cipher suite’s key exchange, authentication, encryption, and MAC;
- the official IANA cipher-suite name;
- the hexadecimal cipher-suite identifier.
This tutorial uses a practical system-administration scenario:
After hardening the TLS configuration on an internal application server, a legacy monitoring client can no longer connect. Modern clients work normally. We need to determine whether the failure is caused by a cipher-suite mismatch and decide whether the client or server configuration should change.
We will use openssl ciphers and openssl s_client to identify the problem without weakening TLS blindly.
What does openssl ciphers do?
The basic command is:
openssl ciphers
The OpenSSL project describes openssl ciphers as the SSL cipher display and cipher-list command. It converts OpenSSL cipher strings into an ordered cipher preference list. See the official OpenSSL 3.5 ciphers documentation.
The most useful form for administrators is usually:
openssl ciphers -v
This produces detailed information for each matching cipher suite.
Another particularly useful option is:
openssl ciphers -s
The -s option limits the output to cipher suites consistent with the current security level and configured protocol range, making the result closer to what an actual OpenSSL application can use.
OpenSSL version compatibility
This guide covers:
- OpenSSL 3.0;
- OpenSSL 3.1;
- OpenSSL 3.2;
- OpenSSL 3.3;
- OpenSSL 3.4;
- OpenSSL 3.5 LTS;
- OpenSSL 4.0.
Check your installed version:
openssl version
For build and configuration information:
openssl version -a
The core workflow remains consistent across OpenSSL 3.0 through OpenSSL 4.0:
openssl ciphers
openssl ciphers -v
openssl ciphers -s
openssl ciphers -V
openssl ciphers 'HIGH:!aNULL'
However, several version differences matter.
OpenSSL 3.0 and 3.1
When working with TLS 1.2-and-earlier cipher lists, use traditional OpenSSL cipher names for the broadest compatibility.
For example:
ECDHE-RSA-AES256-GCM-SHA384
rather than:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
OpenSSL 3.2 and later
Support for using standard IANA cipher-suite names directly in cipher lists was added in OpenSSL 3.2.
That means newer releases can understand names such as:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
in places where OpenSSL traditionally used:
ECDHE-RSA-AES256-GCM-SHA384
For scripts intended to work on OpenSSL 3.0 through 4.0, traditional OpenSSL names remain the safer choice for TLS 1.2 and earlier.
OpenSSL 3.4 and later
Support for TLS 1.3 integrity-only cipher suites was added in OpenSSL 3.4.
This is primarily relevant to specialized environments rather than normal HTTPS administration.
OpenSSL 3.5 LTS
OpenSSL 3.5 is the primary documentation target for this MisterPKI series.
The examples in this article use the OpenSSL 3.5 command syntax while maintaining OpenSSL 3.0 compatibility where practical.
OpenSSL 4.0
openssl ciphers remains available in OpenSSL 4.0 with the same general cipher-list workflow.
For modern deployments, continue using provider-based OpenSSL configuration rather than older ENGINE-based integrations.
Real-world scenario: TLS works for browsers but fails for one application
Assume an internal HTTPS service is available at:
app.example.internal
After a TLS hardening change, browsers continue connecting normally.
However, an older monitoring agent begins reporting:
SSL handshake failed
The first instinct might be to re-enable a broad set of older ciphers.
Don’t do that yet.
Instead, determine:
- which OpenSSL version is on the diagnostic host;
- which cipher suites that OpenSSL supports;
- which protocols the server supports;
- which cipher the server negotiates normally;
- whether a specific TLS 1.2 cipher can connect;
- whether TLS 1.3 is involved;
- whether OpenSSL’s security level is rejecting the connection.
This separates a real compatibility problem from a certificate, protocol, or configuration issue.
Step 1: Check your OpenSSL version
Run:
openssl version -a
The OpenSSL version affects:
- cipher availability;
- security-level behavior;
- provider availability;
- supported protocols;
- accepted cipher names.
When comparing two servers or clients, record the OpenSSL version on both.
Do not assume:
OpenSSL 3
means two hosts have identical behavior.
A machine running OpenSSL 3.0 may differ significantly from one running OpenSSL 3.5 or OpenSSL 4.0.
Step 2: List the default OpenSSL cipher suites
Run:
openssl ciphers
The output is a colon-separated ordered list:
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:...
The order matters because it represents the cipher preference list.
For easier reading, convert the colons to line breaks:
openssl ciphers | tr ':' '\n'
You can count them:
openssl ciphers | tr ':' '\n' | wc -l
The exact list depends on your OpenSSL version, build, providers, configuration, and security level.
Step 3: Display detailed cipher information
Run:
openssl ciphers -v
A TLS 1.2 entry might resemble:
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
This tells you several things.
| Field | Meaning |
|---|---|
| Cipher name | OpenSSL cipher-suite name |
TLSv1.2 | Protocol associated with the suite |
Kx | Key exchange |
Au | Authentication |
Enc | Bulk encryption |
Mac | Message authentication or AEAD |
For example:
Kx=ECDH
indicates elliptic-curve Diffie-Hellman key exchange.
Au=RSA
indicates RSA authentication.
Enc=AESGCM(256)
indicates AES-256 in Galois/Counter Mode.
Mac=AEAD
indicates an authenticated-encryption cipher rather than a separate traditional MAC.
Step 4: Show only cipher suites that are actually supported
There is an important difference between:
openssl ciphers -v
and:
openssl ciphers -s -v
Without -s, OpenSSL lists all cipher suites matching the cipher expression.
With:
-s
OpenSSL considers factors such as:
- current security level;
- minimum protocol version;
- maximum protocol version.
This makes:
openssl ciphers -s -v
more representative of what the OpenSSL installation can actually use.
The OpenSSL documentation notes that even this may not exactly match what a server can negotiate because server certificates, DH parameters, signature algorithms, and other configuration can further restrict usable cipher suites.
For troubleshooting, I generally recommend starting with:
openssl ciphers -s -v
Step 5: List TLS 1.2 cipher suites
Use:
openssl ciphers \
-s \
-tls1_2 \
-v
This shows cipher suites that could be used if TLS 1.2 were negotiated.
Remember that the protocol selector is most meaningful when combined with:
-s
Simply using a cipher string such as:
TLSv1.2
does not force the connection protocol.
It selects cipher suites associated with TLS 1.2.
To actually force TLS 1.2 during a connection test, use openssl s_client:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2
For more TLS troubleshooting examples, see OpenSSL s_client Commands and Examples.
Step 6: List TLS 1.3 cipher suites
Run:
openssl ciphers \
-s \
-tls1_3 \
-v
Typical TLS 1.3 cipher suites include:
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256
OpenSSL’s default TLS 1.3 ciphersuite configuration includes those three suites.
This introduces one of the most important OpenSSL cipher concepts.
TLS 1.3 cipher suites are configured separately
For TLS 1.2 and earlier, you configure cipher suites through a cipher list.
For example:
openssl ciphers 'ECDHE:!aNULL:!eNULL'
TLS 1.3 uses a separate ciphersuite setting.
OpenSSL exposes this through:
-ciphersuites
For example:
openssl ciphers \
-ciphersuites \
'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256'
This is important when configuring software such as:
- Nginx;
- Apache;
- HAProxy;
- Java applications;
- OpenSSL-based servers;
- reverse proxies;
- network appliances.
A TLS 1.2 cipher setting does not necessarily control TLS 1.3.
For example, removing a TLS 1.2 cipher with:
!AES128
does not automatically mean:
TLS_AES_128_GCM_SHA256
has been removed from TLS 1.3.
Treat the TLS 1.3 ciphersuite configuration separately.
Step 7: Check the cipher negotiated by a real server
Now test the actual service:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal
Look for output such as:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Depending on OpenSSL version and output format, the presentation may vary.
A concise alternative is:
echo | openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
2>/dev/null |
grep -E 'Protocol|Cipher'
This answers a different question from:
openssl ciphers
openssl ciphers tells you what the local OpenSSL installation can potentially use.
openssl s_client tells you what was actually negotiated with a remote server.
For a deeper handshake workflow, see Debug TLS Connections with OpenSSL s_client.
Step 8: Force TLS 1.2
Suppose the modern connection negotiates TLS 1.3 successfully.
The failing legacy application only supports TLS 1.2.
Test the server explicitly:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2
If the connection succeeds, the server supports TLS 1.2.
If it fails, investigate whether TLS 1.2 has been intentionally disabled.
A protocol-version failure is not necessarily a cipher-suite failure.
Step 9: Force a specific TLS 1.2 cipher suite
Assume the older application requires:
ECDHE-RSA-AES256-GCM-SHA384
Test it:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
If the handshake succeeds, the server accepts that cipher.
If it fails while another TLS 1.2 cipher succeeds, you may have identified the compatibility issue.
Test another cipher:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2 \
-cipher 'ECDHE-RSA-AES128-GCM-SHA256'
This is far better than changing the server configuration blindly.
Step 10: Force a specific TLS 1.3 cipher suite
TLS 1.3 uses:
-ciphersuites
rather than:
-cipher
For example:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_3 \
-ciphersuites 'TLS_AES_256_GCM_SHA384'
Test another:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_3 \
-ciphersuites 'TLS_AES_128_GCM_SHA256'
This difference is worth remembering:
TLS 1.2 and below → -cipher
TLS 1.3 → -ciphersuites
Step 11: Display the hexadecimal cipher-suite ID
Use:
openssl ciphers -V
This works like -v but also displays the official cipher-suite value in hexadecimal.
Example output may contain:
0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 ...
This is useful when comparing OpenSSL output with:
- packet captures;
- Wireshark;
- vendor documentation;
- TLS debug logs;
- IANA cipher-suite assignments;
- Java debug output.
For the authoritative cipher-suite registry, see the IANA TLS Cipher Suites registry.
Step 12: Display standard IANA cipher names
Use:
openssl ciphers \
-stdname \
-v
This shows the standard cipher name alongside the OpenSSL representation.
For example, OpenSSL may map:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
to:
ECDHE-RSA-AES256-GCM-SHA384
This is useful because vendor documentation often uses IANA names while OpenSSL configuration historically uses OpenSSL names.
Step 13: Convert an IANA cipher name to an OpenSSL name
Suppose vendor documentation specifies:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Convert it:
openssl ciphers \
-convert \
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
The result identifies the equivalent OpenSSL name.
This is particularly helpful when translating:
- Java cipher-suite names;
- IANA documentation;
- vulnerability scanner reports;
- appliance configuration;
- compliance documentation;
into OpenSSL configuration.
Step 14: Understand OpenSSL cipher strings
One of the most powerful features of openssl ciphers is the cipher-string language.
Instead of listing every permitted cipher individually, you can select groups.
For example:
openssl ciphers 'HIGH'
This selects high-strength cipher suites.
Other commonly encountered selectors include:
DEFAULT
ALL
HIGH
AES
AES128
AES256
AESGCM
CHACHA20
ECDHE
DHE
RSA
SHA256
SHA384
You can combine these expressions to construct a cipher policy.
Step 15: Exclude cipher suites with !
The exclamation mark permanently excludes matching cipher suites.
For example:
openssl ciphers \
'HIGH:!aNULL:!eNULL'
Here:
!aNULL
excludes cipher suites without authentication.
And:
!eNULL
excludes cipher suites without encryption.
The OpenSSL documentation specifically warns that anonymous cipher suites are vulnerable to man-in-the-middle attacks and that NULL encryption suites provide no encryption.
Those should not be enabled in normal TLS configurations.
Another example:
openssl ciphers \
'HIGH:!aNULL:!eNULL:!MD5'
Once a cipher has been removed with:
!
it cannot be reintroduced later in the same cipher expression.
Step 16: Understand the difference between ! and –
OpenSSL also supports:
-
to remove a cipher temporarily.
For example:
HIGH:-AES128
removes AES-128 suites from the current list.
Unlike:
!AES128
a cipher removed with - can potentially be added back later.
For strict security exclusions, ! is generally clearer.
Use:
!
when you mean:
Never allow this category.
Step 17: Reorder ciphers with +
The plus sign at the beginning of a cipher token moves matching ciphers to the end of the existing list.
For example:
HIGH:+AES128
does not add new AES-128 ciphers.
It moves matching AES-128 entries already in the list toward the end.
This is a preference operation.
Step 18: Use + as a logical AND
The plus sign also has another meaning when used between algorithm selectors.
For example:
openssl ciphers \
'AESGCM+ECDHE'
means:
Select cipher suites that use both AES-GCM and ECDHE.
This is effectively a logical AND.
That is different from:
+AESGCM
which changes preference order.
The position of the + matters.
Step 19: Sort by encryption strength
Use:
@STRENGTH
to sort the existing list by encryption key length.
Example:
openssl ciphers \
'HIGH:@STRENGTH'
This changes the ordering of the selected cipher suites.
Do not assume that ordering strictly by symmetric key length produces the best modern TLS policy. Modern protocol design involves more than simply comparing AES key sizes.
Use @STRENGTH when you specifically need this ordering behavior.
Step 20: Understand OpenSSL security levels
OpenSSL supports security levels from:
0
through:
5
A cipher string can change the security level using:
@SECLEVEL=n
For example:
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=2'
The security level affects more than the cipher list.
Depending on the level, OpenSSL may reject:
- weak encryption;
- insufficient key sizes;
- weak signature algorithms;
- weak DH parameters;
- other cryptographic parameters below the required security strength.
The official OpenSSL documentation defines security levels through the SSL security-level API.
Why does lowering SECLEVEL make an old connection work?
Suppose this fails:
openssl s_client \
-connect legacy.example.internal:443 \
-servername legacy.example.internal
But a test using a lower security level succeeds.
That indicates the remote endpoint probably relies on cryptographic parameters the current OpenSSL policy considers too weak.
The wrong response is:
Great, let’s permanently lower the security level.
Instead, identify the actual problem.
Possible causes include:
- an old server certificate;
- a weak RSA key;
- SHA-1 signatures;
- small DH parameters;
- legacy cipher suites;
- outdated TLS protocols.
Lowering the security level can be useful as a diagnostic test, but upgrading the remote system is usually the correct long-term solution.
Step 21: Compare security-level cipher lists
Compare:
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=1'
with:
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=2'
Save the results:
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=1' \
> seclevel1.txt
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=2' \
> seclevel2.txt
Compare:
diff -u \
seclevel1.txt \
seclevel2.txt
This can help explain why an application stopped negotiating after a security-policy change.
Step 22: Understand DEFAULT
Run:
openssl ciphers 'DEFAULT'
DEFAULT refers to OpenSSL’s compiled-in default cipher list.
OpenSSL describes the default list as providing strong security while maintaining reasonable interoperability.
You can build on it:
openssl ciphers \
'DEFAULT:!aNULL:!eNULL'
However, do not assume the exact output of:
DEFAULT
will remain identical across:
- OpenSSL releases;
- operating-system distributions;
- vendor builds.
If application behavior depends on an exact cipher policy, test the resulting list on the target systems.
Step 23: Understand HIGH
Run:
openssl ciphers 'HIGH'
HIGH is a class of high-strength cipher suites.
You will often see configuration examples such as:
HIGH:!aNULL:!eNULL
This can be useful, but it is still a broad selector.
A security policy should be based on:
- supported TLS versions;
- application requirements;
- organizational security standards;
- current cryptographic recommendations;
- client compatibility.
Do not assume that every cipher matching a broad keyword is automatically desirable for every environment.
Step 24: Find AES-GCM cipher suites
Run:
openssl ciphers \
-v \
'AESGCM'
To narrow it to ECDHE:
openssl ciphers \
-v \
'AESGCM+ECDHE'
To require RSA authentication:
openssl ciphers \
-v \
'AESGCM+ECDHE+aRSA'
This is useful when determining whether a particular certificate type and cipher combination is available.
Step 25: Find ChaCha20 cipher suites
Run:
openssl ciphers \
-v \
'CHACHA20'
ChaCha20-Poly1305 is an AEAD cipher commonly available in modern TLS implementations.
Remember that TLS 1.3’s:
TLS_CHACHA20_POLY1305_SHA256
belongs to the separately configured TLS 1.3 ciphersuite list.
Step 26: Find ECDHE cipher suites
Run:
openssl ciphers \
-v \
'ECDHE'
ECDHE provides ephemeral elliptic-curve Diffie-Hellman key exchange.
You can combine it with RSA authentication:
openssl ciphers \
-v \
'ECDHE+aRSA'
Or ECDSA authentication:
openssl ciphers \
-v \
'ECDHE+aECDSA'
The server’s certificate type matters.
For example, an RSA certificate cannot satisfy a cipher suite requiring ECDSA authentication.
This is one reason why:
openssl ciphers -s
still cannot perfectly predict every cipher a particular server will use without considering its certificate configuration.
Step 27: Why a cipher can appear in the list but still fail
Assume:
openssl ciphers -v
shows:
ECDHE-ECDSA-AES256-GCM-SHA384
That does not guarantee your server can negotiate it.
The server may have only an RSA certificate.
The suite requires:
ECDSA authentication
and therefore may not be usable.
Other limiting factors include:
- certificate key type;
- signature algorithms;
- DH parameters;
- supported groups;
- protocol configuration;
- security level;
- provider configuration;
- server preference;
- client capabilities.
Treat openssl ciphers as one part of TLS troubleshooting.
Use openssl s_client to test the real endpoint.
Step 28: Diagnose “no shared cipher”
Suppose this command fails:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2
with an error related to:
no shared cipher
Begin by listing your local TLS 1.2 ciphers:
openssl ciphers \
-s \
-tls1_2 \
-v
Then test a likely modern suite explicitly:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
Try another:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal \
-tls1_2 \
-cipher 'ECDHE-RSA-AES128-GCM-SHA256'
If neither works, investigate:
- whether TLS 1.2 is enabled;
- whether the server uses an RSA or ECDSA certificate;
- whether client and server share supported groups;
- whether security policies are blocking the handshake;
- whether a proxy or load balancer terminates TLS before the application.
For more examples, see Debug TLS Connections with OpenSSL s_client.
Step 29: Test a proposed server cipher policy before deploying it
Assume you are considering this TLS 1.2 policy:
ECDHE+AESGCM:!aNULL:!eNULL
Before applying it to Nginx, Apache, HAProxy, or another service, inspect what it actually means:
openssl ciphers \
-s \
-v \
'ECDHE+AESGCM:!aNULL:!eNULL'
If the output is empty, your expression does not match any supported cipher suites under the current configuration.
Count the result:
openssl ciphers \
-s \
'ECDHE+AESGCM:!aNULL:!eNULL' |
tr ':' '\n' |
wc -l
Review every result before deploying the policy.
This is exactly where openssl ciphers is most valuable: it allows you to validate the cipher expression before restarting a production service.
Step 30: Compare two cipher configurations
Suppose you have an old policy:
DEFAULT
and a proposed policy:
ECDHE+AESGCM:!aNULL:!eNULL
Save both:
openssl ciphers \
'DEFAULT' |
tr ':' '\n' \
> default-ciphers.txt
openssl ciphers \
'ECDHE+AESGCM:!aNULL:!eNULL' |
tr ':' '\n' \
> proposed-ciphers.txt
Compare:
diff -u \
default-ciphers.txt \
proposed-ciphers.txt
This immediately shows which TLS 1.2-and-earlier cipher suites would disappear.
Repeat the comparison on the actual production operating system because cipher availability may differ from your workstation.
Step 31: Compare OpenSSL installations on two hosts
When one server supports a TLS integration and another does not, run:
openssl version -a
Then:
openssl ciphers \
-s \
-v \
> openssl-ciphers.txt
Also capture providers:
openssl list -providers \
> openssl-providers.txt
And disabled features:
openssl list -disabled \
> openssl-disabled.txt
Compare the results.
For a complete OpenSSL environment inventory workflow, see How to List OpenSSL Commands and Get Command Help.
TLS protocol names inside cipher strings can be misleading
A cipher selector such as:
TLSv1.2
means:
Select cipher suites introduced for TLS 1.2.
It does not mean:
Force TLS 1.2.
For example:
openssl ciphers 'TLSv1.2'
filters cipher suites.
To force a protocol during a real connection, use:
openssl s_client \
-tls1_2 \
...
This distinction prevents a lot of TLS troubleshooting confusion.
Cipher suites versus certificate signature algorithms
A certificate’s signature algorithm is not the same thing as the TLS cipher suite.
For example, a certificate may be signed using:
sha256WithRSAEncryption
while a TLS connection negotiates:
TLS_AES_256_GCM_SHA384
These describe different cryptographic operations.
To inspect the certificate signature algorithm:
openssl x509 \
-in certificate.pem \
-noout \
-text |
grep 'Signature Algorithm'
To inspect the negotiated TLS cipher:
openssl s_client \
-connect app.example.internal:443 \
-servername app.example.internal
Do not diagnose certificate-signature problems solely from the TLS cipher name.
Cipher suites versus supported groups
TLS cipher configuration is also different from supported elliptic-curve or finite-field groups.
Especially with TLS 1.3, the cipher-suite name does not specify the key-exchange group.
For example:
TLS_AES_256_GCM_SHA384
does not tell you whether the connection used:
X25519
P-256
P-384
or another supported key-exchange group.
That information is negotiated separately.
Use detailed openssl s_client output when you need to inspect the complete handshake rather than just the cipher.
OpenSSL providers and cipher availability
OpenSSL 3 uses the provider architecture.
List loaded providers:
openssl list -providers
For details:
openssl list \
-providers \
-verbose
Provider configuration can affect which cryptographic implementations are available.
If an algorithm is missing unexpectedly, compare:
openssl list -providers
between a working and non-working system.
The openssl ciphers command also supports provider options such as:
-provider
-provider-path
-propquery
and newer OpenSSL releases document provider parameters as well.
Most administrators using standard modern TLS cipher suites will not need to specify provider options manually.
FIPS environments
The cipher selector:
FIPS
can be used to identify cipher suites permitted for FIPS-oriented configurations.
For example:
openssl ciphers \
-s \
-v \
'FIPS'
However, seeing a cipher in this list is not by itself proof that the entire application environment is operating in a validated FIPS configuration.
FIPS compliance involves:
- the cryptographic module;
- module configuration;
- approved algorithms;
- application behavior;
- operating mode;
- platform and validation requirements.
Do not treat:
openssl ciphers FIPS
as a complete FIPS-compliance test.
Avoid blindly enabling legacy cipher suites
When troubleshooting an old system, you may encounter recommendations to enable:
- NULL encryption;
- anonymous authentication;
- RC4;
- DES;
- 3DES;
- old CBC suites;
- obsolete protocols;
- low OpenSSL security levels.
Do not enable weak cryptography globally just to make one outdated client connect.
Instead:
- identify exactly what the client requires;
- confirm whether an upgrade is available;
- isolate the legacy system if necessary;
- minimize the compatibility exception;
- document the exception;
- establish a removal date.
OpenSSL explicitly excludes NULL encryption from normal cipher groups and discourages anonymous authentication because of the security risks.
A practical TLS hardening workflow
Before changing a server’s cipher configuration:
1. Record the current OpenSSL version
openssl version -a
2. Capture current supported TLS 1.2 ciphers
openssl ciphers \
-s \
-tls1_2 \
-v \
> tls12-before.txt
3. Capture TLS 1.3 cipher suites
openssl ciphers \
-s \
-tls1_3 \
-v \
> tls13-before.txt
4. Test the proposed TLS 1.2 expression
openssl ciphers \
-s \
-v \
'ECDHE+AESGCM:!aNULL:!eNULL'
5. Test representative clients
Use:
openssl s_client
and real application testing.
6. Apply the server configuration
Follow the configuration mechanism appropriate for:
- Nginx;
- Apache;
- HAProxy;
- Java;
- application middleware;
- network appliance;
- cloud load balancer.
7. Test again
Check:
TLS 1.2
TLS 1.3
certificate chain
hostname validation
cipher negotiation
application compatibility
8. Compare before and after
Review which cipher suites were actually removed.
This reduces the chance that a security improvement causes an unexpected outage.
Useful openssl ciphers commands
List default ciphers:
openssl ciphers
One per line:
openssl ciphers | tr ':' '\n'
Verbose:
openssl ciphers -v
Only currently supported ciphers:
openssl ciphers -s -v
Show cipher IDs:
openssl ciphers -V
Show standard names:
openssl ciphers -stdname -v
TLS 1.2:
openssl ciphers -s -tls1_2 -v
TLS 1.3:
openssl ciphers -s -tls1_3 -v
AES-GCM:
openssl ciphers -v 'AESGCM'
ECDHE and AES-GCM:
openssl ciphers -v 'ECDHE+AESGCM'
Exclude anonymous and NULL encryption:
openssl ciphers \
'HIGH:!aNULL:!eNULL'
Security level 2:
openssl ciphers \
-s \
-v \
'ALL:@SECLEVEL=2'
Convert an IANA name:
openssl ciphers \
-convert \
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Important openssl ciphers options
| Option | Purpose |
|---|---|
-help | Displays usage information |
-s | Lists ciphers consistent with current security and protocol settings |
-v | Displays detailed cipher information |
-V | Includes hexadecimal cipher-suite values |
-stdname | Displays standard cipher-suite names |
-convert name | Converts a standard cipher name to its OpenSSL name |
-tls1_2 | Filters supported suites for TLS 1.2 when used with -s |
-tls1_3 | Filters supported suites for TLS 1.3 when used with -s |
-ciphersuites | Sets TLS 1.3 cipher suites for the query |
-psk | Includes PSK suites with -s |
-srp | Includes SRP suites with -s; SRP support is deprecated |
-provider | Loads a provider |
-provider-path | Specifies provider module location |
-propquery | Applies an OpenSSL property query |
For the complete syntax, see the official OpenSSL 3.5 ciphers documentation.
Common openssl ciphers troubleshooting questions
Why does openssl ciphers show a cipher that my server cannot negotiate?
The command may show a cipher recognized by the local OpenSSL installation even though the server cannot use it.
Additional restrictions include:
- certificate type;
- signature algorithms;
- DH parameters;
- supported groups;
- protocol versions;
- application configuration;
- security policies.
Try:
openssl ciphers -s -v
and then verify the real endpoint with openssl s_client.
Why does -cipher not affect TLS 1.3?
Because TLS 1.3 cipher suites are configured separately.
Use:
-cipher
for TLS 1.2 and earlier.
Use:
-ciphersuites
for TLS 1.3.
Why does a server report no shared cipher?
The client and server may have no mutually acceptable cipher suite after considering:
- supported protocols;
- cipher configuration;
- certificate authentication type;
- security levels;
- supported algorithms.
Test TLS 1.2 explicitly and then test individual cipher suites with openssl s_client.
Does TLS_AES_256_GCM_SHA384 use RSA authentication?
The TLS 1.3 cipher-suite name does not specify certificate authentication or key exchange the way many TLS 1.2 suite names do.
Authentication and key exchange are negotiated separately in TLS 1.3.
What is the difference between AES256-SHA and TLS_AES_256_GCM_SHA384?
They represent very different generations of TLS cipher suites.
AES256-SHA is an older TLS-style cipher-suite name representing multiple negotiated components.
TLS_AES_256_GCM_SHA384 is a TLS 1.3 cipher suite whose name describes the symmetric encryption and hash associated with the TLS 1.3 key schedule.
Do not assume they are equivalent because both contain AES256.
What does !aNULL mean?
It permanently removes cipher suites that do not provide authentication.
These anonymous cipher suites are not appropriate for normal HTTPS connections.
What does !eNULL mean?
It removes cipher suites that provide no encryption.
NULL-encryption cipher suites should not be enabled for normal TLS services.
What is @SECLEVEL=2?
It tells OpenSSL to apply security level 2 to the cipher expression.
Security levels affect cryptographic parameters beyond only cipher suites.
Should I use HIGH as my production TLS configuration?
HIGH is useful as a cipher selector, but production TLS policy should consider:
- protocol versions;
- supported clients;
- organizational requirements;
- certificate types;
- current security guidance.
Treat it as a building block rather than a complete security policy.
How do I see which cipher a website is using?
Run:
openssl s_client \
-connect example.com:443 \
-servername example.com
Review the negotiated protocol and cipher.
How do I test one TLS 1.2 cipher?
Run:
openssl s_client \
-connect example.com:443 \
-servername example.com \
-tls1_2 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
How do I test one TLS 1.3 cipher?
Run:
openssl s_client \
-connect example.com:443 \
-servername example.com \
-tls1_3 \
-ciphersuites 'TLS_AES_256_GCM_SHA384'
OpenSSL ciphers versus openssl list
These commands answer different questions.
Use:
openssl ciphers
for TLS cipher-suite selection and cipher strings.
Use:
openssl list -cipher-algorithms
to list symmetric cryptographic cipher implementations exposed through OpenSSL’s provider architecture.
For example, openssl list -cipher-algorithms may show cryptographic implementations such as AES, while openssl ciphers shows complete TLS cipher suites.
For more command-discovery examples, see How to List OpenSSL Commands and Get Command Help.
Security and privacy considerations
The openssl ciphers command itself generally reveals only local cryptographic capabilities.
However, TLS troubleshooting output from:
openssl s_client
may expose:
- internal hostnames;
- certificate subjects;
- organization names;
- email addresses;
- internal CA names;
- IP addresses;
- certificate serial numbers;
- network architecture.
Before sharing terminal output publicly, replace real infrastructure with generic placeholders.
Examples in this article use names such as:
app.example.internal
legacy.example.internal
Do not copy real internal hostnames or personally identifying information into public documentation.
Related OpenSSL guides
- Browse the complete OpenSSL Commands Guide.
- Test real TLS endpoints with OpenSSL s_client Commands and Examples.
- Follow a practical workflow to Debug TLS Connections with OpenSSL s_client.
- Learn how to List OpenSSL Commands and Get Command Help.
- Validate certificate chains with OpenSSL verify.
- Inspect certificate details with OpenSSL X.509 examples.
- Review the broader collection of SSL and PKI tools.
External references
- OpenSSL 3.5 ciphers documentation
- OpenSSL 3.0 ciphers documentation
- OpenSSL 4.0 ciphers documentation
- OpenSSL s_client documentation
- OpenSSL TLS security levels
- IANA TLS Cipher Suites Registry
Conclusion
The openssl ciphers command is most useful when you treat it as a TLS configuration and troubleshooting tool, not simply a way to print a huge list of cipher names.
The practical workflow is:
Check the OpenSSL version
↓
List supported cipher suites
↓
Inspect the proposed cipher string
↓
Separate TLS 1.2 from TLS 1.3
↓
Test the real server with s_client
↓
Force individual ciphers when necessary
↓
Identify the actual incompatibility
↓
Fix or upgrade the client instead of weakening TLS blindly
For TLS 1.2 and earlier, remember:
cipher list → -cipher
For TLS 1.3:
cipher suites → -ciphersuites
And when a connection fails after TLS hardening, do not immediately restore every legacy cipher.
Use openssl ciphers to determine what the local system supports, use openssl s_client to determine what the remote server actually negotiates, and narrow the compatibility problem to a specific protocol or cipher suite before changing production configuration.
Leave a Reply