Skip to content

Commit 7a2c589

Browse files
authored
Merge pull request #1429 from flaix/ssh-host-algs
Add new SSH host key types
2 parents 32b1e66 + 27b51f6 commit 7a2c589

File tree

7 files changed

+333
-84
lines changed

7 files changed

+333
-84
lines changed

.classpath

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@
5151
<classpathentry kind="lib" path="ext/commons-logging-1.1.3.jar" sourcepath="ext/src/commons-logging-1.1.3.jar" />
5252
<classpathentry kind="lib" path="ext/commons-codec-1.7.jar" sourcepath="ext/src/commons-codec-1.7.jar" />
5353
<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" />
54-
<classpathentry kind="lib" path="ext/bcprov-jdk15on-1.57.jar" sourcepath="ext/src/bcprov-jdk15on-1.57.jar" />
55-
<classpathentry kind="lib" path="ext/bcmail-jdk15on-1.57.jar" sourcepath="ext/src/bcmail-jdk15on-1.57.jar" />
56-
<classpathentry kind="lib" path="ext/bcpkix-jdk15on-1.57.jar" sourcepath="ext/src/bcpkix-jdk15on-1.57.jar" />
54+
<classpathentry kind="lib" path="ext/bcprov-jdk15on-1.69.jar" sourcepath="ext/src/bcprov-jdk15on-1.69.jar" />
55+
<classpathentry kind="lib" path="ext/bcmail-jdk15on-1.69.jar" sourcepath="ext/src/bcmail-jdk15on-1.69.jar" />
56+
<classpathentry kind="lib" path="ext/bcutil-jdk15on-1.69.jar" sourcepath="ext/src/bcutil-jdk15on-1.69.jar" />
57+
<classpathentry kind="lib" path="ext/bcpkix-jdk15on-1.69.jar" sourcepath="ext/src/bcpkix-jdk15on-1.69.jar" />
5758
<classpathentry kind="lib" path="ext/eddsa-0.2.0.jar" sourcepath="ext/src/eddsa-0.2.0.jar" />
5859
<classpathentry kind="lib" path="ext/sshd-core-1.7.0.jar" sourcepath="ext/src/sshd-core-1.7.0.jar" />
5960
<classpathentry kind="lib" path="ext/mina-core-2.0.21.jar" sourcepath="ext/src/mina-core-2.0.21.jar" />

