Skip to content

Commit 1152a65

Browse files
committed
Merge branch 'master' into 3.0.0
2 parents c975a97 + 8f0c296 commit 1152a65

7 files changed

Lines changed: 92 additions & 84 deletions

File tree

docs/server-setup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ the host keys are modified. **Note**: saving key files in PEM format requires t
3737
supporting artifacts be available in the code's classpath.
3838

3939
* `HostKeyCertificateProvider` - used for OpenSSH public-key certificate authentication system
40-
as defined in [this document](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)
40+
as defined in [this document](https://datatracker.ietf.org/doc/draft-miller-ssh-cert/)
4141

4242
* `ShellFactory` - That's the part one usually has to write to customize the SSHD server. The shell factory will
4343
be used to create a new shell each time a user logs in and wants to run an interactive shell. SSHD provides a simple

docs/standards.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
* [OpenSSH support for U2F/FIDO security keys](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.u2f)
3838
* **Note:** the server side supports these keys by default. The client side requires specific initialization
39-
* [OpenSSH public-key certificate authentication system for use by SSH](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)
39+
* [OpenSSH public-key certificate authentication system for use by SSH](https://datatracker.ietf.org/doc/draft-miller-ssh-cert/)
4040
* [OpenSSH 1.9 transport: strict key exchange extension](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL)
4141
* [(Some) OpenSSH SFTP extensions](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL)
4242

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@
103103
<logback.version>1.2.13</logback.version>
104104
<spring.version>5.3.39</spring.version>
105105
<!-- NOTE: upgrading jGit to 6.x requires Java 11 -->
106-
<jgit.version>5.13.3.202401111512-r</jgit.version>
106+
<jgit.version>5.13.5.202508271544-r</jgit.version>
107107
<!-- mockito 5.0 requires Java 11. -->
108108
<mockito.version>4.11.0</mockito.version>
109109
<testcontainers.version>1.21.3</testcontainers.version>
110110
<grpc.version>1.75.0</grpc.version> <!-- Used only in tests -->
111111

112112
<maven.archiver.version>3.6.4</maven.archiver.version>
113-
<plexus.archiver.version>4.10.1</plexus.archiver.version>
113+
<plexus.archiver.version>4.10.2</plexus.archiver.version>
114114
<!-- See https://pmd.github.io/ for available latest version -->
115115
<pmd.version>7.17.0</pmd.version>
116116
<japicmp.version>0.23.1</japicmp.version>

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

Lines changed: 44 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;
@@ -51,85 +54,19 @@
5154
import org.apache.sshd.common.util.GenericUtils;
5255
import org.apache.sshd.common.util.NumberUtils;
5356
import org.apache.sshd.common.util.ValidateUtils;
57+
import org.apache.sshd.common.util.security.SecurityUtils;
5458

5559
/**
5660
* Utilities for working with elliptic curves.
5761
*
5862
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
5963
*/
6064
public enum ECCurves implements KeyTypeIndicator, KeySizeIndicator, NamedResource, OptionalFeature {
61-
nistp256(Constants.NISTP256, new int[] { 1, 2, 840, 10045, 3, 1, 7 },
62-
new ECParameterSpec(
63-
new EllipticCurve(
64-
new ECFieldFp(
65-
new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
66-
new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
67-
new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
68-
new ECPoint(
69-
new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
70-
new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
71-
new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
72-
1),
73-
32,
74-
BuiltinDigests.sha256),
75-
nistp384(Constants.NISTP384, new int[] { 1, 3, 132, 0, 34 },
76-
new ECParameterSpec(
77-
new EllipticCurve(
78-
new ECFieldFp(
79-
new BigInteger(
80-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
81-
16)),
82-
new BigInteger(
83-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
84-
16),
85-
new BigInteger(
86-
"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
87-
16)),
88-
new ECPoint(
89-
new BigInteger(
90-
"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
91-
16),
92-
new BigInteger(
93-
"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
94-
16)),
95-
new BigInteger(
96-
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
97-
16),
98-
1),
99-
48,
100-
BuiltinDigests.sha384),
101-
nistp521(Constants.NISTP521, new int[] { 1, 3, 132, 0, 35 },
102-
new ECParameterSpec(
103-
new EllipticCurve(
104-
new ECFieldFp(
105-
new BigInteger(
106-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
107-
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
108-
16)),
109-
new BigInteger(
110-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
111-
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
112-
16),
113-
new BigInteger(
114-
"0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
115-
+ "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
116-
16)),
117-
new ECPoint(
118-
new BigInteger(
119-
"00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
120-
+ "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
121-
16),
122-
new BigInteger(
123-
"011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
124-
+ "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
125-
16)),
126-
new BigInteger(
127-
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
128-
+ "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
129-
16),
130-
1),
131-
66,
132-
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);
13370

13471
/**
13572
* A {@link Set} of all the known curves
@@ -159,25 +96,42 @@ public enum ECCurves implements KeyTypeIndicator, KeySizeIndicator, NamedResourc
15996
.collect(Collectors.toList()));
16097

16198
private final String name;
99+
private final String secName;
162100
private final String keyType;
163101
private final String oidString;
164102
private final List<Integer> oidValue;
165-
private final ECParameterSpec params;
166-
private final int keySize;
167103
private final int numOctets;
168104
private final DigestFactory digestFactory;
169105

170-
ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
106+
private ECParameterSpec params;
107+
private volatile int keySize = -1;
108+
109+
ECCurves(String name, String secName, int[] oid, int numOctets, DigestFactory digestFactory) {
171110
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
111+
this.secName = ValidateUtils.checkNotNullAndNotEmpty(secName, "No SEC curve name");
172112
this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
173113
this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
174114
this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
175-
this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
176-
this.keySize = getCurveSize(params);
177115
this.numOctets = numOctets;
178116
this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
179117
}
180118

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

204158
public final ECParameterSpec getParameters() {
205-
return params;
159+
synchronized (this) {
160+
if (params == null) {
161+
params = ValidateUtils.checkNotNull(getParams(secName), "No EC params for %s", name);
162+
}
163+
return params;
164+
}
206165
}
207166

208167
@Override
209168
public final int getKeySize() {
210-
return keySize;
169+
int sz = keySize;
170+
if (sz < 0) {
171+
sz = getCurveSize(getParameters());
172+
keySize = sz;
173+
}
174+
return sz;
211175
}
212176

213177
/**

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.apache.sshd.common.util.security;
2121

22+
import java.security.AlgorithmParameters;
2223
import java.security.GeneralSecurityException;
2324
import java.security.KeyFactory;
2425
import java.security.KeyPairGenerator;
@@ -40,6 +41,10 @@
4041
*/
4142
public interface SecurityEntityFactory {
4243

44+
default AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
45+
throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
46+
}
47+
4348
default CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
4449
throw new NoSuchAlgorithmException("Algorithm '" + algorithm + "' not supported (default)");
4550
}
@@ -88,6 +93,11 @@ public Named(String name) {
8893
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "Security provider name must not be empty");
8994
}
9095

96+
@Override
97+
public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
98+
return AlgorithmParameters.getInstance(algorithm, name);
99+
}
100+
91101
@Override
92102
public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
93103
return CertificateFactory.getInstance(algorithm, name);
@@ -142,6 +152,11 @@ public ByProvider(Provider name) {
142152
this.provider = ValidateUtils.checkNotNull(name, "Security provider must not be null");
143153
}
144154

155+
@Override
156+
public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
157+
return AlgorithmParameters.getInstance(algorithm, provider);
158+
}
159+
145160
@Override
146161
public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
147162
return CertificateFactory.getInstance(algorithm, provider);
@@ -192,6 +207,11 @@ enum Default implements SecurityEntityFactory {
192207

193208
INSTANCE;
194209

210+
@Override
211+
public AlgorithmParameters createAlgorithmParameters(String algorithm) throws GeneralSecurityException {
212+
return AlgorithmParameters.getInstance(algorithm);
213+
}
214+
195215
@Override
196216
public CertificateFactory createCertificateFactory(String algorithm) throws GeneralSecurityException {
197217
return CertificateFactory.getInstance(algorithm);

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.KeyFactory;
@@ -712,6 +713,13 @@ public static SecurityEntityFactory getSecurityEntityProvider(
712713
return registrar.getFactory();
713714
}
714715

716+
public static AlgorithmParameters getAlgorithmParameters(String algorithm) throws GeneralSecurityException {
717+
SecurityEntityFactory factory
718+
= resolveSecurityEntityFactory(AlgorithmParameters.class, algorithm,
719+
r -> r.isSecurityEntitySupported(AlgorithmParameters.class, algorithm));
720+
return factory.createAlgorithmParameters(algorithm);
721+
}
722+
715723
public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
716724
SecurityEntityFactory factory = resolveSecurityEntityFactory(KeyFactory.class, algorithm,
717725
r -> r.isKeyFactorySupported(algorithm));

sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.nio.file.Path;
2424
import java.security.GeneralSecurityException;
2525
import java.security.KeyPair;
26+
import java.security.KeyPairGenerator;
2627
import java.security.PrivateKey;
2728
import java.security.PublicKey;
2829
import java.security.interfaces.DSAPrivateKey;
@@ -32,6 +33,7 @@
3233
import java.security.interfaces.RSAPrivateKey;
3334
import java.security.interfaces.RSAPublicKey;
3435
import java.util.ArrayList;
36+
import java.util.Base64;
3537
import java.util.Collection;
3638
import java.util.List;
3739

@@ -45,6 +47,7 @@
4547
import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
4648
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
4749
import org.apache.sshd.common.util.GenericUtils;
50+
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
4851
import org.apache.sshd.common.util.io.resource.PathResource;
4952
import org.junit.jupiter.api.MethodOrderer.MethodName;
5053
import org.junit.jupiter.api.Tag;
@@ -118,6 +121,19 @@ void loadUnencryptedECPrivateKey() throws Exception {
118121
}
119122
}
120123

124+
@Test
125+
void x509EncodingEc() throws Exception {
126+
KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
127+
generator.initialize(521);
128+
KeyPair kp = generator.generateKeyPair();
129+
String enc = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
130+
ByteArrayBuffer buf = new ByteArrayBuffer();
131+
buf.putPublicKey(kp.getPublic());
132+
PublicKey readBack = buf.getPublicKey();
133+
assertTrue(KeyUtils.compareKeys(kp.getPublic(), readBack), "Public keys should be equal");
134+
assertEquals(enc, Base64.getEncoder().encodeToString(readBack.getEncoded()), readBack.getClass().getCanonicalName());
135+
}
136+
121137
private KeyPair testLoadECPrivateKey(String name) throws Exception {
122138
return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
123139
}

0 commit comments

Comments
 (0)