Merge branch 'master' into issue-358

This commit is contained in:
Jeroen van Erp
2018-03-22 22:50:37 +01:00
17 changed files with 2206 additions and 1564 deletions

View File

@@ -23,6 +23,9 @@ import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
/**
* Registers SpongyCastle as JCE provider.
*/
public class AndroidConfig
extends DefaultConfig {
@@ -30,13 +33,6 @@ public class AndroidConfig
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
}
public AndroidConfig(){
super();
initKeyExchangeFactories(true);
initRandomFactory(true);
initFileKeyProviderFactories(true);
}
// don't add ECDSA
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),

View File

@@ -92,7 +92,7 @@ public class DefaultConfig
properties.load(DefaultConfig.class.getClassLoader().getResourceAsStream("sshj.properties"));
String property = properties.getProperty("sshj.version");
return "SSHJ_" + property.replace('-', '_'); // '-' is a disallowed character, see RFC-4253#section-4.2
} catch (IOException e) {
} catch (Exception e) {
log.error("Could not read the sshj.properties file, returning an 'unknown' version as fallback.");
return "SSHJ_VERSION_UNKNOWN";
}

File diff suppressed because it is too large Load Diff

View File

@@ -460,10 +460,13 @@ public class Buffer<T extends Buffer<T>> {
public PublicKey readPublicKey()
throws BufferException {
KeyType keyType = KeyType.fromString(readString());
try {
return KeyType.fromString(readString()).readPubKeyFromBuffer(this);
return keyType.readPubKeyFromBuffer(this);
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
} catch (UnsupportedOperationException uoe) {
throw new BufferException("Could not decode keytype " + keyType);
}
}

View File

@@ -18,11 +18,21 @@ package net.schmizz.sshj.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import static java.lang.String.format;
@@ -37,12 +47,17 @@ public class SecurityUtils {
*/
public static final String BOUNCY_CASTLE = "BC";
/**
* Identifier for the BouncyCastle JCE provider
*/
public static final String SPONGY_CASTLE = "SC";
/*
* Security provider identifier. null = default JCE
*/
private static String securityProvider = null;
// relate to BC registration
// relate to BC registration (or SpongyCastle on Android)
private static Boolean registerBouncyCastle;
private static boolean registrationDone;
@@ -82,6 +97,8 @@ public class SecurityUtils {
return false;
}
public static synchronized Cipher getCipher(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
register();
@@ -222,11 +239,11 @@ public class SecurityUtils {
* Attempts registering BouncyCastle as security provider if it has not been previously attempted and returns
* whether the registration succeeded.
*
* @return whether BC registered
* @return whether BC (or SC on Android) registered
*/
public static synchronized boolean isBouncyCastleRegistered() {
register();
return BOUNCY_CASTLE.equals(securityProvider);
return BOUNCY_CASTLE.equals(securityProvider) || SPONGY_CASTLE.equals(securityProvider);
}
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {

View File

@@ -0,0 +1,38 @@
/*
* 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.transport.mac;
public class HMACRIPEMD160 extends BaseMAC {
/** Named factory for the HMAC-SHA1 <code>MAC</code> */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<MAC> {
@Override
public MAC create() {
return new HMACRIPEMD160();
}
@Override
public String getName() {
return "hmac-ripemd160";
}
}
public HMACRIPEMD160() {
super("HMACRIPEMD160", 20, 20);
}
}

View File

@@ -41,7 +41,7 @@ public class ConsoleKnownHostsVerifier
protected boolean hostKeyUnverifiableAction(String hostname, PublicKey key) {
final KeyType type = KeyType.fromKey(key);
console.printf("The authenticity of host '%s' can't be established.\n" +
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
"%s key fingerprint is %s.\n", hostname, type, SecurityUtils.getFingerprint(key));
String response = console.readLine("Are you sure you want to continue connecting (yes/no)? ");
while (!(response.equalsIgnoreCase(YES) || response.equalsIgnoreCase(NO))) {
response = console.readLine("Please explicitly enter yes/no: ");
@@ -60,7 +60,7 @@ public class ConsoleKnownHostsVerifier
}
@Override
protected boolean hostKeyChangedAction(KnownHostEntry entry, String hostname, PublicKey key) {
protected boolean hostKeyChangedAction(String hostname, PublicKey key) {
final KeyType type = KeyType.fromKey(key);
final String fp = SecurityUtils.getFingerprint(key);
final String path = getFile().getAbsolutePath();

View File

@@ -87,14 +87,23 @@ public class OpenSSHKnownHosts
final String adjustedHostname = (port != 22) ? "[" + hostname + "]:" + port : hostname;
boolean foundApplicableHostEntry = false;
for (KnownHostEntry e : entries) {
try {
if (e.appliesTo(type, adjustedHostname))
return e.verify(key) || hostKeyChangedAction(e, adjustedHostname, key);
if (e.appliesTo(type, adjustedHostname)) {
foundApplicableHostEntry = true;
if (e.verify(key)) {
return true;
}
}
} catch (IOException ioe) {
log.error("Error with {}: {}", e, ioe);
return false;
}
}
if (foundApplicableHostEntry) {
return hostKeyChangedAction(adjustedHostname, key);
}
return hostKeyUnverifiableAction(adjustedHostname, key);
@@ -104,7 +113,7 @@ public class OpenSSHKnownHosts
return false;
}
protected boolean hostKeyChangedAction(KnownHostEntry entry, String hostname, PublicKey key) {
protected boolean hostKeyChangedAction(String hostname, PublicKey key) {
log.warn("Host key for `{}` has changed!", hostname);
return false;
}
@@ -199,7 +208,7 @@ public class OpenSSHKnownHosts
}
if(split.length < 3) {
log.error("Error reading entry `{}`", line);
return null;
return new BadHostEntry(line);
}
final String hostnames = split[i++];
final String sType = split[i++];
@@ -209,7 +218,13 @@ public class OpenSSHKnownHosts
if (type != KeyType.UNKNOWN) {
final String sKey = split[i++];
key = new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
try {
byte[] keyBytes = Base64.decode(sKey);
key = new Buffer.PlainBuffer(keyBytes).readPublicKey();
} catch (IOException ioe) {
log.warn("Error decoding Base64 key bytes", ioe);
return new BadHostEntry(line);
}
} else if (isBits(sType)) {
type = KeyType.RSA;
// int bits = Integer.valueOf(sType);
@@ -220,11 +235,11 @@ public class OpenSSHKnownHosts
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
} catch (Exception ex) {
log.error("Error reading entry `{}`, could not create key", line, ex);
return null;
return new BadHostEntry(line);
}
} else {
log.error("Error reading entry `{}`, could not determine type", line);
return null;
return new BadHostEntry(line);
}
return new HostEntry(marker, hostnames, type, key);
@@ -310,7 +325,7 @@ public class OpenSSHKnownHosts
protected final PublicKey key;
private final KnownHostMatchers.HostMatcher matcher;
HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
public HostEntry(Marker marker, String hostPart, KeyType type, PublicKey key) throws SSHException {
this.marker = marker;
this.hostPart = hostPart;
this.type = type;
@@ -364,6 +379,44 @@ public class OpenSSHKnownHosts
}
}
public static class BadHostEntry implements KnownHostEntry {
private String line;
public BadHostEntry(String line) {
this.line = line;
}
@Override
public KeyType getType() {
return KeyType.UNKNOWN;
}
@Override
public String getFingerprint() {
return null;
}
@Override
public boolean appliesTo(String host) throws IOException {
return false;
}
@Override
public boolean appliesTo(KeyType type, String host) throws IOException {
return false;
}
@Override
public boolean verify(PublicKey key) throws IOException {
return false;
}
@Override
public String getLine() {
return line;
}
}
public enum Marker {
CA_CERT("@cert-authority"),
REVOKED("@revoked");

View File

@@ -16,6 +16,7 @@
package net.schmizz.sshj.userauth.keyprovider;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
@@ -62,12 +63,12 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
final Object o = r.readObject();
final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
pemConverter.setProvider("BC");
pemConverter.setProvider(SecurityUtils.getSecurityProvider());
if (o instanceof PEMEncryptedKeyPair) {
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
decryptorBuilder.setProvider("BC");
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
try {
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));