build.moxie

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ properties: {
111111
lucene.version : 5.5.2
112112
jgit.version : 4.5.7.201904151645-r
113113
groovy.version : 2.4.4
114-
bouncycastle.version : 1.57
114+
bouncycastle.version : 1.69
115115
selenium.version : 2.28.0
116116
wikitext.version : 1.4
117117
sshd.version: 1.7.0

gitblit.iml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -508,35 +508,46 @@
508508
</library>
509509
</orderEntry>
510510
<orderEntry type="module-library">
511-
<library name="bcprov-jdk15on-1.57.jar">
511+
<library name="bcprov-jdk15on-1.69.jar">
512512
<CLASSES>
513-
<root url="jar://$MODULE_DIR$/ext/bcprov-jdk15on-1.57.jar!/" />
513+
<root url="jar://$MODULE_DIR$/ext/bcprov-jdk15on-1.69.jar!/" />
514514
</CLASSES>
515515
<JAVADOC />
516516
<SOURCES>
517-
<root url="jar://$MODULE_DIR$/ext/src/bcprov-jdk15on-1.57.jar!/" />
517+
<root url="jar://$MODULE_DIR$/ext/src/bcprov-jdk15on-1.69.jar!/" />
518518
</SOURCES>
519519
</library>
520520
</orderEntry>
521521
<orderEntry type="module-library">
522-
<library name="bcmail-jdk15on-1.57.jar">
522+
<library name="bcmail-jdk15on-1.69.jar">
523523
<CLASSES>
524-
<root url="jar://$MODULE_DIR$/ext/bcmail-jdk15on-1.57.jar!/" />
524+
<root url="jar://$MODULE_DIR$/ext/bcmail-jdk15on-1.69.jar!/" />
525525
</CLASSES>
526526
<JAVADOC />
527527
<SOURCES>
528-
<root url="jar://$MODULE_DIR$/ext/src/bcmail-jdk15on-1.57.jar!/" />
528+
<root url="jar://$MODULE_DIR$/ext/src/bcmail-jdk15on-1.69.jar!/" />
529529
</SOURCES>
530530
</library>
531531
</orderEntry>
532532
<orderEntry type="module-library">
533-
<library name="bcpkix-jdk15on-1.57.jar">
533+
<library name="bcutil-jdk15on-1.69.jar">
534534
<CLASSES>
535-
<root url="jar://$MODULE_DIR$/ext/bcpkix-jdk15on-1.57.jar!/" />
535+
<root url="jar://$MODULE_DIR$/ext/bcutil-jdk15on-1.69.jar!/" />
536536
</CLASSES>
537537
<JAVADOC />
538538
<SOURCES>
539-
<root url="jar://$MODULE_DIR$/ext/src/bcpkix-jdk15on-1.57.jar!/" />
539+
<root url="jar://$MODULE_DIR$/ext/src/bcutil-jdk15on-1.69.jar!/" />
540+
</SOURCES>
541+
</library>
542+
</orderEntry>
543+
<orderEntry type="module-library">
544+
<library name="bcpkix-jdk15on-1.69.jar">
545+
<CLASSES>
546+
<root url="jar://$MODULE_DIR$/ext/bcpkix-jdk15on-1.69.jar!/" />
547+
</CLASSES>
548+
<JAVADOC />
549+
<SOURCES>
550+
<root url="jar://$MODULE_DIR$/ext/src/bcpkix-jdk15on-1.69.jar!/" />
540551
</SOURCES>
541552
</library>
542553
</orderEntry>

src/main/java/com/gitblit/transport/ssh/FileKeyPairProvider.java

Lines changed: 107 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,81 +18,92 @@
1818
*/
1919
package com.gitblit.transport.ssh;
2020

21+
import java.io.File;
2122
import java.io.FileInputStream;
2223
import java.io.InputStreamReader;
24+
import java.security.KeyFactory;
2325
import java.security.KeyPair;
26+
import java.security.PrivateKey;
27+
import java.security.PublicKey;
2428
import java.util.Arrays;
2529
import java.util.Iterator;
2630
import java.util.NoSuchElementException;
2731

32+
import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
2833
import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
2934
import org.apache.sshd.common.util.security.SecurityUtils;
30-
import org.bouncycastle.openssl.PEMDecryptorProvider;
31-
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
3235
import org.bouncycastle.openssl.PEMKeyPair;
3336
import org.bouncycastle.openssl.PEMParser;
34-
import org.bouncycastle.openssl.PasswordFinder;
3537
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
36-
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
38+
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
39+
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
40+
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
41+
import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil;
42+
import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
43+
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
44+
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
45+
import org.bouncycastle.util.io.pem.PemObject;
46+
import org.bouncycastle.util.io.pem.PemReader;
3747

3848
/**
3949
* This host key provider loads private keys from the specified files.
40-
*
50+
* <p>
4151
* Note that this class has a direct dependency on BouncyCastle and won't work
4252
* unless it has been correctly registered as a security provider.
4353
*
4454
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
4555
*/
46-
public class FileKeyPairProvider extends AbstractKeyPairProvider {
56+
public class FileKeyPairProvider extends AbstractKeyPairProvider
57+
{
4758

4859
private String[] files;
49-
private PasswordFinder passwordFinder;
50-
51-
public FileKeyPairProvider() {
52-
}
5360

54-
public FileKeyPairProvider(String[] files) {
55-
this.files = files;
61+
public FileKeyPairProvider()
62+
{
5663
}
5764

58-
public FileKeyPairProvider(String[] files, PasswordFinder passwordFinder) {
65+
public FileKeyPairProvider(String[] files)
66+
{
5967
this.files = files;
60-
this.passwordFinder = passwordFinder;
6168
}
6269

63-
public String[] getFiles() {
70+
public String[] getFiles()
71+
{
6472
return files;
6573
}
6674

67-
public void setFiles(String[] files) {
75+
public void setFiles(String[] files)
76+
{
6877
this.files = files;
6978
}
7079

71-
public PasswordFinder getPasswordFinder() {
72-
return passwordFinder;
73-
}
74-
75-
public void setPasswordFinder(PasswordFinder passwordFinder) {
76-
this.passwordFinder = passwordFinder;
77-
}
7880

79-
public Iterable<KeyPair> loadKeys() {
81+
@Override
82+
public Iterable<KeyPair> loadKeys()
83+
{
8084
if (!SecurityUtils.isBouncyCastleRegistered()) {
8185
throw new IllegalStateException("BouncyCastle must be registered as a JCE provider");
8286
}
83-
return new Iterable<KeyPair>() {
87+
return new Iterable<KeyPair>()
88+
{
8489
@Override
85-
public Iterator<KeyPair> iterator() {
86-
return new Iterator<KeyPair>() {
90+
public Iterator<KeyPair> iterator()
91+
{
92+
return new Iterator<KeyPair>()
93+
{
8794
private final Iterator<String> iterator = Arrays.asList(files).iterator();
8895
private KeyPair nextKeyPair;
8996
private boolean nextKeyPairSet = false;
97+
9098
@Override
91-
public boolean hasNext() {
99+
public boolean hasNext()
100+
{
92101
return nextKeyPairSet || setNextObject();
93102
}
103+
94104
@Override
95-
public KeyPair next() {
105+
public KeyPair next()
106+
{
96107
if (!nextKeyPairSet) {
97108
if (!setNextObject()) {
98109
throw new NoSuchElementException();
@@ -101,13 +112,22 @@ public KeyPair next() {
101112
nextKeyPairSet = false;
102113
return nextKeyPair;
103114
}
115+
104116
@Override
105-
public void remove() {
117+
public void remove()
118+
{
106119
throw new UnsupportedOperationException();
107120
}
108-
private boolean setNextObject() {
121+
122+
private boolean setNextObject()
123+
{
109124
while (iterator.hasNext()) {
110125
String file = iterator.next();
126+
File f = new File(file);
127+
if (!f.isFile()) {
128+
log.debug("File does not exist, skipping {}", file);
129+
continue;
130+
}
111131
nextKeyPair = doLoadKey(file);
112132
if (nextKeyPair != null) {
113133
nextKeyPairSet = true;
@@ -122,30 +142,71 @@ private boolean setNextObject() {
122142
};
123143
}
124144

125-
protected KeyPair doLoadKey(String file) {
145+
146+
private KeyPair doLoadKey(String file)
147+
{
126148
try {
127-
PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)));
128-
try {
149+
150+
try (PemReader r = new PemReader(new InputStreamReader(new FileInputStream(file)))) {
151+
PemObject pemObject = r.readPemObject();
152+
if ("OPENSSH PRIVATE KEY".equals(pemObject.getType())) {
153+
// This reads a properly OpenSSH formatted ed25519 private key file.
154+
// It is currently unused because the SSHD library in play doesn't work with proper keys.
155+
// This is kept in the hope that in the future the library offers proper support.
156+
try {
157+
byte[] privateKeyContent = pemObject.getContent();
158+
AsymmetricKeyParameter privateKeyParameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(privateKeyContent);
159+
if (privateKeyParameters instanceof Ed25519PrivateKeyParameters) {
160+
OpenSSHPrivateKeySpec privkeySpec = new OpenSSHPrivateKeySpec(privateKeyContent);
161+
162+
Ed25519PublicKeyParameters publicKeyParameters = ((Ed25519PrivateKeyParameters)privateKeyParameters).generatePublicKey();
163+
OpenSSHPublicKeySpec pubKeySpec = new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(publicKeyParameters));
164+
165+
KeyFactory kf = KeyFactory.getInstance("Ed25519", "BC");
166+
PrivateKey privateKey = kf.generatePrivate(privkeySpec);
167+
PublicKey publicKey = kf.generatePublic(pubKeySpec);
168+
return new KeyPair(publicKey, privateKey);
169+
}
170+
else {
171+
log.warn("OpenSSH format is only supported for Ed25519 key type. Unable to read key " + file);
172+
}
173+
}
174+
catch (Exception e) {
175+
log.warn("Unable to read key " + file, e);
176+
}
177+
return null;
178+
}
179+
180+
if ("EDDSA PRIVATE KEY".equals(pemObject.getType())) {
181+
// This reads the ed25519 key from a file format that we created in SshDaemon.
182+
// The type EDDSA PRIVATE KEY was given by us and nothing official.
183+
byte[] privateKeyContent = pemObject.getContent();
184+
PrivateKeyEntryDecoder<? extends PublicKey,? extends PrivateKey> decoder = SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
185+
PrivateKey privateKey = decoder.decodePrivateKey(null, privateKeyContent, 0, privateKeyContent.length);
186+
PublicKey publicKey = SecurityUtils. recoverEDDSAPublicKey(privateKey);
187+
return new KeyPair(publicKey, privateKey);
188+
}
189+
}
190+
191+
try (PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)))) {
129192
Object o = r.readObject();
130193

131194
JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
132195
pemConverter.setProvider("BC");
133-
if (passwordFinder != null && o instanceof PEMEncryptedKeyPair) {
134-
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
135-
PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword());
136-
o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor));
137-
}
138-
139196
if (o instanceof PEMKeyPair) {
140197
o = pemConverter.getKeyPair((PEMKeyPair)o);
141-
return (KeyPair) o;
142-
} else if (o instanceof KeyPair) {
143-
return (KeyPair) o;
198+
return (KeyPair)o;
199+
}
200+
else if (o instanceof KeyPair) {
201+
return (KeyPair)o;
202+
}
203+
else {
204+
log.warn("Cannot read unsupported PEM object of type: " + o.getClass().getCanonicalName());
144205
}
145-
} finally {
146-
r.close();
147206
}
148-
} catch (Exception e) {
207+
208+
}
209+
catch (Exception e) {
149210
log.warn("Unable to read key " + file, e);
150211
}
151212
return null;

0 commit comments

Comments
 (0)