From 9af09a4176b16ded613f31ba015d45922daf2191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Tue, 28 May 2024 09:17:26 +0200 Subject: [PATCH] working on ssh-ed25519 with jdk-based classes --- .../fs/test/SftpWithPrivateKeyReaderTest.java | 4 +- .../pem/PKCS8PEMResourceKeyPairParser.java | 6 +-- .../common/signature/SignatureFactory.java | 45 ------------------- .../common/util/security/SecurityUtils.java | 4 +- .../edec/EdECPEMResourceKeyParser.java | 17 +++---- .../edec/EdECSecurityProviderRegistrar.java | 4 +- .../OpenSSHEd25519PrivateKeyEntryDecoder.java | 2 + .../util/security/edec/SignatureEd25519.java | 6 +-- settings.gradle | 2 +- 9 files changed, 22 insertions(+), 68 deletions(-) diff --git a/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpWithPrivateKeyReaderTest.java b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpWithPrivateKeyReaderTest.java index ee8cf39..3d63575 100644 --- a/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpWithPrivateKeyReaderTest.java +++ b/files-sftp-fs/src/test/java/org/apache/sshd/fs/test/SftpWithPrivateKeyReaderTest.java @@ -49,10 +49,10 @@ public class SftpWithPrivateKeyReaderTest { } @Test - public void testDionysos() throws Exception { + public void testAlkmene() throws Exception { Map env = new HashMap<>(); env.put("username", "joerg"); - URI uri = URI.create("sftp://dionysos"); + URI uri = URI.create("sftp://alkmene"); Path privateKeyPath = Paths.get(System.getProperty("user.home") + "/.ssh/id_ed25519"); PrivateKeyReader privateKeyReader = new PrivateKeyReader(); try (InputStream inputStream = Files.newInputStream(privateKeyPath); diff --git a/files-sftp/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/files-sftp/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java index 2ef18aa..5438a2f 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java @@ -45,7 +45,7 @@ import org.apache.sshd.common.util.io.der.ASN1Object; import org.apache.sshd.common.util.io.der.ASN1Type; import org.apache.sshd.common.util.io.der.DERParser; import org.apache.sshd.common.util.security.SecurityUtils; -import org.apache.sshd.common.util.security.eddsa.Ed25519PEMResourceKeyParser; +import org.apache.sshd.common.util.security.edec.EdECPEMResourceKeyParser; /** * @author Apache MINA SSHD Project @@ -109,9 +109,9 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar kp = ECDSAPEMResourceKeyPairParser.parseECKeyPair(curve, parser); } } else if (SecurityUtils.isEDDSACurveSupported() - && Ed25519PEMResourceKeyParser.ED25519_OID.endsWith(oid)) { + && EdECPEMResourceKeyParser.ED25519_OID.endsWith(oid)) { ASN1Object privateKeyBytes = pkcs8Info.getPrivateKeyBytes(); - kp = Ed25519PEMResourceKeyParser.decodeEd25519KeyPair(privateKeyBytes.getPureValueBytes()); + kp = EdECPEMResourceKeyParser.decodeKeyPair(privateKeyBytes.getPureValueBytes()); } else { PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes); PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey), diff --git a/files-sftp/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/files-sftp/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java index 3d08775..f984a86 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java @@ -20,7 +20,6 @@ package org.apache.sshd.common.signature; import java.security.PublicKey; -import java.security.interfaces.DSAPublicKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; @@ -207,48 +206,4 @@ public interface SignatureFactory extends BuiltinFactory { return NamedResource.findFirstMatchByName(aliases, String.CASE_INSENSITIVE_ORDER, factories); } } - - /** - * @param pubKey The intended {@link PublicKey} - ignored if {@code null} - * @param algo The intended signature algorithm - if {@code null}/empty and multiple signatures - * available for the key type then a default will be used. Otherwise, it is - * validated to make sure it matches the public key type - * @return The {@link Signature} factory or {@code null} if no match found - * @throws InvalidKeySpecException If specified algorithm does not match the selected public key - */ - static NamedFactory resolveSignatureFactoryByPublicKey(PublicKey pubKey, String algo) - throws InvalidKeySpecException { - if (pubKey == null) { - return null; - } - - NamedFactory factory = null; - if (pubKey instanceof ECPublicKey) { - ECPublicKey ecKey = (ECPublicKey) pubKey; - factory = BuiltinSignatures.getFactoryByCurveSize(ecKey.getParams()); - } else if (pubKey instanceof RSAPublicKey) { - // SSHD-1104 take into account key aliases - if (GenericUtils.isEmpty(algo)) { - factory = BuiltinSignatures.rsa; - } else if (algo.contains("rsa")) { - factory = BuiltinSignatures.fromFactoryName(algo); - } - } else if (SecurityUtils.EDDSA.equalsIgnoreCase(pubKey.getAlgorithm())) { - factory = BuiltinSignatures.ed25519; - } - - if (GenericUtils.isEmpty(algo) || (factory == null)) { - return factory; - } - - String name = factory.getName(); - if (!algo.equalsIgnoreCase(name)) { - throw new InvalidKeySpecException( - "Mismatched factory name (" + name + ")" - + " for algorithm=" + algo + " when using key type" - + KeyUtils.getKeyType(pubKey)); - } - - return factory; - } } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java index 3ba0c51..e77b937 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java @@ -34,9 +34,7 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.cert.CertificateFactory; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -50,6 +48,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.Mac; diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECPEMResourceKeyParser.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECPEMResourceKeyParser.java index 2e812c3..dcae448 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECPEMResourceKeyParser.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECPEMResourceKeyParser.java @@ -60,7 +60,7 @@ public class EdECPEMResourceKeyParser extends AbstractPEMResourceKeyPairParser { return Collections.singletonList(kp); } - private static KeyPair parseEd25519KeyPair(InputStream inputStream, + public static KeyPair parseEd25519KeyPair(InputStream inputStream, boolean okToClose) throws IOException, GeneralSecurityException { try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) { return parseKeyPair(parser); @@ -118,9 +118,9 @@ public class EdECPEMResourceKeyParser extends AbstractPEMResourceKeyPairParser { return decodeKeyPair(obj.getValue()); } - private static KeyPair decodeKeyPair(byte[] keyData) throws IOException, GeneralSecurityException { + public static KeyPair decodeKeyPair(byte[] keyData) throws IOException, GeneralSecurityException { EdECPrivateKey privateKey = getPrivateKey(keyData); - EdECPublicKey publicKey = getPublicKey(privateKey); + EdECPublicKey publicKey = getPublicKey(keyData); return new KeyPair(publicKey, privateKey); } @@ -142,14 +142,11 @@ public class EdECPEMResourceKeyParser extends AbstractPEMResourceKeyPairParser { NamedParameterSpec spec = NamedParameterSpec.ED25519; EdECPrivateKeySpec keySpec = new EdECPrivateKeySpec(spec, seed); KeyFactory factory = KeyFactory.getInstance("EdDSA"); - return EdECPrivateKey.class.cast(factory.generatePrivate(keySpec)); + return (EdECPrivateKey) factory.generatePrivate(keySpec); } - private static EdECPublicKey getPublicKey(PrivateKey key) throws GeneralSecurityException { - if (!(key instanceof EdECPrivateKey)) { - throw new InvalidKeyException("Private key is not EdEC private key"); - } - byte[] pk = key.getEncoded(); + private static EdECPublicKey getPublicKey(byte[] keyData) throws GeneralSecurityException { + byte[] pk = keyData; boolean xisodd = false; int lastbyteInt = pk[pk.length - 1]; if ((lastbyteInt & 255) >> 7 == 1) { @@ -161,6 +158,6 @@ public class EdECPEMResourceKeyParser extends AbstractPEMResourceKeyPairParser { EdECPoint ep = new EdECPoint(xisodd, y); EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(paramSpec, ep); KeyFactory factory = KeyFactory.getInstance("EdDSA"); - return EdECPublicKey.class.cast(factory.generatePublic(publicKeySpec)); + return (EdECPublicKey) factory.generatePublic(publicKeySpec); } } diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECSecurityProviderRegistrar.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECSecurityProviderRegistrar.java index 2bb32de..fd0d9a2 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECSecurityProviderRegistrar.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/EdECSecurityProviderRegistrar.java @@ -11,12 +11,12 @@ import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar; public class EdECSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar { public EdECSecurityProviderRegistrar() { - super("Ed25519"); + super("EdDSA"); } @Override public boolean isEnabled() { - return super.isEnabled(); + return true; } @Override diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/OpenSSHEd25519PrivateKeyEntryDecoder.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/OpenSSHEd25519PrivateKeyEntryDecoder.java index 9c69880..57893fd 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/OpenSSHEd25519PrivateKeyEntryDecoder.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/OpenSSHEd25519PrivateKeyEntryDecoder.java @@ -92,6 +92,7 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr EdECPrivateKey key, EdECPublicKey pubKey) throws IOException { Objects.requireNonNull(key, "No private key provided"); + // TODO return "ssh-ed25519"; } @@ -102,6 +103,7 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr @Override public EdECPublicKey recoverPublicKey(EdECPrivateKey key) throws GeneralSecurityException { + // TODO byte[] pk = key.getEncoded(); boolean xisodd = false; int lastbyteInt = pk[pk.length - 1]; diff --git a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/SignatureEd25519.java b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/SignatureEd25519.java index 22fa8ad..b27081f 100644 --- a/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/SignatureEd25519.java +++ b/files-sftp/src/main/java/org/apache/sshd/common/util/security/edec/SignatureEd25519.java @@ -1,7 +1,6 @@ package org.apache.sshd.common.util.security.edec; import java.util.Map; -import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.signature.AbstractSignature; import org.apache.sshd.common.util.ValidateUtils; @@ -15,10 +14,11 @@ public class SignatureEd25519 extends AbstractSignature { @Override public boolean verify(SessionContext session, byte[] sig) throws Exception { byte[] data = sig; - Map.Entry encoding = extractEncodedSignature(data, KeyPairProvider.SSH_ED25519::equalsIgnoreCase); + Map.Entry encoding = extractEncodedSignature(data, + s -> s.equalsIgnoreCase("ssh-ed25519")); if (encoding != null) { String keyType = encoding.getKey(); - ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType); + ValidateUtils.checkTrue("ssh-ed25519".equals(keyType), "Mismatched key type: %s", keyType); data = encoding.getValue(); } return doVerify(data); diff --git a/settings.gradle b/settings.gradle index 754aa85..fa92fa6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,7 +16,7 @@ dependencyResolutionManagement { versionCatalogs { libs { version('gradle', '8.7') - version('net', '4.6.0') + version('net', '4.6.1') library('net-security', 'org.xbib', 'net-security').versionRef('net') library('maverick-synergy-client', 'com.sshtools', 'maverick-synergy-client').version('3.1.1') }