Browse Source

Merge pull request #1429 from flaix/ssh-host-algs

Add new SSH host key types
pull/1433/head
Florian Zschocke 1 year ago
parent
commit
7a2c589d54
No account linked to committer's email address

+ 4
- 3
.classpath View File

@@ -51,9 +51,10 @@
<classpathentry kind="lib" path="ext/commons-logging-1.1.3.jar" sourcepath="ext/src/commons-logging-1.1.3.jar" />
<classpathentry kind="lib" path="ext/commons-codec-1.7.jar" sourcepath="ext/src/commons-codec-1.7.jar" />
<classpathentry kind="lib" path="ext/org.eclipse.jgit.http.server-4.5.7.201904151645-r.jar" sourcepath="ext/src/org.eclipse.jgit.http.server-4.5.7.201904151645-r.jar" />
<classpathentry kind="lib" path="ext/bcprov-jdk15on-1.57.jar" sourcepath="ext/src/bcprov-jdk15on-1.57.jar" />
<classpathentry kind="lib" path="ext/bcmail-jdk15on-1.57.jar" sourcepath="ext/src/bcmail-jdk15on-1.57.jar" />
<classpathentry kind="lib" path="ext/bcpkix-jdk15on-1.57.jar" sourcepath="ext/src/bcpkix-jdk15on-1.57.jar" />
<classpathentry kind="lib" path="ext/bcprov-jdk15on-1.69.jar" sourcepath="ext/src/bcprov-jdk15on-1.69.jar" />
<classpathentry kind="lib" path="ext/bcmail-jdk15on-1.69.jar" sourcepath="ext/src/bcmail-jdk15on-1.69.jar" />
<classpathentry kind="lib" path="ext/bcutil-jdk15on-1.69.jar" sourcepath="ext/src/bcutil-jdk15on-1.69.jar" />
<classpathentry kind="lib" path="ext/bcpkix-jdk15on-1.69.jar" sourcepath="ext/src/bcpkix-jdk15on-1.69.jar" />
<classpathentry kind="lib" path="ext/eddsa-0.2.0.jar" sourcepath="ext/src/eddsa-0.2.0.jar" />
<classpathentry kind="lib" path="ext/sshd-core-1.7.0.jar" sourcepath="ext/src/sshd-core-1.7.0.jar" />
<classpathentry kind="lib" path="ext/mina-core-2.0.21.jar" sourcepath="ext/src/mina-core-2.0.21.jar" />

+ 1
- 1
build.moxie View File

@@ -111,7 +111,7 @@ properties: {
lucene.version : 5.5.2
jgit.version : 4.5.7.201904151645-r
groovy.version : 2.4.4
bouncycastle.version : 1.57
bouncycastle.version : 1.69
selenium.version : 2.28.0
wikitext.version : 1.4
sshd.version: 1.7.0

+ 20
- 9
gitblit.iml View File

@@ -508,35 +508,46 @@
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="bcprov-jdk15on-1.57.jar">
<library name="bcprov-jdk15on-1.69.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/bcprov-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/bcprov-jdk15on-1.69.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/bcprov-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/src/bcprov-jdk15on-1.69.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="bcmail-jdk15on-1.57.jar">
<library name="bcmail-jdk15on-1.69.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/bcmail-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/bcmail-jdk15on-1.69.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/bcmail-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/src/bcmail-jdk15on-1.69.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="bcpkix-jdk15on-1.57.jar">
<library name="bcutil-jdk15on-1.69.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/bcpkix-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/bcutil-jdk15on-1.69.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/bcpkix-jdk15on-1.57.jar!/" />
<root url="jar://$MODULE_DIR$/ext/src/bcutil-jdk15on-1.69.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="bcpkix-jdk15on-1.69.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/bcpkix-jdk15on-1.69.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/bcpkix-jdk15on-1.69.jar!/" />
</SOURCES>
</library>
</orderEntry>

+ 107
- 46
src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java View File

@@ -18,81 +18,92 @@
*/
package com.gitblit.transport.ssh;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

/**
* This host key provider loads private keys from the specified files.
*
* <p>
* Note that this class has a direct dependency on BouncyCastle and won't work
* unless it has been correctly registered as a security provider.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class FileKeyPairProvider extends AbstractKeyPairProvider {
public class FileKeyPairProvider extends AbstractKeyPairProvider
{

private String[] files;
private PasswordFinder passwordFinder;

public FileKeyPairProvider() {
}

public FileKeyPairProvider(String[] files) {
this.files = files;
public FileKeyPairProvider()
{
}

public FileKeyPairProvider(String[] files, PasswordFinder passwordFinder) {
public FileKeyPairProvider(String[] files)
{
this.files = files;
this.passwordFinder = passwordFinder;
}

public String[] getFiles() {
public String[] getFiles()
{
return files;
}

public void setFiles(String[] files) {
public void setFiles(String[] files)
{
this.files = files;
}

public PasswordFinder getPasswordFinder() {
return passwordFinder;
}

public void setPasswordFinder(PasswordFinder passwordFinder) {
this.passwordFinder = passwordFinder;
}

public Iterable<KeyPair> loadKeys() {
@Override
public Iterable<KeyPair> loadKeys()
{
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new IllegalStateException("BouncyCastle must be registered as a JCE provider");
}
return new Iterable<KeyPair>() {
return new Iterable<KeyPair>()
{
@Override
public Iterator<KeyPair> iterator() {
return new Iterator<KeyPair>() {
public Iterator<KeyPair> iterator()
{
return new Iterator<KeyPair>()
{
private final Iterator<String> iterator = Arrays.asList(files).iterator();
private KeyPair nextKeyPair;
private boolean nextKeyPairSet = false;

@Override
public boolean hasNext() {
public boolean hasNext()
{
return nextKeyPairSet || setNextObject();
}

@Override
public KeyPair next() {
public KeyPair next()
{
if (!nextKeyPairSet) {
if (!setNextObject()) {
throw new NoSuchElementException();
@@ -101,13 +112,22 @@ public class FileKeyPairProvider extends AbstractKeyPairProvider {
nextKeyPairSet = false;
return nextKeyPair;
}

@Override
public void remove() {
public void remove()
{
throw new UnsupportedOperationException();
}
private boolean setNextObject() {

private boolean setNextObject()
{
while (iterator.hasNext()) {
String file = iterator.next();
File f = new File(file);
if (!f.isFile()) {
log.debug("File does not exist, skipping {}", file);
continue;
}
nextKeyPair = doLoadKey(file);
if (nextKeyPair != null) {
nextKeyPairSet = true;
@@ -122,30 +142,71 @@ public class FileKeyPairProvider extends AbstractKeyPairProvider {
};
}

protected KeyPair doLoadKey(String file) {

private KeyPair doLoadKey(String file)
{
try {
PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)));
try {

try (PemReader r = new PemReader(new InputStreamReader(new FileInputStream(file)))) {
PemObject pemObject = r.readPemObject();
if ("OPENSSH PRIVATE KEY".equals(pemObject.getType())) {
// This reads a properly OpenSSH formatted ed25519 private key file.
// It is currently unused because the SSHD library in play doesn't work with proper keys.
// This is kept in the hope that in the future the library offers proper support.
try {
byte[] privateKeyContent = pemObject.getContent();
AsymmetricKeyParameter privateKeyParameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(privateKeyContent);
if (privateKeyParameters instanceof Ed25519PrivateKeyParameters) {
OpenSSHPrivateKeySpec privkeySpec = new OpenSSHPrivateKeySpec(privateKeyContent);

Ed25519PublicKeyParameters publicKeyParameters = ((Ed25519PrivateKeyParameters)privateKeyParameters).generatePublicKey();
OpenSSHPublicKeySpec pubKeySpec = new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(publicKeyParameters));

KeyFactory kf = KeyFactory.getInstance("Ed25519", "BC");
PrivateKey privateKey = kf.generatePrivate(privkeySpec);
PublicKey publicKey = kf.generatePublic(pubKeySpec);
return new KeyPair(publicKey, privateKey);
}
else {
log.warn("OpenSSH format is only supported for Ed25519 key type. Unable to read key " + file);
}
}
catch (Exception e) {
log.warn("Unable to read key " + file, e);
}
return null;
}

if ("EDDSA PRIVATE KEY".equals(pemObject.getType())) {
// This reads the ed25519 key from a file format that we created in SshDaemon.
// The type EDDSA PRIVATE KEY was given by us and nothing official.
byte[] privateKeyContent = pemObject.getContent();
PrivateKeyEntryDecoder<? extends PublicKey,? extends PrivateKey> decoder = SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
PrivateKey privateKey = decoder.decodePrivateKey(null, privateKeyContent, 0, privateKeyContent.length);
PublicKey publicKey = SecurityUtils. recoverEDDSAPublicKey(privateKey);
return new KeyPair(publicKey, privateKey);
}
}

try (PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)))) {
Object o = r.readObject();

JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
pemConverter.setProvider("BC");
if (passwordFinder != null && o instanceof PEMEncryptedKeyPair) {
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword());
o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor));
}

if (o instanceof PEMKeyPair) {
o = pemConverter.getKeyPair((PEMKeyPair)o);
return (KeyPair) o;
} else if (o instanceof KeyPair) {
return (KeyPair) o;
return (KeyPair)o;
}
else if (o instanceof KeyPair) {
return (KeyPair)o;
}
else {
log.warn("Cannot read unsupported PEM object of type: " + o.getClass().getCanonicalName());
}
} finally {
r.close();
}
} catch (Exception e) {

}
catch (Exception e) {
log.warn("Unable to read key " + file, e);
}
return null;

+ 51
- 8
src/main/java/com/gitblit/transport/ssh/SshDaemon.java View File

@@ -15,6 +15,7 @@
*/
package com.gitblit.transport.ssh;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -22,19 +23,28 @@ import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.text.MessageFormat;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import org.apache.sshd.common.config.keys.KeyEntryResolver;
import org.apache.sshd.common.io.IoServiceFactoryFactory;
import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory;
import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar;
import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar;
import org.apache.sshd.common.util.security.eddsa.OpenSSHEd25519PrivateKeyEntryDecoder;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.pubkey.CachingPublicKeyAuthenticator;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.eclipse.jgit.internal.JGitText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,7 +66,7 @@ import com.google.common.io.Files;
*/
public class SshDaemon {

private final Logger log = LoggerFactory.getLogger(SshDaemon.class);
private static final Logger log = LoggerFactory.getLogger(SshDaemon.class);

private static final String AUTH_PUBLICKEY = "publickey";
private static final String AUTH_PASSWORD = "password";
@@ -107,10 +117,14 @@ public class SshDaemon {
// Generate host RSA and DSA keypairs and create the host keypair provider
File rsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-rsa-hostkey.pem");
File dsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-dsa-hostkey.pem");
File ecdsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-ecdsa-hostkey.pem");
File eddsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-eddsa-hostkey.pem");
File ed25519KeyStore = new File(gitblit.getBaseFolder(), "ssh-ed25519-hostkey.pem");
generateKeyPair(rsaKeyStore, "RSA", 2048);
generateKeyPair(dsaKeyStore, "DSA", 0);
generateKeyPair(ecdsaKeyStore, "ECDSA", 256);
generateKeyPair(eddsaKeyStore, "EdDSA", 0);
FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { rsaKeyStore.getPath(), dsaKeyStore.getPath(), dsaKeyStore.getPath() });
hostKeyPairProvider.setFiles(new String [] { ecdsaKeyStore.getPath(), eddsaKeyStore.getPath(), ed25519KeyStore.getPath(), rsaKeyStore.getPath(), dsaKeyStore.getPath() });


// Configure the preferred SSHD backend
@@ -244,7 +258,7 @@ public class SshDaemon {
}
}

private void generateKeyPair(File file, String algorithm, int keySize) {
static void generateKeyPair(File file, String algorithm, int keySize) {
if (file.exists()) {
return;
}
@@ -267,13 +281,42 @@ public class SshDaemon {
}

FileOutputStream os = new FileOutputStream(file);
PEMWriter w = new PEMWriter(new OutputStreamWriter(os));
w.writeObject(kp);
PemWriter w = new PemWriter(new OutputStreamWriter(os));
if (algorithm.equals("ED25519")) {
// This generates a proper OpenSSH formatted ed25519 private key file.
// It is currently unused because the SSHD library in play doesn't work with proper keys.
// This is kept in the hope that in the future the library offers proper support.
AsymmetricKeyParameter keyParam = PrivateKeyFactory.createKey(kp.getPrivate().getEncoded());
byte[] encKey = OpenSSHPrivateKeyUtil.encodePrivateKey(keyParam);
w.writeObject(new PemObject("OPENSSH PRIVATE KEY", encKey));
}
else if (algorithm.equals("EdDSA")) {
// This saves the ed25519 key in a file format that the current SSHD library can work with.
// We call it EDDSA PRIVATE KEY, but that string is given by us and nothing official.
PrivateKey privateKey = kp.getPrivate();
if (privateKey instanceof EdDSAPrivateKey) {
OpenSSHEd25519PrivateKeyEntryDecoder encoder = (OpenSSHEd25519PrivateKeyEntryDecoder)SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
EdDSAPrivateKey dsaPrivateKey = (EdDSAPrivateKey)privateKey;
// Jumping through some hoops here, because the decoder expects the key type as a string at the
// start, but the encoder doesn't put it in. So we have to put it in ourselves.
ByteArrayOutputStream encos = new ByteArrayOutputStream();
String type = encoder.encodePrivateKey(encos, dsaPrivateKey);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
KeyEntryResolver.encodeString(bos, type);
encos.writeTo(bos);
w.writeObject(new PemObject("EDDSA PRIVATE KEY", bos.toByteArray()));
}
else {
log.warn("Unable to encode EdDSA key, got key type " + privateKey.getClass().getCanonicalName());
}
}
else {
w.writeObject(new JcaMiscPEMGenerator(kp));
}
w.flush();
w.close();
} catch (Exception e) {
log.warn(MessageFormat.format("Unable to generate {0} keypair", algorithm), e);
return;
}
}
}

+ 16
- 17
src/main/java/com/gitblit/utils/X509Utils.java View File

@@ -72,7 +72,7 @@ import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509v2CRLBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
@@ -82,7 +82,6 @@ import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.bouncycastle.openssl.PEMEncryptor;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
import org.bouncycastle.operator.ContentSigner;
@@ -445,9 +444,9 @@ public class X509Utils {
boolean asPem = targetFile.getName().toLowerCase().endsWith(".pem");
if (asPem) {
// PEM encoded X509
PEMWriter pemWriter = null;
JcaPEMWriter pemWriter = null;
try {
pemWriter = new PEMWriter(new FileWriter(tmpFile));
pemWriter = new JcaPEMWriter(new FileWriter(tmpFile));
pemWriter.writeObject(cert);
pemWriter.flush();
} finally {
@@ -560,9 +559,9 @@ public class X509Utils {
pair.getPublic());
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
// support alternateSubjectNames for SSL certificates
List<GeneralName> altNames = new ArrayList<GeneralName>();
@@ -571,7 +570,7 @@ public class X509Utils {
}
if (altNames.size() > 0) {
GeneralNames subjectAltName = new GeneralNames(altNames.toArray(new GeneralName [altNames.size()]));
certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltName);
certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
}
ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM)
@@ -629,10 +628,10 @@ public class X509Utils {
caPair.getPublic());
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
caBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic()));
caBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic()));
caBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(true));
caBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign));
caBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic()));
caBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic()));
caBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
caBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign));
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC);
X509Certificate cert = converter.getCertificate(caBuilder.build(caSigner));
@@ -862,14 +861,14 @@ public class X509Utils {
pair.getPublic());
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature));
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature));
if (!StringUtils.isEmpty(clientMetadata.emailAddress)) {
GeneralNames subjectAltName = new GeneralNames(
new GeneralName(GeneralName.rfc822Name, clientMetadata.emailAddress));
certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltName);
certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
}
ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);

