0

My nginx backend server supports the following ciphers:

ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"

I allow the client to upload their own SSL certificate and key and I want to validate that it matches one of the configured ciphers. Otherwise they lose access to the server when I restart nginx config with the newly uploaded certificate. When I use openssl x509 I get an output with something like

Signature Algorithm: ecdsa-with-SHA256

How can I use python or bash code to take this string and figure out if it's valid for my nginx config?

3
  • 1
  • 1
    WRT Signature Algorithm: ecdsa-with-SHA256 - this pertains to how the leaf certificate is signed by the next certificate up in the chain. It has nothing to do with the signature by the cert over the TLS handshake. The good news is that you support several TLS 1.3 cipher suites. These can be used with certificates containing either RSA public keys or ECDH public keys. So, you should be fine with either type, as long as the client supports TLS 1.3.
    – mti2935
    Feb 22 at 19:45
  • @mti2935: 1.3 can use a cert containing RSA (classic or PSS) or ECDSA or EdDSA key, but it can never use an ECDH or XDH (or FFDH) key from a cert, they are always ephemeral. Also specifying 1.3 suites in nginx ssl_ciphers is ineffective; OpenSSL will ignore them and use its default -- which happens to be the same as this directive would have specified. Mar 11 at 0:34

2 Answers 2

1

See my comment above on why this may be a non-issue if you are using TLS 1.3.

If you still feel you need an automated way to analyze a certificate, this short python script might help:

from cryptography import x509
from cryptography.hazmat.backends import default_backend

with open('eccert.pem', 'rb') as file:
    certificate=file.read()
certDecoded = x509.load_pem_x509_certificate(certificate, default_backend())    
print(certDecoded.subject)
print(certDecoded.public_key())

If you place the file eccert.pem in the same directory, you'll see that the script produces the following when run, indicating that the certificate contains an EC public key:

<cryptography.hazmat.backends.openssl.ec._EllipticCurvePublicKey object at 0x7f6539a63df0>

If you run the script against rsacert.pem instead, you'll see that it produces the following, indicating that the certificate contains an RSA public key:

<cryptography.hazmat.backends.openssl.rsa._RSAPublicKey object at 0x7f6539a63f40>

Copied below are both pem files.

eccert.pem

-----BEGIN CERTIFICATE-----
MIIDsjCCAzigAwIBAgISBDJmXL6n06M8cOTXFDbs+kqzMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
MTAeFw0yNDAxMTUwNTE4NDlaFw0yNDA0MTQwNTE4NDhaMBwxGjAYBgNVBAMTEXN0
YWNrZXhjaGFuZ2UuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqAjRWWwJ
RSCyerqocxzh0395qLgCsnhjUAdPeICBf/J6Cu5yWFEmRXokOYlh2kACMIvPslS5
RgdPgp6iK9nwxqOCAkIwggI+MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUMH5X1YCS
COQsDJDBONPhwUb5Q0UwHwYDVR0jBBgwFoAUWvPtK/w2wjd5uVIw6lRvz1XLLqww
VQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vZTEuby5sZW5jci5v
cmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9lMS5pLmxlbmNyLm9yZy8wSwYDVR0RBEQw
QoIYKi5tZXRhLnN0YWNrZXhjaGFuZ2UuY29tghMqLnN0YWNrZXhjaGFuZ2UuY29t
ghFzdGFja2V4Y2hhbmdlLmNvbTATBgNVHSAEDDAKMAgGBmeBDAECATCCAQQGCisG
AQQB1nkCBAIEgfUEgfIA8AB2AKLiv9Ye3i8vB6DWTm03p9xlQ7DGtS6i2reK+Jpt
9RfYAAABjQvFztsAAAQDAEcwRQIgQMs3VNVQgjyAOxCFo+84k4xycmuyDWtIpm1k
yluXCGkCIQD5cw9glnZBovkrRwYd/LMymZeSBP3jaFXi117VlVi05gB2AO7N0GTV
2xrOxVy3nbTNE6Iyh0Z8vOzew1FIWUZxH7WbAAABjQvFzq8AAAQDAEcwRQIhAONp
YR6UgoB+CR29CxL+Y8fdp6Q7GYb3cC/rH22+CKWsAiATEfGRPFXNltwIDmdvU9+F
t2FIXtzJAPZbvCp8DQfr5zAKBggqhkjOPQQDAwNoADBlAjB8PA5ZqHFgziw8qTwI
cNmwHLFpNFMzk6Cry9qdkVqyPNXFiaUe2gq0ZgRzGoOqv+gCMQDnMSjnETYxNMNF
QUZRsXiRlm5FWR/tv4CM2kJ7szFDw1yautMV8+dJDjbC/n0dxgs=
-----END CERTIFICATE-----

