pornin / TestSSLServer

MIT License
140 stars 50 forks source link

Minimum EC size (with extension) not correct when server using certificate signed with EC #1

Open ghost opened 8 years ago

ghost commented 8 years ago

Hi Thomas,

Thank you for developing this great tool, it's really helpful to check the security of a SSL server. But I found an odd thing recently that the info returned in ServerHello might not be correct when server side is using a certificate signed with EC. At first, I wanted to use this tool to check the security of my Tomcat which was using a certificate signed with RSA:2048, but I got WARN[SK004]. To get rid of that warning, I switched to use a certificate signed with EC:secp256r1, but I still got that warning. That made me start to look into the codes to identify the issue is in server side or in the tools. Finally I found if we send ClientHello with the extension including all curves from 1 to 30, the server will tell us it supports 1 ~ 23. But if we send ClientHello with the extension including one curve each time, from 1 to 30, the server will tell us it only supports 23, which's the curve used to sign the certificate. And then I tried a little further, if we send ClientHello with the extension including any other curves its ID less than 23 together with 23, the server will always tell us it supports all of them. I am not sure if this is a defect of the tool or a defect of the server I am using. Can you please take a look?

FYI. The info of my server, certificate, and cipher suites: Web Server: Tomcat 8.0.33 TLSv1.2: server selection: uses client preferences 3f- (key: EC) ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 3f- (key: EC) ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 3f- (key: EC) ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 key type: EC key size: 256 key curve: ansix9p256r1 (P-256) sign hash: SHA-256 Minimum EC size (no extension): 256 Minimum EC size (with extension): 162 Supported curves (size and name) ('*' = selected by server): 162 sect163k1 (K-163)

Thanks, Alan

pornin commented 8 years ago

Hello,

the tricky point is that elliptic curves can be used by the server for two distinct purposes: for the key exchange (the "ECDHE" part), and for validating signatures (ECDSA). The server will validate signatures only if it requests a certificate from the client, and the client sends such a certificate.

TestSSLServer reports supported curves only for the key exchange. When it lists a given curve as supported, then it means that it performed a handshake in which the server indeed used that specific curve. TestSSLServer tries to force the server to use a specific curve by sending a "supported curves" extension that lists only that curve. The curve marked as "selected by server" is the curve that the server chooses when given the choice (if the client's extension includes a long list of curves, the server chooses that specific curve).

Note that the server may still support other curves for ECDSA -- TestSSLServer does not test that (and cannot). Similarly, if the server sends a "supported curves" extension, TestSSLServer completely ignores it (that extension does not specify whether the curves are supported for ECDSA or for ECDHE or for both, and many servers don't send it at all anyway).

Thus, in your case, the server will support only two curves for ECDHE (P-256 and K-163), and will prefer to use P-256 unless forced to use K-163 explicitly (by a supported curve extension from the client). If the server nevertheless sends a long list of supported curves in its own "supported curves" extension, then this probably means that it knows all these curves when verifying ECDSA signatures (but, of course, the server won't have any signature to validate unless it asks for a client certificate).

    --Thomas

Hi Thomas,

Thank you for developing this great tool, it's really helpful to check the security of a SSL server. But I found an odd thing recently that the info returned in ServerHello might not be correct when server side is using a certificate signed with EC. At first, I wanted to use this tool to check the security of my Tomcat which was using a certificate signed with RSA:2048, but I got WARN[SK004]. To get rid of that warning, I switched to use a certificate signed with EC:secp256r1, but I still got that warning. That made me start to look into the codes to identify the issue is in server side or in the tools. Finally I found if we send ClientHello with the extension including all curves from 1 to 30, the server will tell us it supports 1 ~ 23. But if we send ClientHello with the extension including one curve each time, from 1 to 30, the server will tell us it only supports 23, which's the curve used to sign the certificate. And then I tried a little further, if we send ClientHello with the extension including any other curves its ID less than 23 together with 23, the server will always tell us it supports all of them. I am not sure if this is a defect of the tool or a defect of the server I am using. Can you please take a look?

FYI. The info of my server, certificate, and cipher suites: Web Server: Tomcat 8.0.33 TLSv1.2: server selection: uses client preferences 3f- (key: EC) ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 3f- (key: EC) ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 3f- (key: EC) ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 key type: EC key size: 256 key curve: ansix9p256r1 (P-256) sign hash: SHA-256 Minimum EC size (no extension): 256 Minimum EC size (with extension): 162 Supported curves (size and name) ('*' = selected by server): 162 sect163k1 (K-163)

  • 256 secp256r1 (P-256)

Thanks, Alan


You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/pornin/TestSSLServer/issues/1

ghost commented 8 years ago

Hi Thomas,

Thank you for your response. Perhaps you didn't get me, I was talking about the curves for ECDHE, not for ECDSA. Let's put the focus on the [elliptic curve enumeration, version=TLSv1.2, 3 suite(s)] phase of TestSSLServer. In this phase, I think, we are trying to get the exact curves list supported by the server with the client side sending a "supported curves" extension. The odd thing that I observed is, if we put all the 30 curves in the "supported curves" extension, the server will reply that it supports all the curves from 1 to 23, while if we put only one curve each time in the "supported curves" extension, the server will tell us it only support curve 23. In brief, we will get different result to send "supported curves" extension with all curves in one time or with only one curve each time.

1. The following is the output when sending "supported curves" extension with all 30 curves included:

[elliptic curve enumeration, version=TLSv1.2, 3 suite(s)] . tr.Curve Id: 1 | Name: sect163k1 (K-163) | hasECExt: True tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 2 | Name: sect163r1 | hasECExt: True tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 3 | Name: sect163r2 (B-163) | hasECExt: True tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 162 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 4 | Name: sect193r1 | hasECExt: True tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 5 | Name: sect193r2 | hasECExt: True tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 6 | Name: sect233k1 (K-233) | hasECExt: True tr.ECSize: 231 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 231 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 7 | Name: sect233r1 (B-233) | hasECExt: True tr.ECSize: 232 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 232 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 8 | Name: sect239k1 | hasECExt: True tr.ECSize: 237 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 237 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 9 | Name: sect283k1 (K-283) | hasECExt: True tr.ECSize: 281 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 281 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 10 | Name: sect283r1 (B-283) | hasECExt: True tr.ECSize: 282 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 282 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 11 | Name: sect409k1 (K-409) | hasECExt: True tr.ECSize: 407 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 407 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 12 | Name: sect409r1 (B-409) | hasECExt: True tr.ECSize: 408 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 408 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 13 | Name: sect571k1 (K-571) | hasECExt: True tr.ECSize: 569 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 569 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 14 | Name: sect571r1 (B-571) | hasECExt: True tr.ECSize: 570 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 570 | minECSize: 256 | minECSizeExt: 162 . tr.Curve Id: 15 | Name: secp160k1 | hasECExt: True tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 16 | Name: secp160r1 | hasECExt: True tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 17 | Name: secp160r2 | hasECExt: True tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 160 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 18 | Name: secp192k1 | hasECExt: True tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 19 | Name: secp192r1 (P-192) | hasECExt: True tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 192 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 20 | Name: secp224k1 | hasECExt: True tr.ECSize: 224 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 224 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 21 | Name: secp224r1 (P-224) | hasECExt: True tr.ECSize: 224 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 224 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 22 | Name: secp256k1 | hasECExt: True tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 160 . tr.Curve Id: 23 | Name: secp256r1 (P-256) | hasECExt: True tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 160 tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 160

.tb.Curve Id: 24 => tr == null

2. The following is the output when sending "supported curves" extension with only one curve included each time:

[elliptic curve enumeration, version=TLSv1.2, 3 suite(s)] .tb.Curve Id: 1 => tr == null .tb.Curve Id: 2 => tr == null .tb.Curve Id: 3 => tr == null .tb.Curve Id: 4 => tr == null .tb.Curve Id: 5 => tr == null .tb.Curve Id: 6 => tr == null .tb.Curve Id: 7 => tr == null .tb.Curve Id: 8 => tr == null .tb.Curve Id: 9 => tr == null .tb.Curve Id: 10 => tr == null .tb.Curve Id: 11 => tr == null .tb.Curve Id: 12 => tr == null .tb.Curve Id: 13 => tr == null .tb.Curve Id: 14 => tr == null .tb.Curve Id: 15 => tr == null .tb.Curve Id: 16 => tr == null .tb.Curve Id: 17 => tr == null .tb.Curve Id: 18 => tr == null .tb.Curve Id: 19 => tr == null .tb.Curve Id: 20 => tr == null .tb.Curve Id: 21 => tr == null .tb.Curve Id: 22 => tr == null . tr.Curve Id: 23 | Name: secp256r1 (P-256) | hasECExt: True tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 162 tr.ECSize: 256 | minECSize: 256 | minECSizeExt: 162 .tb.Curve Id: 24 => tr == null .tb.Curve Id: 25 => tr == null .tb.Curve Id: 26 => tr == null .tb.Curve Id: 27 => tr == null .tb.Curve Id: 28 => tr == null .tb.Curve Id: 29 => tr == null

.tb.Curve Id: 30 => tr == null

FYI, the following is the diff for what I changed to the codes of TestSSLServer to send "supported curves" extension with only one curve each time:

------------------------------------------------------------------------------------------
477,483c477,487
<           foreach (int id in SSLCurve.ALL.Keys) {
<               AddToSet(rec, id);
<           }
<           while (rec.Count > 0) {
<               tb.SupportedCurves = SetToArray(rec);
<               SSLTestResult tr = DoConnect();
<               if (tr == null) {
---
>           //foreach (int id in SSLCurve.ALL.Keys) {
>           //  AddToSet(rec, id);
>           //}
>           for(int i = 1; i <= 30; i++) {
>               AddToSet(rec, i);
>                                 while (rec.Count > 0) {
>                                     tb.SupportedCurves = SetToArray(rec);
>                                     SSLTestResult tr = DoConnect();
>                                     if (tr == null) {
>                   Console.WriteLine("tb.Curve Id: {0} => tr == null", tb.SupportedCurves[0]);
>                   rec.Remove(tb.SupportedCurves[0]);
485,487c489,492
<               }
<               SSLCurve sc = tr.Curve;
<               if (sc == null) {
---
>                                     }
>                                     SSLCurve sc = tr.Curve;
>                                     if (sc == null) {
>                   Console.WriteLine("tb.Curve Id: {0} => sc == null", tb.SupportedCurves[0]);
489,490c494,496
<               }
<               if (!rec.ContainsKey(sc.Id)) {
---
>                                     }
>                                     if (!rec.ContainsKey(sc.Id)) {
>                   Console.WriteLine("rec.ContainsKey({0}) : {1}", sc.Id, tb.SupportedCurves[0]);
492,495c498,501
<               }
<               rec.Remove(sc.Id);
<           }
< 
---
>                                     }
>                                     rec.Remove(sc.Id);
>                                 }
>                         }
587,591c593,597
<               if (verbose) {
<                   Console.WriteLine();
<                   Console.WriteLine("[hello received]");
<               }
<               return true;
---
>                             if (verbose) {
>                                 Console.WriteLine();
>                                 Console.WriteLine("[hello received]");
>                             }
>                             return true;
592a599
>           //}
633a641,643
>                   Console.WriteLine();
>                   Console.WriteLine("tr.Curve Id: {0} | Name: {1} | hasECExt: {2}", tr.Curve.Id, tr.Curve.Name, hasECExt);
>                   Console.WriteLine("tr.ECSize: {0} | minECSize: {1} | minECSizeExt: {2}", tr.ECSize, minECSize, minECSizeExt);
638a649
>                   Console.WriteLine("tr.ECSize: {0} | minECSize: {1} | minECSizeExt: {2}", tr.ECSize, minECSize, minECSizeExt);
------------------------------------------------------------------------------------------

[Uploading FullTest.zip…]()