+ 134
- 0
src/test/java/com/gitblit/transport/ssh/FileKeyPairProviderTest.java View File

@@ -0,0 +1,134 @@
package com.gitblit.transport.ssh;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.security.KeyPair;
import java.util.Iterator;

import static org.junit.Assert.*;

public class FileKeyPairProviderTest
{
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();

private void generateKeyPair(File file, String algorithm, int keySize) {
if (file.exists()) {
file.delete();
}
SshDaemon.generateKeyPair(file, algorithm, keySize);
}

@Test
public void loadKeysEddsa() throws IOException
{
File file = testFolder.newFile("eddsa.pem");
generateKeyPair(file, "EdDSA", 0);

FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { file.getPath() });

Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys();
Iterator<KeyPair> iterator = keyPairs.iterator();
assertTrue(iterator.hasNext());
KeyPair keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "EdDSA", keyPair.getPrivate().getAlgorithm());
}

@Test
public void loadKeysEd25519() throws IOException
{
File file = testFolder.newFile("ed25519.pem");
generateKeyPair(file, "ED25519", 0);

FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { file.getPath() });

Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys();
Iterator<KeyPair> iterator = keyPairs.iterator();
assertTrue(iterator.hasNext());
KeyPair keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "Ed25519", keyPair.getPrivate().getAlgorithm());
}

