Android Compability, again ;-) (#392)

* Rework SecurityUtils and PKCS8KeyFile for usage with SpongyCastle.

* Specifying providerName for registration is unneccessary.

* Update AndroidConfig, fix imports.

* Workaround for Android 5.0 bug when SpongyCastle is the default JCE provider.

On Android 5.0 reading the version from the jar does throw a SecurityException due to a bug in Android (see https://issuetracker.google.com/issues/36993752). Including that Exception in the catch provides a workaround for that issue.
This commit is contained in:
Michael Prankl
2018-01-30 16:22:05 +01:00
committed by Jeroen van Erp
parent d55eb6d02e
commit 39b72eed62
4 changed files with 28 additions and 14 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.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory; import net.schmizz.sshj.transport.random.SingletonRandomFactory;
/**
* Registers SpongyCastle as JCE provider.
*/
public class AndroidConfig public class AndroidConfig
extends DefaultConfig { extends DefaultConfig {
@@ -30,13 +33,6 @@ public class AndroidConfig
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider"); SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
} }
public AndroidConfig(){
super();
initKeyExchangeFactories(true);
initRandomFactory(true);
initFileKeyProviderFactories(true);
}
// don't add ECDSA // don't add ECDSA
protected void initSignatureFactories() { protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(), setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),

View File

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

View File

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

View File

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