working on EdEC (JDK based ed25519)
This commit is contained in:
parent
5c18f0704f
commit
be26f3e231
7 changed files with 503 additions and 64 deletions
|
@ -1,4 +1,6 @@
|
|||
module org.xbib.files.sftp {
|
||||
requires org.xbib.net.security;
|
||||
requires java.logging;
|
||||
exports org.apache.sshd.client;
|
||||
exports org.apache.sshd.client.auth;
|
||||
exports org.apache.sshd.client.auth.password;
|
||||
|
@ -79,7 +81,5 @@ module org.xbib.files.sftp {
|
|||
exports org.apache.sshd.common.util.security;
|
||||
exports org.apache.sshd.common.util.security.eddsa;
|
||||
exports org.apache.sshd.common.util.threads;
|
||||
requires org.xbib.net.security;
|
||||
requires java.logging;
|
||||
uses org.apache.sshd.common.io.IoServiceFactoryFactory;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package org.apache.sshd.common.util.security.edec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.EdECPrivateKeySpec;
|
||||
import java.security.spec.EdECPublicKeySpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import org.apache.sshd.common.config.keys.KeyEntryResolver;
|
||||
import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
|
||||
public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder<EdECPublicKey, EdECPrivateKey> {
|
||||
|
||||
public static final int MAX_ALLOWED_SEED_LEN = 1024;
|
||||
|
||||
public static final Ed25519PublicKeyDecoder INSTANCE = new Ed25519PublicKeyDecoder();
|
||||
|
||||
private Ed25519PublicKeyDecoder() {
|
||||
super(EdECPublicKey.class, EdECPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPublicKey clonePublicKey(EdECPublicKey key) throws GeneralSecurityException {
|
||||
if (key == null) {
|
||||
return null;
|
||||
} else {
|
||||
NamedParameterSpec namedParameterSpec = NamedParameterSpec.ED25519;
|
||||
EdECPoint edECPoint = key.getPoint();
|
||||
return generatePublicKey(new EdECPublicKeySpec(namedParameterSpec, edECPoint));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPrivateKey clonePrivateKey(EdECPrivateKey key) throws GeneralSecurityException {
|
||||
if (key == null) {
|
||||
return null;
|
||||
} else {
|
||||
NamedParameterSpec namedParameterSpec = NamedParameterSpec.ED25519;
|
||||
return generatePrivateKey(new EdECPrivateKeySpec(namedParameterSpec, key.getEncoded()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
|
||||
return KeyPairGenerator.getInstance("Ed25519");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodePublicKey(OutputStream s, EdECPublicKey key) throws IOException {
|
||||
// TODO
|
||||
return "ssh-ed25519";
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
|
||||
return KeyFactory.getInstance("EdDSA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPublicKey decodePublicKey(SessionContext session,
|
||||
String keyType,
|
||||
InputStream keyData,
|
||||
Map<String, String> headers)
|
||||
throws IOException, GeneralSecurityException {
|
||||
byte[] pk = KeyEntryResolver.readRLEBytes(keyData, MAX_ALLOWED_SEED_LEN);
|
||||
boolean xisodd = false;
|
||||
int lastbyteInt = pk[pk.length - 1];
|
||||
if ((lastbyteInt & 255) >> 7 == 1) {
|
||||
xisodd = true;
|
||||
}
|
||||
pk[pk.length - 1] &= 127;
|
||||
BigInteger y = new BigInteger(1, pk);
|
||||
NamedParameterSpec namedParameterSpec = NamedParameterSpec.ED25519;
|
||||
EdECPoint ep = new EdECPoint(xisodd, y);
|
||||
EdECPublicKeySpec spec = new EdECPublicKeySpec(namedParameterSpec, ep);
|
||||
return EdECPublicKey.class.cast(generatePublicKey(spec));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
package org.apache.sshd.common.util.security.edec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StreamCorruptedException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.EdECPrivateKeySpec;
|
||||
import java.security.spec.EdECPublicKeySpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.sshd.common.NamedResource;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider;
|
||||
import org.apache.sshd.common.config.keys.loader.pem.AbstractPEMResourceKeyPairParser;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.io.NoCloseInputStream;
|
||||
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;
|
||||
|
||||
public class EdECPEMResourceKeyParser extends AbstractPEMResourceKeyPairParser {
|
||||
|
||||
public static final String BEGIN_MARKER = "BEGIN OPENSSH PRIVATE KEY";
|
||||
|
||||
public static final List<String> BEGINNERS = Collections.singletonList(BEGIN_MARKER);
|
||||
|
||||
public static final String END_MARKER = "END OPENSSH PRIVATE KEY";
|
||||
public static final List<String> ENDERS = Collections.singletonList(END_MARKER);
|
||||
|
||||
/**
|
||||
* @see <A HREF="https://tools.ietf.org/html/rfc8410#section-3">RFC8412 section 3</A>
|
||||
*/
|
||||
public static final String ED25519_OID = "1.3.101.112";
|
||||
|
||||
public static final EdECPEMResourceKeyParser INSTANCE = new EdECPEMResourceKeyParser();
|
||||
|
||||
public EdECPEMResourceKeyParser() {
|
||||
super("Ed25519", ED25519_OID, BEGINNERS, ENDERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<KeyPair> extractKeyPairs(SessionContext session,
|
||||
NamedResource resourceKey,
|
||||
String beginMarker, String endMarker,
|
||||
FilePasswordProvider passwordProvider,
|
||||
InputStream stream,
|
||||
Map<String, String> headers) throws IOException, GeneralSecurityException {
|
||||
KeyPair kp = parseEd25519KeyPair(stream, false);
|
||||
return Collections.singletonList(kp);
|
||||
}
|
||||
|
||||
private static KeyPair parseEd25519KeyPair(InputStream inputStream,
|
||||
boolean okToClose) throws IOException, GeneralSecurityException {
|
||||
try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
|
||||
return parseKeyPair(parser);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See https://tools.ietf.org/html/rfc8410#section-7
|
||||
*
|
||||
* SEQUENCE {
|
||||
* INTEGER 0x00 (0 decimal)
|
||||
* SEQUENCE {
|
||||
* OBJECTIDENTIFIER 1.3.101.112
|
||||
* }
|
||||
* OCTETSTRING keyData
|
||||
* }
|
||||
*
|
||||
* NOTE: there is another variant that also has some extra parameters
|
||||
* but it has the same "prefix" structure so we don't care
|
||||
*/
|
||||
private static KeyPair parseKeyPair(DERParser parser) throws IOException, GeneralSecurityException {
|
||||
ASN1Object obj = parser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing version value");
|
||||
}
|
||||
BigInteger version = obj.asInteger();
|
||||
if (!BigInteger.ZERO.equals(version)) {
|
||||
throw new StreamCorruptedException("Invalid version: " + version);
|
||||
}
|
||||
obj = parser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing OID container");
|
||||
}
|
||||
ASN1Type objType = obj.getObjType();
|
||||
if (objType != ASN1Type.SEQUENCE) {
|
||||
throw new StreamCorruptedException("Unexpected OID object type: " + objType);
|
||||
}
|
||||
List<Integer> curveOid;
|
||||
try (DERParser oidParser = obj.createParser()) {
|
||||
obj = oidParser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing OID value");
|
||||
}
|
||||
curveOid = obj.asOID();
|
||||
}
|
||||
String oid = GenericUtils.join(curveOid, '.');
|
||||
// TODO modify if more curves supported
|
||||
if (!ED25519_OID.equals(oid)) {
|
||||
throw new StreamCorruptedException("Unsupported curve OID: " + oid);
|
||||
}
|
||||
obj = parser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing key data");
|
||||
}
|
||||
return decodeKeyPair(obj.getValue());
|
||||
}
|
||||
|
||||
private static KeyPair decodeKeyPair(byte[] keyData) throws IOException, GeneralSecurityException {
|
||||
EdECPrivateKey privateKey = getPrivateKey(keyData);
|
||||
EdECPublicKey publicKey = getPublicKey(privateKey);
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
private static EdECPrivateKey getPrivateKey(byte[] keyData) throws IOException, GeneralSecurityException {
|
||||
try (DERParser parser = new DERParser(keyData)) {
|
||||
ASN1Object obj = parser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing key data container");
|
||||
}
|
||||
ASN1Type objType = obj.getObjType();
|
||||
if (objType != ASN1Type.OCTET_STRING) {
|
||||
throw new StreamCorruptedException("Mismatched key data container type: " + objType);
|
||||
}
|
||||
return generatePrivateKey(obj.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static EdECPrivateKey generatePrivateKey(byte[] seed) throws GeneralSecurityException {
|
||||
NamedParameterSpec spec = NamedParameterSpec.ED25519;
|
||||
EdECPrivateKeySpec keySpec = new EdECPrivateKeySpec(spec, seed);
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return EdECPrivateKey.class.cast(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();
|
||||
boolean xisodd = false;
|
||||
int lastbyteInt = pk[pk.length - 1];
|
||||
if ((lastbyteInt & 255) >> 7 == 1) {
|
||||
xisodd = true;
|
||||
}
|
||||
pk[pk.length - 1] &= 127;
|
||||
BigInteger y = new BigInteger(1, pk);
|
||||
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
|
||||
EdECPoint ep = new EdECPoint(xisodd, y);
|
||||
EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(paramSpec, ep);
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return EdECPublicKey.class.cast(factory.generatePublic(publicKeySpec));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.apache.sshd.common.util.security.edec;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.util.Objects;
|
||||
import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
|
||||
|
||||
public class EdECSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
|
||||
|
||||
public EdECSecurityProviderRegistrar() {
|
||||
super("Ed25519");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return super.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider getSecurityProvider() {
|
||||
return Security.getProvider("SunJCE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
|
||||
if (!isSupported()) {
|
||||
return false;
|
||||
}
|
||||
if (KeyPairGenerator.class.isAssignableFrom(entityType) || KeyFactory.class.isAssignableFrom(entityType)) {
|
||||
return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0;
|
||||
} else if (Signature.class.isAssignableFrom(entityType)) {
|
||||
return Objects.compare("NONEwithEdDSA", name, String.CASE_INSENSITIVE_ORDER) == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package org.apache.sshd.common.util.security.edec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StreamCorruptedException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.EdECPrivateKeySpec;
|
||||
import java.security.spec.EdECPublicKeySpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider;
|
||||
import org.apache.sshd.common.config.keys.KeyEntryResolver;
|
||||
import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
|
||||
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;
|
||||
|
||||
|
||||
public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdECPublicKey, EdECPrivateKey> {
|
||||
|
||||
public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder();
|
||||
|
||||
private static final int PK_SIZE = 32;
|
||||
|
||||
private static final int SK_SIZE = 32;
|
||||
|
||||
private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE;
|
||||
|
||||
public OpenSSHEd25519PrivateKeyEntryDecoder() {
|
||||
super(EdECPublicKey.class, EdECPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPrivateKey decodePrivateKey(SessionContext session,
|
||||
String keyType,
|
||||
FilePasswordProvider passwordProvider,
|
||||
InputStream keyData)
|
||||
throws IOException, GeneralSecurityException {
|
||||
if (!"ssh-ed25519".equals(keyType)) {
|
||||
throw new InvalidKeyException("Unsupported key type: " + keyType);
|
||||
}
|
||||
// ed25519 bernstein naming: pk .. public key, sk .. secret key
|
||||
// we expect to find two byte arrays with the following structure (type:size):
|
||||
// [pk:32], [sk:32,pk:32]
|
||||
byte[] pk = GenericUtils.EMPTY_BYTE_ARRAY;
|
||||
byte[] keypair = GenericUtils.EMPTY_BYTE_ARRAY;
|
||||
try {
|
||||
pk = KeyEntryResolver.readRLEBytes(keyData, PK_SIZE * 2);
|
||||
keypair = KeyEntryResolver.readRLEBytes(keyData, KEYPAIR_SIZE * 2);
|
||||
if (pk.length != PK_SIZE) {
|
||||
throw new InvalidKeyException(
|
||||
String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE));
|
||||
}
|
||||
if (keypair.length != KEYPAIR_SIZE) {
|
||||
throw new InvalidKeyException(
|
||||
String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length,
|
||||
KEYPAIR_SIZE));
|
||||
}
|
||||
// verify that the keypair contains the expected pk
|
||||
// yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface
|
||||
if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) {
|
||||
throw new InvalidKeyException("Keypair did not contain the public key.");
|
||||
}
|
||||
byte[] seed = Arrays.copyOf(keypair, SK_SIZE);
|
||||
NamedParameterSpec spec = NamedParameterSpec.ED25519;
|
||||
EdECPrivateKeySpec keySpec = new EdECPrivateKeySpec(spec, seed);
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return EdECPrivateKey.class.cast(factory.generatePrivate(keySpec));
|
||||
} finally {
|
||||
// get rid of sensitive data a.s.a.p
|
||||
Arrays.fill(pk, (byte) 0);
|
||||
Arrays.fill(keypair, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodePrivateKey(SecureByteArrayOutputStream s,
|
||||
EdECPrivateKey key,
|
||||
EdECPublicKey pubKey) throws IOException {
|
||||
Objects.requireNonNull(key, "No private key provided");
|
||||
return "ssh-ed25519";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicKeyRecoverySupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPublicKey recoverPublicKey(EdECPrivateKey key) throws GeneralSecurityException {
|
||||
byte[] pk = key.getEncoded();
|
||||
boolean xisodd = false;
|
||||
int lastbyteInt = pk[pk.length - 1];
|
||||
if ((lastbyteInt & 255) >> 7 == 1) {
|
||||
xisodd = true;
|
||||
}
|
||||
pk[pk.length - 1] &= 127;
|
||||
BigInteger y = new BigInteger(1, pk);
|
||||
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
|
||||
EdECPoint ep = new EdECPoint(xisodd, y);
|
||||
EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(paramSpec, ep);
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return EdECPublicKey.class.cast(factory.generatePublic(publicKeySpec));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPublicKey clonePublicKey(EdECPublicKey key) throws GeneralSecurityException {
|
||||
if (key == null) {
|
||||
return null;
|
||||
} else {
|
||||
NamedParameterSpec namedParameterSpec = NamedParameterSpec.ED25519;
|
||||
EdECPoint edECPoint = key.getPoint();
|
||||
EdECPublicKeySpec spec = new EdECPublicKeySpec(namedParameterSpec, edECPoint);
|
||||
return generatePublicKey(spec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPrivateKey clonePrivateKey(EdECPrivateKey key) throws GeneralSecurityException {
|
||||
if (key == null) {
|
||||
return null;
|
||||
} else {
|
||||
return getPrivateKey(key.getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
|
||||
return KeyPairGenerator.getInstance("Ed25519");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
|
||||
return KeyFactory.getInstance("EdDSA");
|
||||
}
|
||||
|
||||
private static EdECPrivateKey getPrivateKey(byte[] keyData) throws GeneralSecurityException {
|
||||
try (DERParser parser = new DERParser(keyData)) {
|
||||
ASN1Object obj = parser.readObject();
|
||||
if (obj == null) {
|
||||
throw new StreamCorruptedException("Missing key data container");
|
||||
}
|
||||
ASN1Type objType = obj.getObjType();
|
||||
if (objType != ASN1Type.OCTET_STRING) {
|
||||
throw new StreamCorruptedException("Mismatched key data container type: " + objType);
|
||||
}
|
||||
return generatePrivateKey(obj.getValue());
|
||||
} catch (IOException e) {
|
||||
throw new GeneralSecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static EdECPrivateKey generatePrivateKey(byte[] seed) throws GeneralSecurityException {
|
||||
NamedParameterSpec spec = NamedParameterSpec.ED25519;
|
||||
EdECPrivateKeySpec keySpec = new EdECPrivateKeySpec(spec, seed);
|
||||
KeyFactory factory = KeyFactory.getInstance("EdDSA");
|
||||
return EdECPrivateKey.class.cast(factory.generatePrivate(keySpec));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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;
|
||||
|
||||
public class SignatureEd25519 extends AbstractSignature {
|
||||
|
||||
public SignatureEd25519() {
|
||||
super("NONEwithEdDSA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(SessionContext session, byte[] sig) throws Exception {
|
||||
byte[] data = sig;
|
||||
Map.Entry<String, byte[]> encoding = extractEncodedSignature(data, KeyPairProvider.SSH_ED25519::equalsIgnoreCase);
|
||||
if (encoding != null) {
|
||||
String keyType = encoding.getKey();
|
||||
ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
|
||||
data = encoding.getValue();
|
||||
}
|
||||
return doVerify(data);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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.xbib;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.sshd.common.NamedResource;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider;
|
||||
import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.xbib.net.security.PrivateKeyReader;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class XbibKeyPairResourceParser implements KeyPairResourceParser {
|
||||
|
||||
public static final XbibKeyPairResourceParser INSTANCE = new XbibKeyPairResourceParser();
|
||||
|
||||
public XbibKeyPairResourceParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<KeyPair> loadKeyPairs(SessionContext session,
|
||||
NamedResource resourceKey,
|
||||
FilePasswordProvider passwordProvider,
|
||||
List<String> lines) throws IOException, GeneralSecurityException {
|
||||
PrivateKeyReader privateKeyReader = new PrivateKeyReader();
|
||||
InputStream inputStream = new ByteArrayInputStream(String.join("\n", lines).getBytes(StandardCharsets.US_ASCII));
|
||||
String password = passwordProvider.getPassword(session, resourceKey, 0);
|
||||
return Collections.singleton(privateKeyReader.generateFrom(inputStream, password));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue