Add overloaded init methods that take the public key from a stream an… (#908)

* Add overloaded init methods that take the public key from a stream and properly initialize. Resolves #907.

* Override public key.
This commit is contained in:
David Kocher
2024-04-29 16:46:38 +02:00
committed by GitHub
parent 607e80591c
commit 09e2ca512e
5 changed files with 107 additions and 25 deletions

View File

@@ -20,6 +20,7 @@ import com.hierynomus.sshj.common.KeyDecryptionFailedException;
import com.hierynomus.sshj.transport.cipher.BlockCiphers; import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.ChachaPolyCiphers; import com.hierynomus.sshj.transport.cipher.ChachaPolyCiphers;
import com.hierynomus.sshj.transport.cipher.GcmCiphers; import com.hierynomus.sshj.transport.cipher.GcmCiphers;
import com.hierynomus.sshj.userauth.keyprovider.bcrypt.BCrypt;
import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
@@ -29,24 +30,23 @@ import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider; import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider; import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat; import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import com.hierynomus.sshj.userauth.keyprovider.bcrypt.BCrypt;
import org.bouncycastle.openssl.EncryptionException; import org.bouncycastle.openssl.EncryptionException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.BufferedReader; import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.*; import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPrivateKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.Arrays; import java.util.Arrays;
@@ -83,6 +83,12 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
private PublicKey pubKey; private PublicKey pubKey;
@Override
public PublicKey getPublic()
throws IOException {
return pubKey != null ? pubKey : super.getPublic();
}
public static class Factory public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> { implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@@ -100,16 +106,41 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
protected final Logger log = LoggerFactory.getLogger(getClass()); protected final Logger log = LoggerFactory.getLogger(getClass());
@Override @Override
public void init(File location) { public void init(File location, PasswordFinder pwdf) {
File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(location); File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(location);
if (pubKey != null) if (pubKey != null) {
try { try {
initPubKey(new FileReader(pubKey)); initPubKey(new FileReader(pubKey));
} catch (IOException e) { } catch (IOException e) {
// let super provide both public & private key // let super provide both public & private key
log.warn("Error reading public key file: {}", e.toString()); log.warn("Error reading public key file: {}", e.toString());
} }
super.init(location); }
super.init(location, pwdf);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
if (pubKey != null) {
try {
initPubKey(new StringReader(publicKey));
} catch (IOException e) {
log.warn("Error reading public key file: {}", e.toString());
}
}
super.init(privateKey, null, pwdf);
}
@Override
public void init(Reader privateKey, Reader publicKey, PasswordFinder pwdf) {
if (pubKey != null) {
try {
initPubKey(publicKey);
} catch (IOException e) {
log.warn("Error reading public key file: {}", e.toString());
}
}
super.init(privateKey, null, pwdf);
} }
@Override @Override

View File

@@ -34,38 +34,47 @@ public abstract class BaseFileKeyProvider implements FileKeyProvider {
@Override @Override
public void init(Reader location) { public void init(Reader location) {
assert location != null; this.init(location, (PasswordFinder) null);
resource = new PrivateKeyReaderResource(location);
} }
@Override @Override
public void init(Reader location, PasswordFinder pwdf) { public void init(Reader location, PasswordFinder pwdf) {
init(location); this.init(location, null, pwdf);
}
@Override
public void init(Reader privateKey, Reader publicKey) {
this.init(privateKey, publicKey, null);
}
@Override
public void init(Reader privateKey, Reader publicKey, PasswordFinder pwdf) {
assert publicKey == null;
this.resource = new PrivateKeyReaderResource(privateKey);
this.pwdf = pwdf; this.pwdf = pwdf;
} }
@Override @Override
public void init(File location) { public void init(File location) {
assert location != null; this.init(location, null);
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
} }
@Override @Override
public void init(File location, PasswordFinder pwdf) { public void init(File location, PasswordFinder pwdf) {
init(location); this.resource = new PrivateKeyFileResource(location.getAbsoluteFile());
this.pwdf = pwdf; this.pwdf = pwdf;
} }
@Override @Override
public void init(String privateKey, String publicKey) { public void init(String privateKey, String publicKey) {
assert privateKey != null; this.init(privateKey, publicKey, null);
assert publicKey == null;
resource = new PrivateKeyStringResource(privateKey);
} }
@Override @Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) { public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey); assert privateKey != null;
assert publicKey == null;
this.resource = new PrivateKeyStringResource(privateKey);
this.pwdf = pwdf; this.pwdf = pwdf;
} }

