mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-1769 improved Ghidra Server SSH key use and error handling. Replaced use of ganymed-ssh2 library with Bouncy Castle library use.
This commit is contained in:
parent
1a1d06b749
commit
8c209ce76e
15 changed files with 384 additions and 314 deletions
|
@ -1 +0,0 @@
|
|||
MODULE FILE LICENSE: lib/ganymed-ssh2-262.jar Christian Plattner
|
|
@ -26,7 +26,5 @@ dependencies {
|
|||
api project(':Generic')
|
||||
api project(':DB')
|
||||
api project(':Docking')
|
||||
api "ch.ethz.ganymed:ganymed-ssh2:262@jar"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
##VERSION: 2.0
|
||||
##MODULE IP: Christian Plattner
|
||||
Module.manifest||GHIDRA||||END|
|
||||
src/main/java/ghidra/framework/client/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/ghidra/framework/store/db/package.html||GHIDRA||reviewed||END|
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.Component;
|
|||
import java.io.*;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
import javax.security.auth.callback.*;
|
||||
|
||||
|
@ -86,29 +87,23 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
|
|||
ClientUtil.setClientAuthenticator(authenticator);
|
||||
|
||||
if (keystorePath != null) {
|
||||
File f = new File(keystorePath);
|
||||
if (!f.exists()) {
|
||||
File keyfile = new File(keystorePath);
|
||||
if (!keyfile.exists()) {
|
||||
// If keystorePath file not found - try accessing as SSH key resource stream
|
||||
// InputStream keyIn = ResourceManager.getResourceAsStream(keystorePath);
|
||||
InputStream keyIn = keystorePath.getClass().getResourceAsStream(keystorePath);
|
||||
if (keyIn != null) {
|
||||
try {
|
||||
sshPrivateKey = SSHKeyManager.getSSHPrivateKey(keyIn);
|
||||
Msg.info(HeadlessClientAuthenticator.class,
|
||||
"Loaded SSH key: " + keystorePath);
|
||||
return;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(HeadlessClientAuthenticator.class,
|
||||
"Failed to open keystore for SSH use: " + keystorePath, e);
|
||||
throw new IOException("Failed to parse keystore: " + keystorePath);
|
||||
}
|
||||
finally {
|
||||
try (InputStream keyIn =
|
||||
HeadlessClientAuthenticator.class.getResourceAsStream(keystorePath)) {
|
||||
if (keyIn != null) {
|
||||
try {
|
||||
keyIn.close();
|
||||
sshPrivateKey = SSHKeyManager.getSSHPrivateKey(keyIn);
|
||||
Msg.info(HeadlessClientAuthenticator.class,
|
||||
"Loaded SSH key: " + keystorePath);
|
||||
return;
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
catch (Exception e) {
|
||||
Msg.error(HeadlessClientAuthenticator.class,
|
||||
"Failed to open keystore for SSH use: " + keystorePath, e);
|
||||
throw new IOException("Failed to parse keystore: " + keystorePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,24 +111,27 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
|
|||
throw new FileNotFoundException("Keystore not found: " + keystorePath);
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
sshPrivateKey = SSHKeyManager.getSSHPrivateKey(new File(keystorePath));
|
||||
sshPrivateKey = SSHKeyManager.getSSHPrivateKey(keyfile);
|
||||
success = true;
|
||||
Msg.info(HeadlessClientAuthenticator.class, "Loaded SSH key: " + keystorePath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
try {
|
||||
// try keystore as PKI keystore if failed as SSH keystore
|
||||
ApplicationKeyManagerFactory.setKeyStore(keystorePath, false);
|
||||
Msg.info(HeadlessClientAuthenticator.class, "Loaded PKI key: " + keystorePath);
|
||||
}
|
||||
catch (IOException e1) {
|
||||
Msg.error(HeadlessClientAuthenticator.class,
|
||||
"Failed to open keystore for PKI use: " + keystorePath, e1);
|
||||
Msg.error(HeadlessClientAuthenticator.class,
|
||||
"Failed to open keystore for SSH use: " + keystorePath, e);
|
||||
throw new IOException("Failed to parse keystore: " + keystorePath);
|
||||
catch (InvalidKeyException e) { // keyfile is not a valid SSH provate key format
|
||||
// does not appear to be an SSH private key - try PKI keystore parse
|
||||
if (ApplicationKeyManagerFactory.setKeyStore(keystorePath, false)) {
|
||||
success = true;
|
||||
Msg.info(HeadlessClientAuthenticator.class,
|
||||
"Loaded PKI keystore: " + keystorePath);
|
||||
}
|
||||
}
|
||||
catch (IOException e) { // SSH key parse failure only
|
||||
Msg.error(HeadlessClientAuthenticator.class,
|
||||
"Failed to open keystore for SSH use: " + keystorePath, e);
|
||||
}
|
||||
if (!success) {
|
||||
throw new IOException("Failed to parse keystore: " + keystorePath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sshPrivateKey = null;
|
||||
|
|
|
@ -15,14 +15,17 @@
|
|||
*/
|
||||
package ghidra.framework.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.security.SecureRandom;
|
||||
import java.io.*;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
|
||||
import ch.ethz.ssh2.signature.*;
|
||||
import generic.random.SecureRandomFactory;
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.DataLengthException;
|
||||
import org.bouncycastle.crypto.digests.SHA1Digest;
|
||||
import org.bouncycastle.crypto.params.DSAKeyParameters;
|
||||
import org.bouncycastle.crypto.params.RSAKeyParameters;
|
||||
import org.bouncycastle.crypto.signers.*;
|
||||
import org.bouncycastle.util.Strings;
|
||||
|
||||
/**
|
||||
* <code>SSHSignatureCallback</code> provides a Callback implementation used
|
||||
|
@ -45,6 +48,7 @@ public class SSHSignatureCallback implements Callback, Serializable {
|
|||
/**
|
||||
* Construct callback with a random token to be signed by the client.
|
||||
* @param token random bytes to be signed
|
||||
* @param serverSignature server signature of token (using server PKI)
|
||||
*/
|
||||
public SSHSignatureCallback(byte[] token, byte[] serverSignature) {
|
||||
this.token = token;
|
||||
|
@ -66,6 +70,7 @@ public class SSHSignatureCallback implements Callback, Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the server signature of token (using server PKI)
|
||||
* @return the server's signature of the token bytes.
|
||||
*/
|
||||
public byte[] getServerSignature() {
|
||||
|
@ -80,28 +85,77 @@ public class SSHSignatureCallback implements Callback, Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sign this challenge with the specified SSH private key.
|
||||
* @param sshPrivateKey RSAPrivateKey or DSAPrivateKey
|
||||
* @throws IOException if signature generation failed
|
||||
* @see RSAPrivateKey
|
||||
* @see DSAPrivateKey
|
||||
* Write UInt32 to an SSH-encoded buffer.
|
||||
* (modeled after org.bouncycastle.crypto.util.SSHBuilder.u32(int))
|
||||
* @param value integer value
|
||||
* @param out data output stream
|
||||
*/
|
||||
public void sign(Object sshPrivateKey) throws IOException {
|
||||
if (sshPrivateKey instanceof RSAPrivateKey) {
|
||||
RSAPrivateKey key = (RSAPrivateKey) sshPrivateKey;
|
||||
// TODO: verify correct key by using accepted public key fingerprint
|
||||
RSASignature rsaSignature = RSASHA1Verify.generateSignature(token, key);
|
||||
signature = RSASHA1Verify.encodeSSHRSASignature(rsaSignature);
|
||||
private static void sshBuilderWriteUInt32(int value, ByteArrayOutputStream out) {
|
||||
byte[] tmp = new byte[4];
|
||||
tmp[0] = (byte) ((value >>> 24) & 0xff);
|
||||
tmp[1] = (byte) ((value >>> 16) & 0xff);
|
||||
tmp[2] = (byte) ((value >>> 8) & 0xff);
|
||||
tmp[3] = (byte) (value & 0xff);
|
||||
out.writeBytes(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write byte array to an SSH-encoded buffer.
|
||||
* (modeled after org.bouncycastle.crypto.util.SSHBuilder.writeBlock(byte[])
|
||||
* @param value byte array
|
||||
* @param out data output stream
|
||||
*/
|
||||
private static void sshBuilderWriteBlock(byte[] value, ByteArrayOutputStream out) {
|
||||
sshBuilderWriteUInt32(value.length, out);
|
||||
out.writeBytes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write string to an SSH-encoded buffer.
|
||||
* (modeled after org.bouncycastle.crypto.util.SSHBuilder.writeString(String)
|
||||
* @param str string data
|
||||
* @param out data output stream
|
||||
*/
|
||||
private static void sshBuilderWriteString(String str, ByteArrayOutputStream out) {
|
||||
sshBuilderWriteBlock(Strings.toByteArray(str), out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign this challenge with the specified SSH private key.
|
||||
* @param privateKeyParameters SSH private key parameters
|
||||
* ({@link RSAKeyParameters} or {@link RSAKeyParameters})
|
||||
* @throws IOException if signature generation failed
|
||||
*/
|
||||
public void sign(Object privateKeyParameters) throws IOException {
|
||||
try {
|
||||
// NOTE: Signature is formatted consistent with legacy implementation
|
||||
// for backward compatibility
|
||||
if (privateKeyParameters instanceof RSAKeyParameters) {
|
||||
RSAKeyParameters cipherParams = (RSAKeyParameters) privateKeyParameters;
|
||||
RSADigestSigner signer = new RSADigestSigner(new SHA1Digest());
|
||||
signer.init(true, cipherParams);
|
||||
signer.update(token, 0, token.length);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
sshBuilderWriteString("ssh-rsa", out);
|
||||
sshBuilderWriteBlock(signer.generateSignature(), out);
|
||||
signature = out.toByteArray();
|
||||
}
|
||||
else if (privateKeyParameters instanceof DSAKeyParameters) {
|
||||
DSAKeyParameters cipherParams = (DSAKeyParameters) privateKeyParameters;
|
||||
DSADigestSigner signer = new DSADigestSigner(new DSASigner(), new SHA1Digest());
|
||||
signer.init(true, cipherParams);
|
||||
signer.update(token, 0, token.length);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
sshBuilderWriteString("ssh-dss", out);
|
||||
sshBuilderWriteBlock(signer.generateSignature(), out);
|
||||
signature = out.toByteArray();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported SSH private key");
|
||||
}
|
||||
}
|
||||
else if (sshPrivateKey instanceof DSAPrivateKey) {
|
||||
DSAPrivateKey key = (DSAPrivateKey) sshPrivateKey;
|
||||
// TODO: verify correct key by using accepted public key fingerprint
|
||||
SecureRandom random = SecureRandomFactory.getSecureRandom();
|
||||
DSASignature dsaSignature = DSASHA1Verify.generateSignature(token, key, random);
|
||||
signature = DSASHA1Verify.encodeSSHDSASignature(dsaSignature);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported SSH private key");
|
||||
catch (DataLengthException | CryptoException e) {
|
||||
throw new IOException("Cannot generate SSH signature: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,26 +15,30 @@
|
|||
*/
|
||||
package ghidra.framework.remote.security;
|
||||
|
||||
import ghidra.security.KeyStorePasswordProvider;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
|
||||
import ch.ethz.ssh2.crypto.Base64;
|
||||
import ch.ethz.ssh2.crypto.PEMDecoder;
|
||||
import ch.ethz.ssh2.signature.*;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.params.DSAKeyParameters;
|
||||
import org.bouncycastle.crypto.params.RSAKeyParameters;
|
||||
import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil;
|
||||
import org.bouncycastle.crypto.util.PrivateKeyFactory;
|
||||
import org.bouncycastle.openssl.*;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import ghidra.security.KeyStorePasswordProvider;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class SSHKeyManager {
|
||||
|
||||
// private static final String DEFAULT_KEYSTORE_PATH =
|
||||
// System.getProperty("user.home") + File.separator + ".ssh/id_rsa";
|
||||
//
|
||||
// /**
|
||||
// * Preference name for the SSH key file paths
|
||||
// */
|
||||
// private static final String SSH_KEYSTORE_PROPERTY = "ghidra.sshKeyFile";
|
||||
|
||||
// The public key file is derived by adding this extension to the key store filename
|
||||
//private static final String SSH_PUBLIC_KEY_EXT1 = ".pub";
|
||||
static {
|
||||
// For JcaPEMKeyConverter().setProvider("BC")
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private static KeyStorePasswordProvider passwordProvider;
|
||||
|
||||
|
@ -45,148 +48,143 @@ public class SSHKeyManager {
|
|||
|
||||
/**
|
||||
* Set PKI protected keystore password provider
|
||||
* @param provider
|
||||
* @param provider key store password provider
|
||||
*/
|
||||
public static synchronized void setProtectedKeyStorePasswordProvider(
|
||||
KeyStorePasswordProvider provider) {
|
||||
passwordProvider = provider;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Return the SSH private key for the current user. The default ~/.ssh/id_rsa file
|
||||
// * will be used unless the System property <i>ghidra.sshKeyFile</i> has been set.
|
||||
// * If the corresponding key file is encrypted the currently installed password
|
||||
// * provider will be used to obtain the decrypt password.
|
||||
// * @return RSAPrivateKey or DSAPrivateKey
|
||||
// * @throws FileNotFoundException key file not found
|
||||
// * @throws IOException if key file not found or key parse failed
|
||||
// * @see RSAPrivateKey
|
||||
// * @see DSAPrivateKey
|
||||
// */
|
||||
// public static Object getUsersSSHPrivateKey() throws IOException {
|
||||
//
|
||||
// String privateKeyStorePath = System.getProperty(SSH_KEYSTORE_PROPERTY);
|
||||
// if (privateKeyStorePath == null) {
|
||||
// privateKeyStorePath = DEFAULT_KEYSTORE_PATH;
|
||||
// }
|
||||
//
|
||||
// return getSSHPrivateKey(new File(privateKeyStorePath));
|
||||
// }
|
||||
|
||||
/**
|
||||
* Return the SSH private key corresponding to the specified key file.
|
||||
* If the specified key file is encrypted the currently installed password
|
||||
* provider will be used to obtain the decrypt password.
|
||||
* @param sshPrivateKeyFile
|
||||
* @return RSAPrivateKey or DSAPrivateKey
|
||||
* @param sshPrivateKeyFile private ssh key file
|
||||
* @return private key cipher parameters ({@link RSAKeyParameters} or {@link DSAKeyParameters})
|
||||
* @throws FileNotFoundException key file not found
|
||||
* @throws IOException if key file not found or key parse failed
|
||||
* @see RSAPrivateKey
|
||||
* @see DSAPrivateKey
|
||||
* @throws InvalidKeyException if key is not an SSH private key (i.e., PEM format)
|
||||
*/
|
||||
public static Object getSSHPrivateKey(File sshPrivateKeyFile) throws IOException {
|
||||
public static CipherParameters getSSHPrivateKey(File sshPrivateKeyFile)
|
||||
throws InvalidKeyException, IOException {
|
||||
|
||||
if (!sshPrivateKeyFile.isFile()) {
|
||||
throw new FileNotFoundException("SSH private key file not found: " + sshPrivateKeyFile);
|
||||
}
|
||||
|
||||
InputStream keyIn = new FileInputStream(sshPrivateKeyFile);
|
||||
try {
|
||||
try (InputStream keyIn = new FileInputStream(sshPrivateKeyFile)) {
|
||||
return getSSHPrivateKey(keyIn, sshPrivateKeyFile.getAbsolutePath());
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
keyIn.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SSH private key corresponding to the specified key input stream.
|
||||
* If the specified key is encrypted the currently installed password
|
||||
* provider will be used to obtain the decrypt password.
|
||||
* @param sshPrivateKeyIn
|
||||
* @return RSAPrivateKey or DSAPrivateKey
|
||||
* @param sshPrivateKeyIn private ssh key resource input stream
|
||||
* @return private key cipher parameters ({@link RSAKeyParameters} or {@link DSAKeyParameters})
|
||||
* @throws FileNotFoundException key file not found
|
||||
* @throws IOException if key file not found or key parse failed
|
||||
* @see RSAPrivateKey
|
||||
* @see DSAPrivateKey
|
||||
* @throws InvalidKeyException if key is not an SSH private key (i.e., PEM format)
|
||||
*/
|
||||
public static Object getSSHPrivateKey(InputStream sshPrivateKeyIn) throws IOException {
|
||||
public static CipherParameters getSSHPrivateKey(InputStream sshPrivateKeyIn)
|
||||
throws InvalidKeyException, IOException {
|
||||
return getSSHPrivateKey(sshPrivateKeyIn, "Protected SSH Key");
|
||||
}
|
||||
|
||||
private static Object getSSHPrivateKey(InputStream sshPrivateKeyIn, String srcName)
|
||||
throws IOException {
|
||||
private static CipherParameters getSSHPrivateKey(InputStream sshPrivateKeyIn, String srcName)
|
||||
throws InvalidKeyException, IOException {
|
||||
|
||||
boolean isEncrypted = false;
|
||||
StringBuffer keyBuf = new StringBuffer();
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(sshPrivateKeyIn));
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("Proc-Type:")) {
|
||||
isEncrypted = (line.indexOf("ENCRYPTED") > 0);
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(sshPrivateKeyIn))) {
|
||||
boolean checkKeyFormat = true;
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (checkKeyFormat) {
|
||||
if (!line.startsWith("-----BEGIN ") || line.indexOf(" KEY-----") < 0) {
|
||||
throw new InvalidKeyException("Invalid SSH Private Key");
|
||||
}
|
||||
if (!line.startsWith("-----BEGIN RSA PRIVATE KEY-----") &&
|
||||
!line.startsWith("-----BEGIN DSA PRIVATE KEY-----")) {
|
||||
Msg.error(SSHKeyManager.class,
|
||||
"Unsupported SSH Key Format (see svrREADME.html)");
|
||||
throw new IOException("Unsupported SSH Private Key");
|
||||
}
|
||||
checkKeyFormat = false;
|
||||
}
|
||||
if (keyBuf.length() != 0) {
|
||||
keyBuf.append('\n');
|
||||
}
|
||||
keyBuf.append(line);
|
||||
}
|
||||
keyBuf.append(line);
|
||||
keyBuf.append('\n');
|
||||
}
|
||||
r.close();
|
||||
|
||||
String password = null;
|
||||
if (isEncrypted) {
|
||||
char[] pwd = passwordProvider.getKeyStorePassword(srcName, false);
|
||||
if (pwd == null) {
|
||||
throw new IOException("Password required to open SSH private keystore");
|
||||
}
|
||||
// Don't like using String for password - but API doesn't give us a choice
|
||||
password = new String(pwd);
|
||||
}
|
||||
|
||||
return PEMDecoder.decode(keyBuf.toString().toCharArray(), password);
|
||||
char[] password = null;
|
||||
try (Reader r = new StringReader(keyBuf.toString())) {
|
||||
|
||||
PEMParser pemParser = new PEMParser(r);
|
||||
Object object = pemParser.readObject();
|
||||
|
||||
PrivateKeyInfo privateKeyInfo;
|
||||
if (object instanceof PEMEncryptedKeyPair) {
|
||||
|
||||
password = passwordProvider.getKeyStorePassword(srcName, false);
|
||||
if (password == null) {
|
||||
throw new IOException("Password required to open SSH private keystore");
|
||||
}
|
||||
|
||||
// Encrypted key - we will use provided password
|
||||
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) object;
|
||||
PEMDecryptorProvider decProv =
|
||||
new JcePEMDecryptorProviderBuilder().build(password);
|
||||
privateKeyInfo = ckp.decryptKeyPair(decProv).getPrivateKeyInfo();
|
||||
}
|
||||
else {
|
||||
// Unencrypted key - no password needed
|
||||
PEMKeyPair ukp = (PEMKeyPair) object;
|
||||
privateKeyInfo = ukp.getPrivateKeyInfo();
|
||||
}
|
||||
return PrivateKeyFactory.createKey(privateKeyInfo);
|
||||
}
|
||||
finally {
|
||||
if (password != null) {
|
||||
Arrays.fill(password, (char) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to instantiate an SSH public key from the specified file
|
||||
* which contains a single public key.
|
||||
* @param sshPublicKeyFile
|
||||
* @return RSAPublicKey or DSAPublicKey
|
||||
* @param sshPublicKeyFile public ssh key file
|
||||
* @return public key cipher parameters {@link RSAKeyParameters} or {@link DSAKeyParameters}
|
||||
* @throws FileNotFoundException key file not found
|
||||
* @throws IOException if key file not found or key parse failed
|
||||
* @see RSAPublicKey
|
||||
* @see DSAPublicKey
|
||||
*/
|
||||
public static Object getSSHPublicKey(File sshPublicKeyFile) throws IOException {
|
||||
public static CipherParameters getSSHPublicKey(File sshPublicKeyFile) throws IOException {
|
||||
|
||||
BufferedReader r = new BufferedReader(new FileReader(sshPublicKeyFile));
|
||||
String keyLine = null;
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (!line.startsWith("ssh-")) {
|
||||
continue;
|
||||
try (BufferedReader r = new BufferedReader(new FileReader(sshPublicKeyFile))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (!line.startsWith("ssh-")) {
|
||||
continue;
|
||||
}
|
||||
keyLine = line;
|
||||
break;
|
||||
}
|
||||
keyLine = line;
|
||||
break;
|
||||
}
|
||||
r.close();
|
||||
|
||||
if (keyLine != null) {
|
||||
String[] pieces = keyLine.split(" ");
|
||||
if (pieces.length >= 2) {
|
||||
byte[] pubkeyBytes = Base64.decode(pieces[1].toCharArray());
|
||||
if ("ssh-rsa".equals(pieces[0])) {
|
||||
return RSASHA1Verify.decodeSSHRSAPublicKey(pubkeyBytes);
|
||||
}
|
||||
else if ("ssh-dsa".equals(pieces[0])) {
|
||||
return DSASHA1Verify.decodeSSHDSAPublicKey(pubkeyBytes);
|
||||
}
|
||||
String[] part = keyLine.split("\\s+");
|
||||
if (part.length >= 2 && part[0].startsWith("ssh-")) {
|
||||
byte[] pubkeyBytes = Base64.decode(part[1]);
|
||||
return OpenSSHPublicKeyUtil.parsePublicKey(pubkeyBytes);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException(
|
||||
"Invalid SSH public key file, valid ssh-rsa or ssh-dsa entry not found: " +
|
||||
"Invalid SSH public key file, supported SSH public key not found: " +
|
||||
sshPublicKeyFile);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue