You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by tw...@apache.org on 2025/07/19 20:06:31 UTC
(mina-sshd) branch master updated (4c5b96887 -> bca964c72)
This is an automated email from the ASF dual-hosted git repository.
twolf pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
from 4c5b96887 #771 Avoid NoClassDefFoundError: net/i2p/crypto/eddsa/EdDSAPublicKey (#773)
new 591e44e4c Resolve JUnit warnings about private BeforeAll methods
new bca964c72 GH-767: Make SkED25519PublicKey work with arbitrary ed25519 keys
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
CHANGES.md | 6 +-
.../keys/impl/SkED25519PublicKeyEntryDecoder.java | 20 +++--
.../common/config/keys/u2f/SkED25519PublicKey.java | 17 +++--
.../keys/SkED25519BufferPublicKeyParser.java | 3 +-
.../util/security/eddsa/generic/EdDSAUtils.java | 89 ++++++++++++++++++++++
.../signature/KnownHostsCertificateTest.java | 2 +-
.../signature/OpenSshHostCertificateTest.java | 2 +-
.../server/auth/AuthorizedKeysCertificateTest.java | 2 +-
8 files changed, 121 insertions(+), 20 deletions(-)
create mode 100644 sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/EdDSAUtils.java
(mina-sshd) 02/02: GH-767: Make SkED25519PublicKey work with arbitrary ed25519 keys
Posted by tw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
twolf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit bca964c7265c27e415d949e6a7764899012647f4
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Mon Jul 14 21:32:17 2025 +0200
GH-767: Make SkED25519PublicKey work with arbitrary ed25519 keys
Remove the hard dependency on net.i2p.
---
CHANGES.md | 6 +-
.../keys/impl/SkED25519PublicKeyEntryDecoder.java | 20 +++--
.../common/config/keys/u2f/SkED25519PublicKey.java | 17 +++--
.../keys/SkED25519BufferPublicKeyParser.java | 3 +-
.../util/security/eddsa/generic/EdDSAUtils.java | 89 ++++++++++++++++++++++
5 files changed, 118 insertions(+), 17 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index a99f71630..99efaf1e0 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -39,6 +39,8 @@
* [GH-727](https://github.com/apache/mina-sshd/issues/727) Supply default port 22 for proxy jump hosts for which there is no `HostConfigEntry`
* [GH-733](https://github.com/apache/mina-sshd/issues/733) Fix `SftpRemotePathChannel.transferTo()` (avoid NPE)
* [GH-751](https://github.com/apache/mina-sshd/issues/751) Fix SFTP v3 "long name" if SFTP server uses an `SftpFileSystem` to another server
+* [GH-767](https://github.com/apache/mina-sshd/issues/767) Remove dependency on net.i2p.crypto in `SkED25519PublicKey`
+* [GH-771](https://github.com/apache/mina-sshd/issues/771) Remove dependency on net.i2p.crypto in `EdDSAPuttyKeyDecoder`
* [GH-774](https://github.com/apache/mina-sshd/issues/774) Fix `WritePendingException` in SFTP file copy
@@ -55,8 +57,8 @@
## Potential Compatibility Issues
-Client-side KEX: we've changed the default of the setting `CoreModuleProperties.ABORT_ON_INVALID_CERTIFICATE` from `false` to `true`.
-A client will newly abort an SSH connection if the server presents an invalid OpenSSH host certificate as host key.
+* Client-side KEX: we've changed the default of the setting `CoreModuleProperties.ABORT_ON_INVALID_CERTIFICATE` from `false` to `true`. A client will newly abort an SSH connection if the server presents an invalid OpenSSH host certificate as host key.
+* [GH-767](https://github.com/apache/mina-sshd/issues/767) and [GH-771](https://github.com/apache/mina-sshd/issues/771) cause API changes in classes `SkED25519PublicKey` and `EdDSAPuttyKeyDecoder`. Both changes are unlikely to be noticed in user code since user code normally doesn't need to use either class.
## Major Code Re-factoring
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/SkED25519PublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/SkED25519PublicKeyEntryDecoder.java
index 550e1b925..1380a2b41 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/SkED25519PublicKeyEntryDecoder.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/SkED25519PublicKeyEntryDecoder.java
@@ -23,21 +23,23 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
import org.apache.sshd.common.config.keys.KeyEntryResolver;
import org.apache.sshd.common.config.keys.u2f.SkED25519PublicKey;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.SessionContext;
-import org.apache.sshd.common.util.security.eddsa.Ed25519PublicKeyDecoder;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.security.eddsa.generic.EdDSAUtils;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -63,10 +65,10 @@ public class SkED25519PublicKeyEntryDecoder extends AbstractPublicKeyEntryDecode
}
boolean noTouchRequired = parseBooleanHeader(headers, NO_TOUCH_REQUIRED_HEADER, false);
- EdDSAPublicKey edDSAPublicKey
- = Ed25519PublicKeyDecoder.INSTANCE.decodePublicKey(session, KeyPairProvider.SSH_ED25519, keyData, headers);
+ PublicKey pk = SecurityUtils.getEDDSAPublicKeyEntryDecoder().decodePublicKey(session, KeyPairProvider.SSH_ED25519,
+ keyData, headers);
String appName = KeyEntryResolver.decodeString(keyData, MAX_APP_NAME_LENGTH);
- return new SkED25519PublicKey(appName, noTouchRequired, edDSAPublicKey);
+ return new SkED25519PublicKey(appName, noTouchRequired, pk);
}
@Override
@@ -82,8 +84,12 @@ public class SkED25519PublicKeyEntryDecoder extends AbstractPublicKeyEntryDecode
public String encodePublicKey(OutputStream s, SkED25519PublicKey key) throws IOException {
Objects.requireNonNull(key, "No public key provided");
KeyEntryResolver.encodeString(s, KEY_TYPE);
- byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(key.getDelegatePublicKey());
- KeyEntryResolver.writeRLEBytes(s, seed);
+ try {
+ byte[] keyData = EdDSAUtils.getBytes(key.getDelegatePublicKey());
+ KeyEntryResolver.writeRLEBytes(s, keyData);
+ } catch (InvalidKeyException e) {
+ throw new IOException(e.getMessage(), e);
+ }
KeyEntryResolver.encodeString(s, key.getAppName());
return KEY_TYPE;
}
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/u2f/SkED25519PublicKey.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/u2f/SkED25519PublicKey.java
index ebac6a583..a6f525e66 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/u2f/SkED25519PublicKey.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/u2f/SkED25519PublicKey.java
@@ -18,24 +18,29 @@
*/
package org.apache.sshd.common.config.keys.u2f;
+import java.security.PublicKey;
import java.util.Objects;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
-public class SkED25519PublicKey implements SecurityKeyPublicKey<EdDSAPublicKey> {
+public class SkED25519PublicKey implements SecurityKeyPublicKey<PublicKey> {
public static final String ALGORITHM = "ED25519-SK";
- private static final long serialVersionUID = 4587115316266869640L;
+ private static final long serialVersionUID = -3947776805731312115L;
private final String appName;
private final boolean noTouchRequired;
- private final EdDSAPublicKey delegatePublicKey;
+ private final PublicKey delegatePublicKey;
- public SkED25519PublicKey(String appName, boolean noTouchRequired, EdDSAPublicKey delegatePublicKey) {
+ public SkED25519PublicKey(String appName, boolean noTouchRequired, PublicKey delegatePublicKey) {
this.appName = appName;
this.noTouchRequired = noTouchRequired;
+ ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(KeyUtils.getKeyType(delegatePublicKey)),
+ "Key is not an ed25519 key");
this.delegatePublicKey = delegatePublicKey;
}
@@ -70,7 +75,7 @@ public class SkED25519PublicKey implements SecurityKeyPublicKey<EdDSAPublicKey>
}
@Override
- public EdDSAPublicKey getDelegatePublicKey() {
+ public PublicKey getDelegatePublicKey() {
return delegatePublicKey;
}
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/SkED25519BufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/SkED25519BufferPublicKeyParser.java
index c905e7727..217258de8 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/SkED25519BufferPublicKeyParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/SkED25519BufferPublicKeyParser.java
@@ -22,7 +22,6 @@ package org.apache.sshd.common.util.buffer.keys;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
import org.apache.sshd.common.config.keys.u2f.SkED25519PublicKey;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
@@ -44,6 +43,6 @@ public class SkED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParse
// the end
PublicKey publicKey = ED25519BufferPublicKeyParser.INSTANCE.getRawPublicKey(KeyPairProvider.SSH_ED25519, buffer);
String appName = buffer.getString();
- return new SkED25519PublicKey(appName, false, (EdDSAPublicKey) publicKey);
+ return new SkED25519PublicKey(appName, false, publicKey);
}
}
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/EdDSAUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/EdDSAUtils.java
new file mode 100644
index 000000000..713b49e37
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/generic/EdDSAUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa.generic;
+
+import java.security.InvalidKeyException;
+import java.security.PublicKey;
+import java.util.Arrays;
+
+/**
+ * Utilities to extract the raw key bytes from ed25519 or ed448 public keys, in a manner that is independent of the
+ * actual concrete key implementation classes.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EdDSAUtils {
+
+ private static final int ED25519_LENGTH = 32; // bytes
+
+ private static final int ED448_LENGTH = 57; // bytes
+
+ // These are the constant prefixes of X.509 encodings of ed25519 and ed448 keys. Appending the actual 32
+ // or 57 key bytes yields valid encodings.
+
+ // Sequence, length 42, Sequence, length 5, OID, length 3, O, I, D, Bit String, length 33, zero unused bits
+ private static final byte[] ED25519_X509_PREFIX = {
+ 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00 };
+ // Sequence, length 67, Sequence, length 5, OID, length 3, O, I, D, Bit String, length 58, zero unused bits
+ private static final byte[] ED448_X509_PREFIX = {
+ 0x30, 0x43, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x71, 0x03, 0x3a, 0x00 };
+
+ private EdDSAUtils() {
+ throw new IllegalStateException("No instantiation");
+ }
+
+ private static boolean startsWith(byte[] data, byte[] prefix) {
+ if (data == null || prefix == null || prefix.length == 0 || data.length < prefix.length) {
+ return false;
+ }
+ int unequal = 0;
+ int length = prefix.length;
+ for (int i = 0; i < length; i++) {
+ unequal |= data[i] ^ prefix[i];
+ }
+ return unequal == 0;
+ }
+
+ /**
+ * Retrieves the raw key bytes from an ed25519 or ed448 {@link PublicKey}.
+ *
+ * @param key {@link PublicKey} to get the bytes of
+ * @return the raw key bytes
+ * @throws InvalidKeyException if the key is not an ed25519 or ed448 key, or if it doesn't use X.509 encoding
+ */
+ public static byte[] getBytes(PublicKey key) throws InvalidKeyException {
+ // Extract the public key bytes from the X.509 encoding (last n bytes, depending on the OID).
+ if (!"X.509".equalsIgnoreCase(key.getFormat())) {
+ throw new InvalidKeyException("Cannot extract public key bytes from a non-X.509 encoding");
+ }
+ byte[] encoded = key.getEncoded();
+ if (encoded == null) {
+ throw new InvalidKeyException("Public key " + key.getClass().getCanonicalName() + " does not support encoding");
+ }
+ int n;
+ if (encoded.length == ED25519_LENGTH + ED25519_X509_PREFIX.length && startsWith(encoded, ED25519_X509_PREFIX)) {
+ n = ED25519_LENGTH;
+ } else if (encoded.length == ED448_LENGTH + ED448_X509_PREFIX.length && startsWith(encoded, ED448_X509_PREFIX)) {
+ n = ED448_LENGTH;
+ } else {
+ throw new InvalidKeyException("Public key is neither ed25519 nor ed448");
+ }
+ return Arrays.copyOfRange(encoded, encoded.length - n, encoded.length);
+ }
+}
(mina-sshd) 01/02: Resolve JUnit warnings about private BeforeAll methods
Posted by tw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
twolf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 591e44e4c50e57eb27fa9a6737ccc69d78a2b5eb
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Sat Jul 19 22:05:33 2025 +0200
Resolve JUnit warnings about private BeforeAll methods
Make them package visible.
---
.../org/apache/sshd/common/signature/KnownHostsCertificateTest.java | 2 +-
.../org/apache/sshd/common/signature/OpenSshHostCertificateTest.java | 2 +-
.../java/org/apache/sshd/server/auth/AuthorizedKeysCertificateTest.java | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java
index 4cf041fe0..ffb3c1cd5 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java
@@ -64,7 +64,7 @@ class KnownHostsCertificateTest extends BaseTestSupport {
private KeyPair caKey;
@BeforeAll
- private static void setupClientAndServer() throws Exception {
+ static void setupClientAndServer() throws Exception {
sshd = CoreTestSupportUtils.setupTestFullSupportServer(KnownHostsCertificateTest.class);
sshd.start();
port = sshd.getPort();
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSshHostCertificateTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSshHostCertificateTest.java
index 8aa52a2cd..07942f93e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSshHostCertificateTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSshHostCertificateTest.java
@@ -54,7 +54,7 @@ class OpenSshHostCertificateTest extends BaseTestSupport {
private KeyPair caKey;
@BeforeAll
- private static void setupClientAndServer() throws Exception {
+ static void setupClientAndServer() throws Exception {
sshd = CoreTestSupportUtils.setupTestFullSupportServer(OpenSshHostCertificateTest.class);
sshd.start();
port = sshd.getPort();
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/AuthorizedKeysCertificateTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/AuthorizedKeysCertificateTest.java
index 549e1f6ea..49b2b2052 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/AuthorizedKeysCertificateTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/AuthorizedKeysCertificateTest.java
@@ -71,7 +71,7 @@ class AuthorizedKeysCertificateTest extends BaseTestSupport {
private OpenSshCertificate userCert;
@BeforeAll
- private static void setupClientAndServer() throws Exception {
+ static void setupClientAndServer() throws Exception {
sshd = CoreTestSupportUtils.setupTestFullSupportServer(AuthorizedKeysCertificateTest.class);
sshd.start();
port = sshd.getPort();