View File

@@ -30,6 +30,10 @@ public interface FileKeyProvider
void init(Reader location); void init(Reader location);
void init(Reader privateKey, Reader publicKey);
void init(Reader privateKey, Reader publicKey, PasswordFinder pwdf);
void init(Reader location, PasswordFinder pwdf); void init(Reader location, PasswordFinder pwdf);
void init(String privateKey, String publicKey); void init(String privateKey, String publicKey);

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.userauth.keyprovider; package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyFileUtil; import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyFileUtil;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import java.io.*; import java.io.*;
import java.security.PublicKey; import java.security.PublicKey;
@@ -54,21 +55,22 @@ public class OpenSSHKeyFile
} }
@Override @Override
public void init(File location) { public void init(File location, PasswordFinder pwdf) {
// try cert key location first // try cert key location first
File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(location); File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(location);
if (pubKey != null) if (pubKey != null) {
try { try {
initPubKey(new FileReader(pubKey)); initPubKey(new FileReader(pubKey));
} catch (IOException e) { } catch (IOException e) {
// let super provide both public & private key // let super provide both public & private key
log.warn("Error reading public key file: {}", e.toString()); log.warn("Error reading public key file: {}", e.toString());
} }
super.init(location); }
super.init(location, pwdf);
} }
@Override @Override
public void init(String privateKey, String publicKey) { public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
if (publicKey != null) { if (publicKey != null) {
try { try {
initPubKey(new StringReader(publicKey)); initPubKey(new StringReader(publicKey));
@@ -77,7 +79,20 @@ public class OpenSSHKeyFile
log.warn("Error reading public key: {}", e.toString()); log.warn("Error reading public key: {}", e.toString());
} }
} }
super.init(privateKey, null); super.init(privateKey, null, pwdf);
}
@Override
public void init(Reader privateKey, Reader publicKey, PasswordFinder pwdf) {
if (publicKey != null) {
try {
initPubKey(publicKey);
} catch (IOException e) {
// let super provide both public & private key
log.warn("Error reading public key: {}", e.toString());
}
}
super.init(privateKey, null, pwdf);
} }
/** /**

View File

@@ -381,6 +381,18 @@ public class OpenSSHKeyFileTest {
} }
@Test
public void shouldSuccessfullyLoadSignedRSAPublicKeyFromStream() throws IOException {
FileKeyProvider keyFile = new OpenSSHKeyFile();
keyFile.init(new FileReader("src/test/resources/keytypes/certificate/test_rsa"),
new FileReader("src/test/resources/keytypes/certificate/test_rsa.pub"),
PasswordUtils.createOneOff(correctPassphrase));
assertNotNull(keyFile.getPrivate());
PublicKey pubKey = keyFile.getPublic();
assertNotNull(pubKey);
assertEquals("RSA", pubKey.getAlgorithm());
}
@Test @Test
public void shouldSuccessfullyLoadSignedRSAPublicKeyWithMaxDate() throws IOException { public void shouldSuccessfullyLoadSignedRSAPublicKeyWithMaxDate() throws IOException {
FileKeyProvider keyFile = new OpenSSHKeyFile(); FileKeyProvider keyFile = new OpenSSHKeyFile();
@@ -422,6 +434,17 @@ public class OpenSSHKeyFileTest {
assertEquals("", certificate.getExtensions().get("permit-pty")); assertEquals("", certificate.getExtensions().get("permit-pty"));
} }
@Test
public void shouldSuccessfullyLoadSignedDSAPublicKeyFromStream() throws IOException {
FileKeyProvider keyFile = new OpenSSHKeyFile();
keyFile.init(new FileReader("src/test/resources/keytypes/certificate/test_dsa"),
new FileReader("src/test/resources/keytypes/certificate/test_dsa-cert.pub"),
PasswordUtils.createOneOff(correctPassphrase));
assertNotNull(keyFile.getPrivate());
PublicKey pubKey = keyFile.getPublic();
assertEquals("DSA", pubKey.getAlgorithm());
}
/** /**
* Sometimes users copy-pastes private and public keys in text editors. It leads to redundant * Sometimes users copy-pastes private and public keys in text editors. It leads to redundant
* spaces and newlines. OpenSSH can easily read such keys, so users expect from SSHJ the same. * spaces and newlines. OpenSSH can easily read such keys, so users expect from SSHJ the same.