@Test
public void loadKeysECDSA() throws IOException
{
File file = testFolder.newFile("ecdsa.pem");
generateKeyPair(file, "ECDSA", 0);

FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { file.getPath() });

Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys();
Iterator<KeyPair> iterator = keyPairs.iterator();
assertTrue(iterator.hasNext());
KeyPair keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "ECDSA", keyPair.getPrivate().getAlgorithm());
}

@Test
public void loadKeysRSA() throws IOException
{
File file = testFolder.newFile("rsa.pem");
generateKeyPair(file, "RSA", 4096);

FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { file.getPath() });

Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys();
Iterator<KeyPair> iterator = keyPairs.iterator();
assertTrue(iterator.hasNext());
KeyPair keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "RSA", keyPair.getPrivate().getAlgorithm());
}

@Test
public void loadKeysDefault() throws IOException
{
File rsa = testFolder.newFile("rsa.pem");
generateKeyPair(rsa, "RSA", 2048);
File ecdsa = testFolder.newFile("ecdsa.pem");
generateKeyPair(ecdsa, "ECDSA", 0);
File eddsa = testFolder.newFile("eddsa.pem");
generateKeyPair(eddsa, "EdDSA", 0);
File ed25519 = testFolder.newFile("ed25519.pem");
generateKeyPair(ed25519, "ED25519", 0);

FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider();
hostKeyPairProvider.setFiles(new String [] { ecdsa.getPath(), eddsa.getPath(), rsa.getPath(), ed25519.getPath() });

Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys();
Iterator<KeyPair> iterator = keyPairs.iterator();

assertTrue(iterator.hasNext());
KeyPair keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "ECDSA", keyPair.getPrivate().getAlgorithm());

assertTrue(iterator.hasNext());
keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "EdDSA", keyPair.getPrivate().getAlgorithm());

assertTrue(iterator.hasNext());
keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "RSA", keyPair.getPrivate().getAlgorithm());

assertTrue(iterator.hasNext());
keyPair = iterator.next();
assertNotNull(keyPair);
assertEquals("Unexpected key pair type", "Ed25519", keyPair.getPrivate().getAlgorithm());

assertFalse(iterator.hasNext());
}
}

Loading…
Cancel
Save