rsacert.pem

-----BEGIN CERTIFICATE-----
MIIHrjCCBpagAwIBAgIQPTiQEKFXQA/z/MDIBIrKazANBgkqhkiG9w0BAQsFADCB
lTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMT0wOwYDVQQD
EzRTZWN0aWdvIFJTQSBPcmdhbml6YXRpb24gVmFsaWRhdGlvbiBTZWN1cmUgU2Vy
dmVyIENBMB4XDTIzMTEwMjAwMDAwMFoXDTI0MTEwMTIzNTk1OVowSjELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEzARBgNVBAoTCmVCYXksIEluYy4x
ETAPBgNVBAMTCGViYXkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAqQT6wNkzfTtFjfQtwKEeM4mpmHRqcDWATSq+bW/xtj879V5gsNd0lvWd8SWI
OUgR+6csWerleRMCHg6ZKojIF+THc+nvdV6WJVKQwOmRtm745lGMlxjuJmACNMN9
21INIxhmcY4kDcAig5CEOD+jGg0ajqqx7wG2xiWEp0t1we5ypgGiiJPp1FVETiXN
yklnkPkzFF8sLDBdVQHDGnBzYEVm/HUwatxFB09HyDqegsK7phiTVm77M2CG1/KR
4KFmunLtN1qqnZ0SquKyJzibimKf6UGuWHd3u548VtIUg3UigXuqLb04ZToidbi1
05KanEowHL1uXF1WyVV2ccgkFwIDAQABo4IEQjCCBD4wHwYDVR0jBBgwFoAUF9nW
JSdn+THCSUPZMDZEjGypT+swHQYDVR0OBBYEFDqQtqCnnA6X6MzznbmVGKac3mV8
MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUF
BwMBBggrBgEFBQcDAjBKBgNVHSAEQzBBMDUGDCsGAQQBsjEBAgEDBDAlMCMGCCsG
AQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgIwWgYDVR0f
BFMwUTBPoE2gS4ZJaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBT3Jn
YW5pemF0aW9uVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNybDCBigYIKwYBBQUH
AQEEfjB8MFUGCCsGAQUFBzAChklodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3Rp
Z29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCMG
CCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTCCAX0GCisGAQQB1nkC
BAIEggFtBIIBaQFnAHYAdv+IPwq2+5VRwmHM9Ye6NLSkzbsp3GhCCp/mZ0xaOnQA
AAGLkj6m4wAABAMARzBFAiEAw3JysbrSb8ZALyx99vWRNT6p3ADzrUYVrkf8pJJu
NbYCIHlVObN/vnoYfidhO/ZSOie8vjr3ruCrxUXKn8tEEhusAHYAPxdLT9ciR1iU
HWUchL4NEu2QN38fhWrrwb8ohez4ZG4AAAGLkj6nEgAABAMARzBFAiEA9xY3dmYP
Iepjpg4uPuka5rucAA9XDQtgX5AaPo9irc0CIF2cSAxf1RMHDenJEbZ/nsWcIAFJ
wBJhM28AF2ExZLUMAHUA7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZsA
AAGLkj6nEQAABAMARjBEAiBHnn1aJMAUfj/+wLCKqudzqHFvcM6JOgsCXqiwl3bC
EQIgLTU1YRbmG8v9ifFUGqIOwBUNV+Ra2+kKcy4H6LE4f/YwggEHBgNVHREEgf8w
gfyCCGViYXkuY29tggxiZWZyLmViYXkuYmWCDGJlbmwuZWJheS5iZYIMY2Fmci5l
YmF5LmNhgghlLWJheS5pdIIHZWJheS5hdIIHZWJheS5iZYIHZWJheS5jYYIHZWJh
eS5jaIIKZWJheS5jby51a4ILZWJheS5jb20uYXWCC2ViYXkuY29tLmhrggtlYmF5
LmNvbS5teYILZWJheS5jb20uc2eCB2ViYXkuZGWCB2ViYXkuZXOCB2ViYXkuZnKC
B2ViYXkuaWWCB2ViYXkuaW6CB2ViYXkuaXSCB2ViYXkubmyCB2ViYXkucGiCB2Vi
YXkucGyCB2ViYXkudXMwDQYJKoZIhvcNAQELBQADggEBAJW/U2cRX2UQsyz8iRTI
3actxYUVPtu42+1/V26KmgYOHuYJXWBNSJ+FpIwDJbbYzPzNKX1/uZThLt/erOBV
NB2u9TG1wyg5xG+aYefyRSy9OEipTZ42mSlxzoeOCrZ0u8fLraFUlDnyuTRzYdX6
gu38/N7VLWvGWDUksEQaapklGU2pvduxSJ6mIxdS6RXinKB9eYFynePpQdBrS0aA
gO22gX652iv4wXlQOjyfbF2b6RSOFyUFbshaEKoDzNCZ7fyzcNvjjGRsdu2TkeTz
wIjVTcfltSJFJ3AsmZHPM4QWzt56ihmyJoCjUCGDCyAgM7x7K39HMockrfDQ1b6P
RvY=
-----END CERTIFICATE-----
1
  • Thanks for the kind response. Your python code is kind of close to what I wanted but I still can't achieve my goal. What I want is basically a machine that take a certificate and a list of cipher suites in the format I show in the original question and returns True/False depending on the certificate matching one of the suites
    – Shahar G
    Apr 4 at 13:58
1

Update your ciphersuites, instead

It seems you are facing a problem that you have caused by yourself by not supporting ECDSA for TLS 1.2. Instead of trying to figure out how to validate the certificated provided are RSA certificates you could update your ciphersuites to support both type of certificates by adding the corresponding ECDSA alternatives:

RSA ECDSA
DHE-RSA-AES256-GCM-SHA384 DHE-ECDSA-AES256-GCM-SHA384
DHE-RSA-AES128-GCM-SHA256 DHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256

Resulting:

ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-ECDSA-AES256-GCM-SHA384:DHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256"

Or switch to the recommendations from RFC9325, 4.2:

IANA name from RFC OpenSSL name for Nginx
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ECDHE-RSA-AES256-GCM-SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ECDHE-ECDSA-AES128-GCM-SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ECDHE-ECDSA-AES256-GCM-SHA384

Resulting:

ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384"

With either you would not have the problem in the first place.

If you validate, validate for all possible problems

You wrote your concern is:

Otherwise they lose access to the server when I restart Nginx config with the newly uploaded certificate.

This could happen for a number of other reasons:

  • The files are corrupted or not certificate and key altogether.
  • The client could have uploaded the key in place of the certificate and vice versa.
  • The key might not match the certificate.

And if HTTP Strict Transport Security (HSTS) was enabled:

  • The certificate does not match the hostname.
  • The certificate is not signed by a trusted CA.

In order to test all possibilities at once you could use an actual connection to test the certificate the same way a browser would do. You could use a staging environment with the updated configuration and try to make a connection using, e.g., curl --hsts --fail (HSTS since 7.77.0) or wget (supports HSTS).

1
  • There are no DHE-ECDSA ciphersuites for TLS1.2 (or lower), and the API used by nginx ssl_ciphers (OpenSSL's SSL[_CTX]_set_cipher_list) does not apply to TLS1.3 suites, so both of these are ignored. Mar 11 at 0:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .