Skip to content

Commit 29e36fc

Browse files
authored
Add support for more named curves (#55179) (#55538)
We implicitly only supported the prime256v1 ( aka secp256r1 ) curve for the EC keys we read as PEM files to be used in any SSL Context. We would not fail when trying to read a key pair using a different curve but we would silently assume that it was using `secp256r1` which would lead to strange TLS handshake issues if the curve was actually another one. This commit fixes that behavior in that it supports parsing EC keys that use any of the named curves defined in rfc5915 and rfc5480 making no assumptions about whether the security provider in use supports them (JDK8 and higher support all the curves defined in rfc5480).
1 parent 61f7c19 commit 29e36fc

File tree

19 files changed

+316
-16
lines changed

19 files changed

+316
-16
lines changed

libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,12 @@ private static ECPrivateKeySpec parseEcDer(byte[] keyBytes) throws IOException,
510510
parser.readAsn1Object().getInteger(); // version
511511
String keyHex = parser.readAsn1Object().getString();
512512
BigInteger privateKeyInt = new BigInteger(keyHex, 16);
513+
DerParser.Asn1Object choice = parser.readAsn1Object();
514+
parser = choice.getParser();
515+
String namedCurve = getEcCurveNameFromOid(parser.readAsn1Object().getOid());
513516
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
514-
AlgorithmParameterSpec prime256v1ParamSpec = new ECGenParameterSpec("secp256r1");
515-
keyPairGenerator.initialize(prime256v1ParamSpec);
517+
AlgorithmParameterSpec algorithmParameterSpec = new ECGenParameterSpec(namedCurve);
518+
keyPairGenerator.initialize(algorithmParameterSpec);
516519
ECParameterSpec parameterSpec = ((ECKey) keyPairGenerator.generateKeyPair().getPrivate()).getParams();
517520
return new ECPrivateKeySpec(privateKeyInt, parameterSpec);
518521
}
@@ -610,4 +613,43 @@ private static MessageDigest messageDigest(String digestAlgorithm) {
610613
throw new SslConfigException("unexpected exception creating MessageDigest instance for [" + digestAlgorithm + "]", e);
611614
}
612615
}
616+
617+
private static String getEcCurveNameFromOid(String oidString) throws GeneralSecurityException {
618+
switch (oidString) {
619+
// see https://tools.ietf.org/html/rfc5480#section-2.1.1.1
620+
case "1.2.840.10045.3.1":
621+
return "secp192r1";
622+
case "1.3.132.0.1":
623+
return "sect163k1";
624+
case "1.3.132.0.15":
625+
return "sect163r2";
626+
case "1.3.132.0.33":
627+
return "secp224r1";
628+
case "1.3.132.0.26":
629+
return "sect233k1";
630+
case "1.3.132.0.27":
631+
return "sect233r1";
632+
case "1.2.840.10045.3.1.7":
633+
return "secp256r1";
634+
case "1.3.132.0.16":
635+
return "sect283k1";
636+
case "1.3.132.0.17":
637+
return "sect283r1";
638+
case "1.3.132.0.34":
639+
return "secp384r1";
640+
case "1.3.132.0.36":
641+
return "sect409k1";
642+
case "1.3.132.0.37":
643+
return "sect409r1";
644+
case "1.3.132.0.35":
645+
return "secp521r1";
646+
case "1.3.132.0.38":
647+
return "sect571k1";
648+
case "1.3.132.0.39":
649+
return "sect571r1";
650+
}
651+
throw new GeneralSecurityException("Error parsing EC named curve identifier. Named curve with OID: " + oidString
652+
+ " is not supported");
653+
}
654+
613655
}

libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemUtilsTests.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.security.Key;
2828
import java.security.KeyStore;
2929
import java.security.PrivateKey;
30+
import java.security.interfaces.ECPrivateKey;
31+
import java.security.spec.ECParameterSpec;
3032
import java.util.function.Supplier;
3133

3234
import static org.hamcrest.Matchers.equalTo;
@@ -53,7 +55,6 @@ public void testReadPKCS8RsaKeyWithBagAttrs() throws Exception {
5355
assertThat(key, notNullValue());
5456
assertThat(key, instanceOf(PrivateKey.class));
5557
PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/testnode_with_bagattrs.pem"), EMPTY_PASSWORD);
56-
assertThat(privateKey, notNullValue());
5758
assertThat(privateKey, equalTo(key));
5859
}
5960

@@ -66,6 +67,15 @@ public void testReadPKCS8DsaKey() throws Exception {
6667
assertThat(privateKey, equalTo(key));
6768
}
6869

70+
public void testReadEcKeyCurves() throws Exception {
71+
String curve = randomFrom("secp256r1", "secp384r1", "secp521r1");
72+
PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/private_" + curve + ".pem"), ""::toCharArray);
73+
assertThat(privateKey, instanceOf(ECPrivateKey.class));
74+
ECParameterSpec parameterSpec = ((ECPrivateKey) privateKey).getParams();
75+
// This is brittle but we can't access sun.security.util.NamedCurve
76+
assertThat(parameterSpec.toString(), containsString(curve));
77+
}
78+
6979
public void testReadPKCS8EcKey() throws Exception {
7080
Key key = getKeyFromKeystore("EC");
7181
assertThat(key, notNullValue());

libs/ssl-config/src/test/resources/certs/pem-utils/README.asciidoc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,29 @@ openssl x509 -req -in n2.c2.csr -extensions SAN -CA ca.crt -CAkey ca.key -CAcrea
147147
-extfile <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=otherName.1:2.5.4.3;UTF8:node2.cluster2"))\
148148
-out n2.c2.crt -days 10000
149149
------
150+
151+
== Generate EC keys using various curves for testing
152+
153+
[source,shell]
154+
-------
155+
openssl ecparam -list_curves
156+
-------
157+
158+
will list all the available curves in a given system.
159+
For the purposes of the tests here, the following curves were used to generate ec keys named accordingly:
160+
161+
[source,shell]
162+
-------
163+
openssl ecparam -name secp256r1 -genkey -out private_secp256r1.pem
164+
openssl ecparam -name secp384r1 -genkey -out private_secp384r1.pem
165+
openssl ecparam -name secp521r1 -genkey -out private_secp521r1.pem
166+
-------
167+
168+
and the respective certificates
169+
170+
[source,shell]
171+
-------
172+
openssl req -x509 -extensions v3_req -key private_secp256r1.pem -out certificate_secp256r1.pem -days 1460 -config openssl_config.cnf
173+
openssl req -x509 -extensions v3_req -key private_secp384r1.pem -out certificate_secp384r1.pem -days 1460 -config openssl_config.cnf
174+
openssl req -x509 -extensions v3_req -key private_secp521r1.pem -out certificate_secp521r1.pem -days 1460 -config openssl_config.cnf
175+
-------
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICBzCCAaygAwIBAgIUAhfs6i7USsFCrKcjhaYmjOOekd8wCgYIKoZIzj0EAwIw
3+
IjEgMB4GA1UEAxMXRWxhc3RpY3NlYXJjaCBUZXN0IE5vZGUwHhcNMjAwNDE0MTg0
4+
OTA0WhcNMjQwNDEzMTg0OTA0WjAiMSAwHgYDVQQDExdFbGFzdGljc2VhcmNoIFRl
5+
c3QgTm9kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABN7Ioe2JD2Ssbk0pF19W
6+
iwO/leZtIcCIZP9btPMGhq0r4e6va/qCYFRJoAMEKv49RQwL23MfBK1Djm63pl7z
7+
33Cjgb8wgbwwCQYDVR0TBAIwADAdBgNVHQ4EFgQUZGVhl0jaavD09XqqAZq+QB+q
8+
VzMwgY8GA1UdEQSBhzCBhIIJbG9jYWxob3N0ghVsb2NhbGhvc3QubG9jYWxkb21h
9+
aW6CCmxvY2FsaG9zdDSCF2xvY2FsaG9zdDQubG9jYWxkb21haW40ggpsb2NhbGhv
10+
c3Q2ghdsb2NhbGhvc3Q2LmxvY2FsZG9tYWluNocEfwAAAYcQAAAAAAAAAAAAAAAA
11+
AAAAATAKBggqhkjOPQQDAgNJADBGAiEA5rkkz7V8zFb9ME4b3SiBqFQaXGnLNzz5
12+
UXmL31oevUUCIQCsL/qw/HKhBtojG9LnK5TezFCYauafDPsVqsxvj7F9UA==
13+
-----END CERTIFICATE-----
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICQzCCAcmgAwIBAgIUFBuqf8Y7xcDb5MvDH3/WKCaqZOwwCgYIKoZIzj0EAwIw
3+
IjEgMB4GA1UEAxMXRWxhc3RpY3NlYXJjaCBUZXN0IE5vZGUwHhcNMjAwNDE0MTg1
4+
MjE4WhcNMjQwNDEzMTg1MjE4WjAiMSAwHgYDVQQDExdFbGFzdGljc2VhcmNoIFRl
5+
c3QgTm9kZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABKLpoDYudvcmGfr1+aImIap7
6+
C1cC9SUBcI8EOWlogODMUM1DWcaWrQbbQzhUNpQFvX6A/I2SiME5WM2IC+lJX/W8
7+
fafcLzYF+Ts2Eftmdi9usBsQz+JEGTPcgRNyM/N3FaOBvzCBvDAJBgNVHRMEAjAA
8+
MB0GA1UdDgQWBBTuCqvozIlpHH5kLc3BfsT1bRqpHDCBjwYDVR0RBIGHMIGEggls
9+
b2NhbGhvc3SCFWxvY2FsaG9zdC5sb2NhbGRvbWFpboIKbG9jYWxob3N0NIIXbG9j
10+
YWxob3N0NC5sb2NhbGRvbWFpbjSCCmxvY2FsaG9zdDaCF2xvY2FsaG9zdDYubG9j
11+
YWxkb21haW42hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMAoGCCqGSM49BAMCA2gA
12+
MGUCMQDtmO2fQY1vVD58fFHsAt0LoStzrhB22SkcfKtTVNlrHkTX8SXjToqKKbxX
13+
AMgUCNoCMFSn7lc3V7xycDx+P1icdb+jLVoFl7G1Ki17B1z6W8JlZRJBsyEiu6qC
14+
UxZU5NBdww==
15+
-----END CERTIFICATE-----
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICjjCCAe+gAwIBAgIUR5YlaSjZ7BE/bCe5f2966kG8+cowCgYIKoZIzj0EAwIw
3+
IjEgMB4GA1UEAxMXRWxhc3RpY3NlYXJjaCBUZXN0IE5vZGUwHhcNMjAwNDE0MTg1
4+
MjM4WhcNMjQwNDEzMTg1MjM4WjAiMSAwHgYDVQQDExdFbGFzdGljc2VhcmNoIFRl
5+
c3QgTm9kZTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAC/v/jT1EwJzFyVjSYw8
6+
H/Ix6Ty9KjTJ+duN1qc9ByGg2YoJw5Z179mAPoDp7LalGCawplhs38J45rqh7pbN
7+
MI+1AaAilKSJiuIzByPlkKjxWOX1sYaxmBY4Kc0UOKpqFfY70fBzhIi8M+9t3eaB
8+
TWoLbIghGkDHG6icTCUawesuTI7/o4G/MIG8MAkGA1UdEwQCMAAwHQYDVR0OBBYE
9+
FNIirnFLQRx8t9uMd3D5Cux+/uSzMIGPBgNVHREEgYcwgYSCCWxvY2FsaG9zdIIV
10+
bG9jYWxob3N0LmxvY2FsZG9tYWluggpsb2NhbGhvc3Q0ghdsb2NhbGhvc3Q0Lmxv
11+
Y2FsZG9tYWluNIIKbG9jYWxob3N0NoIXbG9jYWxob3N0Ni5sb2NhbGRvbWFpbjaH
12+
BH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDgYwAMIGIAkIAo+T4
13+
wkgf9OwzupXYQc8ftQydvucF29sK1OdJDnJHN/oBFtYdo4ZOMar8PzJZ3KtiOETo
14+
IInuL8YE6kO9aTaQOUwCQgDfs3/nnEITC9OzpYpHWDp54phcrKgbHUDEUPn8CPU1
15+
aH8dJ/TVeSiYkt7dAeqklOP790HfHjS+rTAyMFn7uq4pkw==
16+
-----END CERTIFICATE-----
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN EC PARAMETERS-----
2+
BggqhkjOPQMBBw==
3+
-----END EC PARAMETERS-----
4+
-----BEGIN EC PRIVATE KEY-----
5+
MHcCAQEEIMdU2MBFYjUeThgqXbSrVByV+rMmsKKe6qzwBjgBwgHXoAoGCCqGSM49
6+
AwEHoUQDQgAE3sih7YkPZKxuTSkXX1aLA7+V5m0hwIhk/1u08waGrSvh7q9r+oJg
7+
VEmgAwQq/j1FDAvbcx8ErUOObremXvPfcA==
8+
-----END EC PRIVATE KEY-----
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN EC PARAMETERS-----
2+
BgUrgQQAIg==
3+
-----END EC PARAMETERS-----
4+
-----BEGIN EC PRIVATE KEY-----
5+
MIGkAgEBBDA6lA/V9jd1eZJrD+fkOJMNWDU0xT5aRyUJxrNdIwMWFu1wvswHLvF8
6+
kZELRUMx3QmgBwYFK4EEACKhZANiAASi6aA2Lnb3Jhn69fmiJiGqewtXAvUlAXCP
7+
BDlpaIDgzFDNQ1nGlq0G20M4VDaUBb1+gPyNkojBOVjNiAvpSV/1vH2n3C82Bfk7
8+
NhH7ZnYvbrAbEM/iRBkz3IETcjPzdxU=
9+
-----END EC PRIVATE KEY-----
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN EC PARAMETERS-----
2+
BgUrgQQAIw==
3+
-----END EC PARAMETERS-----
4+
-----BEGIN EC PRIVATE KEY-----
5+
MIHcAgEBBEIANfC2QUp9OWMWk+1+7i1S3hhg1sXiE2Ysv2lTSV3Jct547FJRoNnl
6+
kJEdojfPbWNlP/uxtoWdIY0T/c+K8ErSkPGgBwYFK4EEACOhgYkDgYYABAAv7/40
7+
9RMCcxclY0mMPB/yMek8vSo0yfnbjdanPQchoNmKCcOWde/ZgD6A6ey2pRgmsKZY
8+
bN/CeOa6oe6WzTCPtQGgIpSkiYriMwcj5ZCo8Vjl9bGGsZgWOCnNFDiqahX2O9Hw
9+
c4SIvDPvbd3mgU1qC2yIIRpAxxuonEwlGsHrLkyO/w==
10+
-----END EC PRIVATE KEY-----

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PemUtils.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,12 @@ private static ECPrivateKeySpec parseEcDer(byte[] keyBytes) throws IOException,
481481
parser.readAsn1Object().getInteger(); // version
482482
String keyHex = parser.readAsn1Object().getString();
483483
BigInteger privateKeyInt = new BigInteger(keyHex, 16);
484+
DerParser.Asn1Object choice = parser.readAsn1Object();
485+
parser = choice.getParser();
486+
String namedCurve = getEcCurveNameFromOid(parser.readAsn1Object().getOid());
484487
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
485-
AlgorithmParameterSpec prime256v1ParamSpec = new ECGenParameterSpec("secp256r1");
486-
keyPairGenerator.initialize(prime256v1ParamSpec);
488+
AlgorithmParameterSpec algorithmParameterSpec = new ECGenParameterSpec(namedCurve);
489+
keyPairGenerator.initialize(algorithmParameterSpec);
487490
ECParameterSpec parameterSpec = ((ECKey) keyPairGenerator.generateKeyPair().getPrivate()).getParams();
488491
return new ECPrivateKeySpec(privateKeyInt, parameterSpec);
489492
}
@@ -555,7 +558,45 @@ private static String getKeyAlgorithmIdentifier(byte[] keyBytes) throws IOExcept
555558
case "1.2.840.10045.2.1":
556559
return "EC";
557560
}
558-
throw new GeneralSecurityException("Error parsing key algorithm identifier. Algorithm with OID: "+oidString+ " is not " +
561+
throw new GeneralSecurityException("Error parsing key algorithm identifier. Algorithm with OID: " + oidString + " is not " +
562+
"supported");
563+
}
564+
565+
private static String getEcCurveNameFromOid(String oidString) throws GeneralSecurityException {
566+
switch (oidString) {
567+
// see https://tools.ietf.org/html/rfc5480#section-2.1.1.1
568+
case "1.2.840.10045.3.1":
569+
return "secp192r1";
570+
case "1.3.132.0.1":
571+
return "sect163k1";
572+
case "1.3.132.0.15":
573+
return "sect163r2";
574+
case "1.3.132.0.33":
575+
return "secp224r1";
576+
case "1.3.132.0.26":
577+
return "sect233k1";
578+
case "1.3.132.0.27":
579+
return "sect233r1";
580+
case "1.2.840.10045.3.1.7":
581+
return "secp256r1";
582+
case "1.3.132.0.16":
583+
return "sect283k1";
584+
case "1.3.132.0.17":
585+
return "sect283r1";
586+
case "1.3.132.0.34":
587+
return "secp384r1";
588+
case "1.3.132.0.36":
589+
return "sect409k1";
590+
case "1.3.132.0.37":
591+
return "sect409r1";
592+
case "1.3.132.0.35":
593+
return "secp521r1";
594+
case "1.3.132.0.38":
595+
return "sect571k1";
596+
case "1.3.132.0.39":
597+
return "sect571r1";
598+
}
599+
throw new GeneralSecurityException("Error parsing EC named curve identifier. Named curve with OID: " + oidString + " is not " +
559600
"supported");
560601
}
561602
}

0 commit comments

Comments
 (0)