Skip to content

Commit 8f0c296

Browse files
committed
GH-830: Get EC curve parameters from the security provider
For EC public keys read from a ByteArrayBuffer, Bouncy Castle would generate X.509 encodings using as algorithm parameters not the named curve OID but the raw curve parameters. This was caused by our using manually constructed ECParameterSpecs with the values given in [1]. SunEC has code matching raw parameters back to known curve names, and then using the OID, but Bouncy Castle does not. So instead of constructing the ECParameterSpecs explicitly let the security provider provide them. With that approach, Bouncy Castle can produce a spec that also transports the OID, and then uses the OID in the X.509 encoding.
1 parent 74be99e commit 8f0c296

3 files changed

Lines changed: 52 additions & 81 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
* [GH-807](https://github.com/apache/mina-sshd/issues/807) Handle "verified" flag for sk-* keys
3333
* [GH-809](https://github.com/apache/mina-sshd/pull/809) Fix server-side authentication for FIDO/U2F sk-* keys with flags in `authorized_keys`
34-
* [GH-827](https://github.com/apache/mina-sshd/pull/827) Don't fail on invalid `known_hosts` lines; log and skip them
34+
* [GH-827](https://github.com/apache/mina-sshd/issues/827) Don't fail on invalid `known_hosts` lines; log and skip them
35+
* [GH-830](https://github.com/apache/mina-sshd/issues/830) EC public keys: let Bouncy Castle generate X.509 encodings with the curve OID as algorithm parameter
3536

3637
## New Features
3738

sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java

Lines changed: 42 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
import java.io.StreamCorruptedException;
2626
import java.io.UncheckedIOException;
2727
import java.math.BigInteger;
28+
import java.security.AlgorithmParameters;
29+
import java.security.GeneralSecurityException;
2830
import java.security.interfaces.ECKey;
2931
import java.security.spec.ECField;
30-
import java.security.spec.ECFieldFp;
32+
import java.security.spec.ECGenParameterSpec;
3133
import java.security.spec.ECParameterSpec;
3234
import java.security.spec.ECPoint;
3335
import java.security.spec.EllipticCurve;
@@ -43,6 +45,7 @@
4345
import org.apache.sshd.common.NamedResource;
4446
import org.apache.sshd.common.OptionalFeature;
4547
import org.apache.sshd.common.config.keys.KeyEntryResolver;
48+
import org.apache.sshd.common.config.keys.KeyUtils;
4649
import org.apache.sshd.common.digest.BuiltinDigests;
4750
import org.apache.sshd.common.digest.Digest;
4851
import org.apache.sshd.common.digest.DigestFactory;
@@ -59,78 +62,11 @@
5962
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
6063
*/
6164
public enum ECCurves implements KeyTypeIndicator, KeySizeIndicator, NamedResource, OptionalFeature {
62-
nistp256(Constants.NISTP256, new int[] { 1, 2, 840, 10045, 3, 1, 7 },
63-
new ECParameterSpec(
64-
new EllipticCurve(
65-
new ECFieldFp(
66-
new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
67-
new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
68-
new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
69-
new ECPoint(
70-
new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
71-
new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
72-
new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
73-
1),
74-
32,
75-
BuiltinDigests.sha256),
76-
nistp384(Constants.NISTP384, new int[] { 1, 3, 132, 0, 34 },
77-
new ECParameterSpec(
78-
new EllipticCurve(
79-
new ECFieldFp(
80-
new BigInteger(
81-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
82-
16)),
83-
new BigInteger(
84-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
85-
16),
86-
new BigInteger(
87-
"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
88-
16)),
89-
new ECPoint(
90-
new BigInteger(
91-
"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
92-
16),
93-
new BigInteger(
94-
"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
95-
16)),
96-
new BigInteger(
97-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
98-
16),
99-
1),
100-
48,
101-
BuiltinDigests.sha384),
102-
nistp521(Constants.NISTP521, new int[] { 1, 3, 132, 0, 35 },
103-
new ECParameterSpec(
104-
new EllipticCurve(
105-
new ECFieldFp(
106-
new BigInteger(
107-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
108-
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
109-
16)),
110-
new BigInteger(
111-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
112-
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
113-
16),
114-
new BigInteger(
115-
"0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
116-
+ "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
117-
16)),
118-
new ECPoint(
119-
new BigInteger(
120-
"00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
121-
+ "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
122-
16),
123-
new BigInteger(
124-
"011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
125-
+ "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
126-
16)),
127-
new BigInteger(
128-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
129-
+ "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
130-
16),
131-
1),
132-
66,
133-
BuiltinDigests.sha512);
65+
66+
// See RFC 5656: https://datatracker.ietf.org/doc/html/rfc5656#section-10.1
67+
nistp256(Constants.NISTP256, "secp256r1", new int[] { 1, 2, 840, 10045, 3, 1, 7 }, 32, BuiltinDigests.sha256),
68+
nistp384(Constants.NISTP384, "secp384r1", new int[] { 1, 3, 132, 0, 34 }, 48, BuiltinDigests.sha384),
69+
nistp521(Constants.NISTP521, "secp521r1", new int[] { 1, 3, 132, 0, 35 }, 66, BuiltinDigests.sha512);
13470

13571
/**
13672
* A {@link Set} of all the known curves
@@ -160,25 +96,41 @@ public enum ECCurves implements KeyTypeIndicator, KeySizeIndicator, NamedResourc
16096
.collect(Collectors.toList()));
16197

16298
private final String name;
99+
private final String secName;
163100
private final String keyType;
164101
private final String oidString;
165102
private final List<Integer> oidValue;
166-
private final ECParameterSpec params;
167-
private final int keySize;
103+
private ECParameterSpec params;
104+
private volatile int keySize = -1;
168105
private final int numOctets;
169106
private final DigestFactory digestFactory;
170107

171-
ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
108+
ECCurves(String name, String secName, int[] oid, int numOctets, DigestFactory digestFactory) {
172109
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
110+
this.secName = ValidateUtils.checkNotNullAndNotEmpty(secName, "No SEC curve name");
173111
this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
174112
this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
175113
this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
176-
this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
177-
this.keySize = getCurveSize(params);
178114
this.numOctets = numOctets;
179115
this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
180116
}
181117

118+
private ECParameterSpec getParams(String secName) {
119+
try {
120+
AlgorithmParameters paramsFactory = SecurityUtils.getAlgorithmParameters(KeyUtils.EC_ALGORITHM);
121+
// Although ECGenParameterSpec exists since Java 1.5 the parameter names were documented only in Java 14
122+
// with JDK-8210755. But the names were available all the time and are also supported in Bouncy Castle.
123+
//
124+
// Note that the names must not be the NIST names but the SEC names.
125+
//
126+
// See also https://www.secg.org/sec2-v2.pdf for the name definitions and the exact numerical parameters.
127+
paramsFactory.init(new ECGenParameterSpec(secName));
128+
return paramsFactory.getParameterSpec(ECParameterSpec.class);
129+
} catch (GeneralSecurityException e) {
130+
return null;
131+
}
132+
}
133+
182134
@Override // The curve's standard name
183135
public final String getName() {
184136
return name;
@@ -203,12 +155,22 @@ public final boolean isSupported() {
203155
}
204156

205157
public final ECParameterSpec getParameters() {
206-
return params;
158+
synchronized (this) {
159+
if (params == null) {
160+
params = ValidateUtils.checkNotNull(getParams(secName), "No EC params for %s", name);
161+
}
162+
return params;
163+
}
207164
}
208165

209166
@Override
210167
public final int getKeySize() {
211-
return keySize;
168+
int sz = keySize;
169+
if (sz < 0) {
170+
sz = getCurveSize(getParameters());
171+
keySize = sz;
172+
}
173+
return sz;
212174
}
213175

214176
/**

sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.InputStream;
2323
import java.math.BigInteger;
2424
import java.nio.file.Path;
25+
import java.security.AlgorithmParameters;
2526
import java.security.GeneralSecurityException;
2627
import java.security.InvalidKeyException;
2728
import java.security.Key;
@@ -846,6 +847,13 @@ public static <T> SecurityEntityFactory<T> createSecurityEntityFactory(
846847
}
847848
}
848849

850+
public static AlgorithmParameters getAlgorithmParameters(String algorithm) throws GeneralSecurityException {
851+
SecurityEntityFactory<AlgorithmParameters> factory
852+
= resolveSecurityEntityFactory(AlgorithmParameters.class, algorithm,
853+
r -> r.isSecurityEntitySupported(AlgorithmParameters.class, algorithm));
854+
return factory.getInstance(algorithm);
855+
}
856+
849857
public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
850858
SecurityEntityFactory<KeyFactory> factory
851859
= resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));

0 commit comments

Comments
 (0)