Removed GNUmake-related files from source control

Removed 3rd-party JARs from source control
Re-introduced original BouncyCastle-dependent PKCS5KeyFile class
Re-named non-BouncyCastle-dependent PKCS8KeyFile class to PKCS5KeyFile, since it really only supports PKCS5 formats anyway
This commit is contained in:
David Solin
2016-08-13 10:03:52 -05:00
parent 09616c4834
commit e7c50165c7
13 changed files with 354 additions and 442 deletions

View File

@@ -1,46 +0,0 @@
Default: all
TOP=$(realpath .)
include $(TOP)/../DeveloperTools/install/common.mk
RSRC=rsrc
LIBDIR=$(RSRC)/lib
LIB=$(subst $(SPACE),$(CLN),$(filter %.jar %.zip, $(wildcard $(LIBDIR)/*)))
BUILD=build
SRC=src/main/java
DOCS=docs
CLASSPATH="$(CLASSLIB)$(CLN)$(LIB)$(CLN)$(SRC)"
CWD=$(shell pwd)
include classes.mk
CLASS_FILES:=$(foreach class, $(CLASSES), $(BUILD)/$(subst .,/,$(class)).class)
PACKAGES=$(sort $(basename $(CLASSES)))
PACKAGEDIRS=$(subst .,/,$(PACKAGES))
all: sshj.jar
sshj.jar: classes
$(JAR) cvf $@ -C $(BUILD)/ .
javadocs:
mkdir -p $(DOCS)
$(JAVA_HOME)/bin/javadoc -d $(DOCS) -classpath $(CLASSPATH) $(PACKAGES)
clean:
rm -rf $(BUILD)
classes: classdirs $(CLASS_FILES)
install: all
cp sshj.jar $(TOP)/../jOVAL-Commercial/components/wsmv/winrs/rsrc/lib
cp sshj.jar $(TOP)/../jOVAL-Commercial/components/provider/remote/rsrc/lib
cp sshj.jar $(TOP)/../jOVAL-Commercial/components/sdk/dist/3rd-party
classdirs: $(foreach pkg, $(PACKAGEDIRS), $(BUILD)/$(pkg)/)
$(BUILD)/%.class: $(SRC)/%.java
$(JAVAC) $(JAVACFLAGS) -d $(BUILD) -classpath $(CLASSPATH) $<
$(BUILD)/%/:
mkdir -p $(subst PKG,,$@)

View File

@@ -1,211 +0,0 @@
CLASSES=\
com.hierynomus.sshj.backport.JavaVersion \
com.hierynomus.sshj.backport.Jdk7HttpProxySocket \
com.hierynomus.sshj.backport.Sockets \
com.hierynomus.sshj.secg.SecgUtils \
com.hierynomus.sshj.signature.Ed25519PublicKey \
com.hierynomus.sshj.signature.SignatureEdDSA \
com.hierynomus.sshj.transport.cipher.BlockCiphers \
com.hierynomus.sshj.transport.cipher.ExtendedBlockCiphers \
com.hierynomus.sshj.transport.cipher.StreamCipher \
com.hierynomus.sshj.transport.cipher.StreamCiphers \
com.hierynomus.sshj.transport.IdentificationStringParser \
net.schmizz.concurrent.ErrorDeliveryUtil \
net.schmizz.concurrent.Event \
net.schmizz.concurrent.ExceptionChainer \
net.schmizz.concurrent.Promise \
net.schmizz.keepalive.Heartbeater \
net.schmizz.keepalive.KeepAlive \
net.schmizz.keepalive.KeepAliveProvider \
net.schmizz.keepalive.KeepAliveRunner \
net.schmizz.sshj.AbstractService \
net.schmizz.sshj.AndroidConfig \
net.schmizz.sshj.common.Base64 \
net.schmizz.sshj.common.Buffer \
net.schmizz.sshj.common.ByteArrayUtils \
net.schmizz.sshj.common.DisconnectReason \
net.schmizz.sshj.common.ErrorNotifiable \
net.schmizz.sshj.common.Factory \
net.schmizz.sshj.common.IOUtils \
net.schmizz.sshj.common.KeyType \
net.schmizz.sshj.common.Message \
net.schmizz.sshj.common.SecurityUtils \
net.schmizz.sshj.common.SSHException \
net.schmizz.sshj.common.SSHPacket \
net.schmizz.sshj.common.SSHPacketHandler \
net.schmizz.sshj.common.SSHRuntimeException \
net.schmizz.sshj.common.StreamCopier \
net.schmizz.sshj.Config \
net.schmizz.sshj.ConfigImpl \
net.schmizz.sshj.connection.channel.AbstractChannel \
net.schmizz.sshj.connection.channel.Channel \
net.schmizz.sshj.connection.channel.ChannelInputStream \
net.schmizz.sshj.connection.channel.ChannelOutputStream \
net.schmizz.sshj.connection.channel.direct.AbstractDirectChannel \
net.schmizz.sshj.connection.channel.direct.LocalPortForwarder \
net.schmizz.sshj.connection.channel.direct.PTYMode \
net.schmizz.sshj.connection.channel.direct.Session \
net.schmizz.sshj.connection.channel.direct.SessionChannel \
net.schmizz.sshj.connection.channel.direct.SessionFactory \
net.schmizz.sshj.connection.channel.direct.Signal \
net.schmizz.sshj.connection.channel.forwarded.AbstractForwardedChannel \
net.schmizz.sshj.connection.channel.forwarded.AbstractForwardedChannelOpener \
net.schmizz.sshj.connection.channel.forwarded.ConnectListener \
net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener \
net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder \
net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectListener \
net.schmizz.sshj.connection.channel.forwarded.X11Forwarder \
net.schmizz.sshj.connection.channel.OpenFailException \
net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor \
net.schmizz.sshj.connection.channel.Window \
net.schmizz.sshj.connection.Connection \
net.schmizz.sshj.connection.ConnectionException \
net.schmizz.sshj.connection.ConnectionImpl \
net.schmizz.sshj.DefaultConfig \
net.schmizz.sshj.Service \
net.schmizz.sshj.sftp.FileAttributes \
net.schmizz.sshj.sftp.FileMode \
net.schmizz.sshj.sftp.OpenMode \
net.schmizz.sshj.sftp.PacketReader \
net.schmizz.sshj.sftp.PacketType \
net.schmizz.sshj.sftp.PathComponents \
net.schmizz.sshj.sftp.PathHelper \
net.schmizz.sshj.sftp.RandomAccessRemoteFile \
net.schmizz.sshj.sftp.RemoteDirectory \
net.schmizz.sshj.sftp.RemoteFile \
net.schmizz.sshj.sftp.RemoteResource \
net.schmizz.sshj.sftp.RemoteResourceFilter \
net.schmizz.sshj.sftp.RemoteResourceInfo \
net.schmizz.sshj.sftp.Request \
net.schmizz.sshj.sftp.Requester \
net.schmizz.sshj.sftp.Response \
net.schmizz.sshj.sftp.SFTPClient \
net.schmizz.sshj.sftp.SFTPEngine \
net.schmizz.sshj.sftp.SFTPException \
net.schmizz.sshj.sftp.SFTPFileTransfer \
net.schmizz.sshj.sftp.SFTPPacket \
net.schmizz.sshj.sftp.StatefulSFTPClient \
net.schmizz.sshj.signature.AbstractSignature \
net.schmizz.sshj.signature.Signature \
net.schmizz.sshj.signature.SignatureDSA \
net.schmizz.sshj.signature.SignatureECDSA \
net.schmizz.sshj.signature.SignatureRSA \
net.schmizz.sshj.SocketClient \
net.schmizz.sshj.SSHClient \
net.schmizz.sshj.transport.cipher.AES128CBC \
net.schmizz.sshj.transport.cipher.AES128CTR \
net.schmizz.sshj.transport.cipher.AES192CBC \
net.schmizz.sshj.transport.cipher.AES192CTR \
net.schmizz.sshj.transport.cipher.AES256CBC \
net.schmizz.sshj.transport.cipher.AES256CTR \
net.schmizz.sshj.transport.cipher.BaseCipher \
net.schmizz.sshj.transport.cipher.BlockCipher \
net.schmizz.sshj.transport.cipher.BlowfishCBC \
net.schmizz.sshj.transport.cipher.Cipher \
net.schmizz.sshj.transport.cipher.NoneCipher \
net.schmizz.sshj.transport.cipher.TripleDESCBC \
net.schmizz.sshj.transport.compression.Compression \
net.schmizz.sshj.transport.compression.DelayedZlibCompression \
net.schmizz.sshj.transport.compression.NoneCompression \
net.schmizz.sshj.transport.compression.ZlibCompression \
net.schmizz.sshj.transport.Converter \
net.schmizz.sshj.transport.Decoder \
net.schmizz.sshj.transport.digest.BaseDigest \
net.schmizz.sshj.transport.digest.Digest \
net.schmizz.sshj.transport.digest.MD5 \
net.schmizz.sshj.transport.digest.SHA1 \
net.schmizz.sshj.transport.digest.SHA256 \
net.schmizz.sshj.transport.digest.SHA384 \
net.schmizz.sshj.transport.digest.SHA512 \
net.schmizz.sshj.transport.DisconnectListener \
net.schmizz.sshj.transport.Encoder \
net.schmizz.sshj.transport.kex.AbstractDH \
net.schmizz.sshj.transport.kex.AbstractDHG \
net.schmizz.sshj.transport.kex.AbstractDHGex \
net.schmizz.sshj.transport.kex.Curve25519DH \
net.schmizz.sshj.transport.kex.Curve25519SHA256 \
net.schmizz.sshj.transport.kex.DH \
net.schmizz.sshj.transport.kex.DHBase \
net.schmizz.sshj.transport.kex.DHG1 \
net.schmizz.sshj.transport.kex.DHG14 \
net.schmizz.sshj.transport.kex.DHGexSHA1 \
net.schmizz.sshj.transport.kex.DHGexSHA256 \
net.schmizz.sshj.transport.kex.DHGroupData \
net.schmizz.sshj.transport.kex.ECDH \
net.schmizz.sshj.transport.kex.ECDHNistP \
net.schmizz.sshj.transport.kex.KeyExchange \
net.schmizz.sshj.transport.kex.KeyExchangeBase \
net.schmizz.sshj.transport.KeyExchanger \
net.schmizz.sshj.transport.mac.BaseMAC \
net.schmizz.sshj.transport.mac.HMACMD5 \
net.schmizz.sshj.transport.mac.HMACMD596 \
net.schmizz.sshj.transport.mac.HMACSHA1 \
net.schmizz.sshj.transport.mac.HMACSHA196 \
net.schmizz.sshj.transport.mac.HMACSHA2256 \
net.schmizz.sshj.transport.mac.HMACSHA2512 \
net.schmizz.sshj.transport.mac.MAC \
net.schmizz.sshj.transport.NegotiatedAlgorithms \
net.schmizz.sshj.transport.Proposal \
net.schmizz.sshj.transport.random.BouncyCastleRandom \
net.schmizz.sshj.transport.random.JCERandom \
net.schmizz.sshj.transport.random.Random \
net.schmizz.sshj.transport.random.SingletonRandomFactory \
net.schmizz.sshj.transport.Reader \
net.schmizz.sshj.transport.Transport \
net.schmizz.sshj.transport.TransportException \
net.schmizz.sshj.transport.TransportImpl \
net.schmizz.sshj.transport.verification.AlgorithmsVerifier \
net.schmizz.sshj.transport.verification.ConsoleKnownHostsVerifier \
net.schmizz.sshj.transport.verification.HostKeyVerifier \
net.schmizz.sshj.transport.verification.OpenSSHKnownHosts \
net.schmizz.sshj.transport.verification.PromiscuousVerifier \
net.schmizz.sshj.userauth.AuthParams \
net.schmizz.sshj.userauth.keyprovider.FileKeyProvider \
net.schmizz.sshj.userauth.keyprovider.KeyFormat \
net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper \
net.schmizz.sshj.userauth.keyprovider.KeyProvider \
net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil \
net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile \
net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile \
net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile \
net.schmizz.sshj.userauth.method.AbstractAuthMethod \
net.schmizz.sshj.userauth.method.AuthGssApiWithMic \
net.schmizz.sshj.userauth.method.AuthHostbased \
net.schmizz.sshj.userauth.method.AuthKeyboardInteractive \
net.schmizz.sshj.userauth.method.AuthMethod \
net.schmizz.sshj.userauth.method.AuthNone \
net.schmizz.sshj.userauth.method.AuthPassword \
net.schmizz.sshj.userauth.method.AuthPublickey \
net.schmizz.sshj.userauth.method.ChallengeResponseProvider \
net.schmizz.sshj.userauth.method.KeyedAuthMethod \
net.schmizz.sshj.userauth.method.PasswordResponseProvider \
net.schmizz.sshj.userauth.password.AccountResource \
net.schmizz.sshj.userauth.password.PasswordFinder \
net.schmizz.sshj.userauth.password.PasswordUpdateProvider \
net.schmizz.sshj.userauth.password.PasswordUtils \
net.schmizz.sshj.userauth.password.PrivateKeyFileResource \
net.schmizz.sshj.userauth.password.PrivateKeyReaderResource \
net.schmizz.sshj.userauth.password.PrivateKeyStringResource \
net.schmizz.sshj.userauth.password.Resource \
net.schmizz.sshj.userauth.UserAuth \
net.schmizz.sshj.userauth.UserAuthException \
net.schmizz.sshj.userauth.UserAuthImpl \
net.schmizz.sshj.xfer.AbstractFileTransfer \
net.schmizz.sshj.xfer.FilePermission \
net.schmizz.sshj.xfer.FileSystemFile \
net.schmizz.sshj.xfer.FileTransfer \
net.schmizz.sshj.xfer.InMemoryDestFile \
net.schmizz.sshj.xfer.InMemorySourceFile \
net.schmizz.sshj.xfer.LocalDestFile \
net.schmizz.sshj.xfer.LocalFileFilter \
net.schmizz.sshj.xfer.LocalSourceFile \
net.schmizz.sshj.xfer.LoggingTransferListener \
net.schmizz.sshj.xfer.scp.AbstractSCPClient \
net.schmizz.sshj.xfer.scp.ScpCommandLine \
net.schmizz.sshj.xfer.scp.SCPDownloadClient \
net.schmizz.sshj.xfer.scp.SCPEngine \
net.schmizz.sshj.xfer.scp.SCPException \
net.schmizz.sshj.xfer.scp.SCPFileTransfer \
net.schmizz.sshj.xfer.scp.SCPRemoteException \
net.schmizz.sshj.xfer.scp.SCPUploadClient \
net.schmizz.sshj.xfer.TransferListener

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,309 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed 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 net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.transport.cipher.AES128CBC;
import net.schmizz.sshj.transport.cipher.AES192CBC;
import net.schmizz.sshj.transport.cipher.AES256CBC;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.cipher.NoneCipher;
import net.schmizz.sshj.transport.cipher.TripleDESCBC;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.MD5;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.PrivateKeyFileResource;
import net.schmizz.sshj.userauth.password.PrivateKeyReaderResource;
import net.schmizz.sshj.userauth.password.PrivateKeyStringResource;
import net.schmizz.sshj.userauth.password.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
/** Represents a PKCS5-encoded key file. This is the format typically used by OpenSSH, OpenSSL, Amazon, etc. */
public class PKCS5KeyFile
implements FileKeyProvider {
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
@Override
public FileKeyProvider create() {
return new PKCS5KeyFile();
}
@Override
public String getName() {
return "PKCS5";
}
}
protected PasswordFinder pwdf;
protected Resource<?> resource;
protected KeyPair kp;
protected KeyType type;
protected char[] passphrase; // for blanking out
@Override
public PrivateKey getPrivate()
throws IOException {
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
}
@Override
public PublicKey getPublic()
throws IOException {
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
}
@Override
public KeyType getType()
throws IOException {
return type != null ? type : (type = KeyType.fromKey(getPublic()));
}
@Override
public void init(Reader location) {
assert location != null;
resource = new PrivateKeyReaderResource(location);
}
@Override
public void init(Reader location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(File location) {
assert location != null;
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
}
@Override
public void init(File location, PasswordFinder pwdf) {
init(location);
this.pwdf = pwdf;
}
@Override
public void init(String privateKey, String publicKey) {
assert privateKey != null;
assert publicKey == null;
resource = new PrivateKeyStringResource(privateKey);
}
@Override
public void init(String privateKey, String publicKey, PasswordFinder pwdf) {
init(privateKey, publicKey);
this.pwdf = pwdf;
}
protected KeyPair readKeyPair()
throws IOException {
BufferedReader reader = new BufferedReader(resource.getReader());
try {
String type = null;
String line = null;
Cipher cipher = new NoneCipher();
StringBuffer sb = new StringBuffer();
byte[] iv = new byte[0]; // salt
while ((line = reader.readLine()) != null) {
if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
int end = line.length() - 17;
if (end > 11) {
type = line.substring(11, line.length() - 17);
} else {
type = "UNKNOWN";
}
} else if (line.startsWith("-----END")) {
break;
} else if (type != null) {
if (line.startsWith("Proc-Type: ")) {
if (!"4,ENCRYPTED".equals(line.substring(11))) {
throw new IOException("Unrecognized Proc-Type: " + line.substring(11));
}
} else if (line.startsWith("DEK-Info: ")) {
int ptr = line.indexOf(",");
if (ptr == -1) {
throw new IOException("Unrecognized DEK-Info: " + line.substring(10));
} else {
String algorithm = line.substring(10,ptr);
if ("DES-EDE3-CBC".equals(algorithm)) {
cipher = new TripleDESCBC();
iv = DatatypeConverter.parseHexBinary(line.substring(ptr+1));
} else if ("AES-128-CBC".equals(algorithm)) {
cipher = new AES128CBC();
iv = DatatypeConverter.parseHexBinary(line.substring(ptr+1));
} else if ("AES-192-CBC".equals(algorithm)) {
cipher = new AES192CBC();
iv = Base64.decode(line.substring(ptr+1));
} else if ("AES-256-CBC".equals(algorithm)) {
cipher = new AES256CBC();
iv = Base64.decode(line.substring(ptr+1));
} else {
throw new IOException("Not a supported algorithm: " + algorithm);
}
}
} else if (line.length() > 0) {
sb.append(line);
}
}
}
byte[] data = Base64.decode(sb.toString());
if (pwdf != null) {
boolean decrypted = false;
do {
CharBuffer cb = CharBuffer.wrap(pwdf.reqPassword(resource));
ByteBuffer bb = IOUtils.UTF8.encode(cb);
byte[] passphrase = Arrays.copyOfRange(bb.array(), bb.position(), bb.limit());
Arrays.fill(cb.array(), '\u0000');
Arrays.fill(bb.array(), (byte)0);
byte[] key = new byte[cipher.getBlockSize()];
iv = Arrays.copyOfRange(iv, 0, cipher.getIVSize());
Digest md5 = new MD5();
md5.init();
int hsize = md5.getBlockSize();
byte[] hn = new byte[key.length / hsize * hsize + (key.length % hsize == 0 ? 0 : hsize)];
byte[] tmp = null;
for (int i=0; i + hsize <= hn.length;) {
if (tmp != null) {
md5.update(tmp, 0, tmp.length);
}
md5.update(passphrase, 0, passphrase.length);
md5.update(iv, 0, iv.length > 8 ? 8 : iv.length);
tmp = md5.digest();
System.arraycopy(tmp, 0, hn, i, tmp.length);
i += tmp.length;
}
Arrays.fill(passphrase, (byte)0);
System.arraycopy(hn, 0, key, 0, key.length);
cipher.init(Cipher.Mode.Decrypt, key, iv);
Arrays.fill(key, (byte)0);
cipher.update(data, 0, data.length);
decrypted = 0x30 == data[0];
} while (!decrypted && pwdf.shouldRetry(resource));
}
if (0x30 != data[0]) {
throw new IOException("Failed to decrypt key");
}
ASN1Data asn = new ASN1Data(data);
if ("RSA".equals(type)) {
KeyFactory factory = KeyFactory.getInstance("RSA");
asn.readNext();
BigInteger modulus = asn.readNext();
BigInteger pubExp = asn.readNext();
BigInteger prvExp = asn.readNext();
PublicKey pubKey = factory.generatePublic(new RSAPublicKeySpec(modulus, pubExp));
PrivateKey prvKey = factory.generatePrivate(new RSAPrivateKeySpec(modulus, prvExp));
return new KeyPair(pubKey, prvKey);
} else if ("DSA".equals(type)) {
KeyFactory factory = KeyFactory.getInstance("DSA");
asn.readNext();
BigInteger p = asn.readNext();
BigInteger q = asn.readNext();
BigInteger g = asn.readNext();
BigInteger pub = asn.readNext();
BigInteger prv = asn.readNext();
PublicKey pubKey = factory.generatePublic(new DSAPublicKeySpec(pub, p, q, g));
PrivateKey prvKey = factory.generatePrivate(new DSAPrivateKeySpec(prv, p, q, g));
return new KeyPair(pubKey, prvKey);
} else {
throw new IOException("Unrecognized key type: " + type);
}
} catch (NoSuchAlgorithmException e) {
throw new IOException(e);
} catch (InvalidKeySpecException e) {
throw new IOException(e);
} finally {
reader.close();
}
}
@Override
public String toString() {
return "PKCS5KeyFile{resource=" + resource + "}";
}
class ASN1Data {
private byte[] buff;
private int index, length;
ASN1Data(byte[] buff) throws IOException {
this.buff = buff;
index = 0;
if (buff[index++] != (byte)0x30) {
throw new IOException("Not ASN.1 data");
}
length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
if ((index + length) > buff.length) {
throw new IOException("Length mismatch: " + buff.length + " != " + (index + length));
}
}
BigInteger readNext() throws IOException {
if (index >= length) {
throw new EOFException();
} else if (buff[index++] != 0x02) {
throw new IOException("Not an int code: " + Integer.toHexString(0xff & buff[index]));
}
int length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
byte[] sequence = new byte[length];
System.arraycopy(buff, index, sequence, 0, length);
index += length;
return new BigInteger(sequence);
}
}
}

View File

@@ -15,47 +15,29 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.transport.cipher.AES128CBC;
import net.schmizz.sshj.transport.cipher.AES192CBC;
import net.schmizz.sshj.transport.cipher.AES256CBC;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.cipher.NoneCipher;
import net.schmizz.sshj.transport.cipher.TripleDESCBC;
import net.schmizz.sshj.transport.digest.Digest;
import net.schmizz.sshj.transport.digest.MD5;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.PrivateKeyFileResource;
import net.schmizz.sshj.userauth.password.PrivateKeyReaderResource;
import net.schmizz.sshj.userauth.password.PrivateKeyStringResource;
import net.schmizz.sshj.userauth.password.Resource;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import javax.xml.bind.DatatypeConverter;
/** Represents a PKCS8-encoded key file. This is the format used by OpenSSH and OpenSSL. */
public class PKCS8KeyFile
@@ -141,173 +123,51 @@ public class PKCS8KeyFile
protected KeyPair readKeyPair()
throws IOException {
BufferedReader reader = new BufferedReader(resource.getReader());
try {
String type = null;
String line = null;
Cipher cipher = new NoneCipher();
StringBuffer sb = new StringBuffer();
byte[] iv = new byte[0]; // salt
while ((line = reader.readLine()) != null) {
if (line.startsWith("-----BEGIN ") && line.endsWith(" PRIVATE KEY-----")) {
int end = line.length() - 17;
if (end > 11) {
type = line.substring(11, line.length() - 17);
} else {
type = "UNKNOWN";
}
} else if (line.startsWith("-----END")) {
break;
} else if (type != null) {
if (line.startsWith("Proc-Type: ")) {
if (!"4,ENCRYPTED".equals(line.substring(11))) {
throw new IOException("Unrecognized Proc-Type: " + line.substring(11));
}
} else if (line.startsWith("DEK-Info: ")) {
int ptr = line.indexOf(",");
if (ptr == -1) {
throw new IOException("Unrecognized DEK-Info: " + line.substring(10));
} else {
String algorithm = line.substring(10,ptr);
if ("DES-EDE3-CBC".equals(algorithm)) {
cipher = new TripleDESCBC();
iv = DatatypeConverter.parseHexBinary(line.substring(ptr+1));
} else if ("AES-128-CBC".equals(algorithm)) {
cipher = new AES128CBC();
iv = DatatypeConverter.parseHexBinary(line.substring(ptr+1));
} else if ("AES-192-CBC".equals(algorithm)) {
cipher = new AES192CBC();
iv = Base64.decode(line.substring(ptr+1));
} else if ("AES-256-CBC".equals(algorithm)) {
cipher = new AES256CBC();
iv = Base64.decode(line.substring(ptr+1));
} else {
throw new IOException("Not a supported algorithm: " + algorithm);
}
}
} else if (line.length() > 0) {
sb.append(line);
}
}
}
KeyPair kp = null;
byte[] data = Base64.decode(sb.toString());
if (pwdf != null) {
boolean decrypted = false;
do {
CharBuffer cb = CharBuffer.wrap(pwdf.reqPassword(resource));
ByteBuffer bb = IOUtils.UTF8.encode(cb);
byte[] passphrase = Arrays.copyOfRange(bb.array(), bb.position(), bb.limit());
Arrays.fill(cb.array(), '\u0000');
Arrays.fill(bb.array(), (byte)0);
byte[] key = new byte[cipher.getBlockSize()];
iv = Arrays.copyOfRange(iv, 0, cipher.getIVSize());
Digest md5 = new MD5();
md5.init();
int hsize = md5.getBlockSize();
byte[] hn = new byte[key.length / hsize * hsize + (key.length % hsize == 0 ? 0 : hsize)];
byte[] tmp = null;
for (int i=0; i + hsize <= hn.length;) {
if (tmp != null) {
md5.update(tmp, 0, tmp.length);
}
md5.update(passphrase, 0, passphrase.length);
md5.update(iv, 0, iv.length > 8 ? 8 : iv.length);
tmp = md5.digest();
System.arraycopy(tmp, 0, hn, i, tmp.length);
i += tmp.length;
for (PEMParser r = null; ; ) {
// while the PasswordFinder tells us we should retry
try {
r = new PEMParser(resource.getReader());
final Object o = r.readObject();
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
pemConverter.setProvider("BC");
if (o instanceof PEMEncryptedKeyPair) {
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
decryptorBuilder.setProvider("BC");
try {
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
} finally {
PasswordUtils.blankOut(passphrase);
}
Arrays.fill(passphrase, (byte)0);
System.arraycopy(hn, 0, key, 0, key.length);
cipher.init(Cipher.Mode.Decrypt, key, iv);
Arrays.fill(key, (byte)0);
cipher.update(data, 0, data.length);
decrypted = 0x30 == data[0];
} while (!decrypted && pwdf.shouldRetry(resource));
}
if (0x30 != data[0]) {
throw new IOException("Failed to decrypt key");
}
} else if (o instanceof PEMKeyPair) {
kp = pemConverter.getKeyPair((PEMKeyPair) o);
} else {
log.debug("Expected PEMEncryptedKeyPair or PEMKeyPair, got: {}", o);
}
ASN1Data asn = new ASN1Data(data);
if ("RSA".equals(type)) {
KeyFactory factory = KeyFactory.getInstance("RSA");
asn.readNext();
BigInteger modulus = asn.readNext();
BigInteger pubExp = asn.readNext();
BigInteger prvExp = asn.readNext();
PublicKey pubKey = factory.generatePublic(new RSAPublicKeySpec(modulus, pubExp));
PrivateKey prvKey = factory.generatePrivate(new RSAPrivateKeySpec(modulus, prvExp));
return new KeyPair(pubKey, prvKey);
} else if ("DSA".equals(type)) {
KeyFactory factory = KeyFactory.getInstance("DSA");
asn.readNext();
BigInteger p = asn.readNext();
BigInteger q = asn.readNext();
BigInteger g = asn.readNext();
BigInteger pub = asn.readNext();
BigInteger prv = asn.readNext();
PublicKey pubKey = factory.generatePublic(new DSAPublicKeySpec(pub, p, q, g));
PrivateKey prvKey = factory.generatePrivate(new DSAPrivateKeySpec(prv, p, q, g));
return new KeyPair(pubKey, prvKey);
} else {
throw new IOException("Unrecognized key type: " + type);
}
} catch (NoSuchAlgorithmException e) {
throw new IOException(e);
} catch (InvalidKeySpecException e) {
throw new IOException(e);
} finally {
reader.close();
}
} catch (EncryptionException e) {
if (pwdf != null && pwdf.shouldRetry(resource))
continue;
else
throw e;
} finally {
IOUtils.closeQuietly(r);
}
break;
}
if (kp == null)
throw new IOException("Could not read key pair from: " + resource);
return kp;
}
@Override
public String toString() {
return "PKCS8KeyFile{resource=" + resource + "}";
}
class ASN1Data {
private byte[] buff;
private int index, length;
ASN1Data(byte[] buff) throws IOException {
this.buff = buff;
index = 0;
if (buff[index++] != (byte)0x30) {
throw new IOException("Not ASN.1 data");
}
length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
if ((index + length) > buff.length) {
throw new IOException("Length mismatch: " + buff.length + " != " + (index + length));
}
}
BigInteger readNext() throws IOException {
if (index >= length) {
throw new EOFException();
} else if (buff[index++] != 0x02) {
throw new IOException("Not an int code: " + Integer.toHexString(0xff & buff[index]));
}
int length = buff[index++] & 0xff;
if ((length & 0x80) != 0) {
int counter = length & 0x7f;
length = 0;
while (counter-- > 0) {
length = (length << 8) + (buff[index++] & 0xff);
}
}
byte[] sequence = new byte[length];
System.arraycopy(buff, index, sequence, 0, length);
index += length;
return new BigInteger(sequence);
}
}
}
}