working on ssh-ed25519 with jdk-based classes
This commit is contained in:
parent
5cf088bd12
commit
9af09a4176
9 changed files with 22 additions and 68 deletions
|
@ -49,10 +49,10 @@ public class SftpWithPrivateKeyReaderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDionysos() throws Exception {
|
||||
public void testAlkmene() throws Exception {
|
||||
Map<String, String> 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);
|
||||
|
|
|
@ -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 <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
@ -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),
|
||||
|
|
|
@ -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<Signature> {
|
|||
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<Signature> resolveSignatureFactoryByPublicKey(PublicKey pubKey, String algo)
|
||||
throws InvalidKeySpecException {
|
||||
if (pubKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NamedFactory<Signature> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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<String, byte[]> encoding = extractEncodedSignature(data, KeyPairProvider.SSH_ED25519::equalsIgnoreCase);
|
||||
Map.Entry<String, byte[]> 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);
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue