Skip to content

Commit

Permalink
Added DefaultSecurityProviderConfig with Bouncy Castle disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
exceptionfactory committed Jul 14, 2023
1 parent 0783709 commit 20fd4c4
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 83 deletions.
9 changes: 0 additions & 9 deletions src/main/java/net/schmizz/sshj/AndroidConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
import com.hierynomus.sshj.key.KeyAlgorithms;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;

import java.util.Arrays;

Expand All @@ -34,7 +32,6 @@ public class AndroidConfig
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
}


@Override
protected void initKeyAlgorithms() {
setKeyAlgorithms(Arrays.<Factory.Named<KeyAlgorithm>>asList(
Expand All @@ -43,10 +40,4 @@ protected void initKeyAlgorithms() {
KeyAlgorithms.SSHDSA()
));
}

@Override
protected void initRandomFactory(boolean ignored) {
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
}

}
114 changes: 52 additions & 62 deletions src/main/java/net/schmizz/sshj/DefaultConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
Expand All @@ -43,7 +42,11 @@
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;

import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Properties;

/**
* A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
Expand All @@ -62,8 +65,6 @@
* <li>{@link net.schmizz.sshj.ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li>
* </ul>
* <p/>
* [1] It is worth noting that Sun's JRE does not have the unlimited cryptography extension enabled by default. This
* prevents using ciphers with strength greater than 128.
*/
public class DefaultConfig
extends ConfigImpl {
Expand All @@ -73,11 +74,10 @@ public class DefaultConfig
public DefaultConfig() {
setLoggerFactory(LoggerFactory.DEFAULT);
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initKeyExchangeFactories();
initKeyAlgorithms();
initRandomFactory(bouncyCastleRegistered);
initFileKeyProviderFactories(bouncyCastleRegistered);
initRandomFactory();
initFileKeyProviderFactories();
initCipherFactories();
initCompressionFactories();
initMACFactories();
Expand All @@ -102,35 +102,32 @@ public void setLoggerFactory(LoggerFactory loggerFactory) {
log = loggerFactory.getLogger(getClass());
}

protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setKeyExchangeFactories(
new Curve25519SHA256.Factory(),
new Curve25519SHA256.FactoryLibSsh(),
new DHGexSHA256.Factory(),
new ECDHNistP.Factory521(),
new ECDHNistP.Factory384(),
new ECDHNistP.Factory256(),
new DHGexSHA1.Factory(),
DHGroups.Group1SHA1(),
DHGroups.Group14SHA1(),
DHGroups.Group14SHA256(),
DHGroups.Group15SHA512(),
DHGroups.Group16SHA512(),
DHGroups.Group17SHA512(),
DHGroups.Group18SHA512(),
ExtendedDHGroups.Group14SHA256AtSSH(),
ExtendedDHGroups.Group15SHA256(),
ExtendedDHGroups.Group15SHA256AtSSH(),
ExtendedDHGroups.Group15SHA384AtSSH(),
ExtendedDHGroups.Group16SHA256(),
ExtendedDHGroups.Group16SHA384AtSSH(),
ExtendedDHGroups.Group16SHA512AtSSH(),
ExtendedDHGroups.Group18SHA512AtSSH(),
new ExtInfoClientFactory());
} else {
setKeyExchangeFactories(DHGroups.Group1SHA1(), new DHGexSHA1.Factory());
}
protected void initKeyExchangeFactories() {
setKeyExchangeFactories(
new Curve25519SHA256.Factory(),
new Curve25519SHA256.FactoryLibSsh(),
new DHGexSHA256.Factory(),
new ECDHNistP.Factory521(),
new ECDHNistP.Factory384(),
new ECDHNistP.Factory256(),
new DHGexSHA1.Factory(),
DHGroups.Group1SHA1(),
DHGroups.Group14SHA1(),
DHGroups.Group14SHA256(),
DHGroups.Group15SHA512(),
DHGroups.Group16SHA512(),
DHGroups.Group17SHA512(),
DHGroups.Group18SHA512(),
ExtendedDHGroups.Group14SHA256AtSSH(),
ExtendedDHGroups.Group15SHA256(),
ExtendedDHGroups.Group15SHA256AtSSH(),
ExtendedDHGroups.Group15SHA384AtSSH(),
ExtendedDHGroups.Group16SHA256(),
ExtendedDHGroups.Group16SHA384AtSSH(),
ExtendedDHGroups.Group16SHA512AtSSH(),
ExtendedDHGroups.Group18SHA512AtSSH(),
new ExtInfoClientFactory()
);
}

protected void initKeyAlgorithms() {
Expand All @@ -151,21 +148,19 @@ protected void initKeyAlgorithms() {
KeyAlgorithms.SSHDSA()));
}

protected void initRandomFactory(boolean bouncyCastleRegistered) {
protected void initRandomFactory() {
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
}

protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setFileKeyProviderFactories(
new OpenSSHKeyV1KeyFile.Factory(),
new PKCS8KeyFile.Factory(),
new OpenSSHKeyFile.Factory(),
new PuTTYKeyFile.Factory());
}
protected void initFileKeyProviderFactories() {
setFileKeyProviderFactories(
new OpenSSHKeyV1KeyFile.Factory(),
new PKCS8KeyFile.Factory(),
new OpenSSHKeyFile.Factory(),
new PuTTYKeyFile.Factory()
);
}


protected void initCipherFactories() {
List<Factory.Named<Cipher>> avail = new LinkedList<Factory.Named<Cipher>>(Arrays.<Factory.Named<Cipher>>asList(
ChachaPolyCiphers.CHACHA_POLY_OPENSSH(),
Expand Down Expand Up @@ -203,27 +198,22 @@ protected void initCipherFactories() {
StreamCiphers.Arcfour256())
);

boolean warn = false;
// Ref. https://issues.apache.org/jira/browse/SSHD-24
// "AES256 and AES192 requires unlimited cryptography extension"
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext(); ) {
final Factory.Named<Cipher> f = i.next();
final ListIterator<Factory.Named<Cipher>> factories = avail.listIterator();
while (factories.hasNext()) {
final Factory.Named<Cipher> factory = factories.next();
try {
final Cipher c = f.create();
final byte[] key = new byte[c.getBlockSize()];
final byte[] iv = new byte[c.getIVSize()];
c.init(Cipher.Mode.Encrypt, key, iv);
final Cipher cipher = factory.create();
final byte[] key = new byte[cipher.getBlockSize()];
final byte[] iv = new byte[cipher.getIVSize()];
cipher.init(Cipher.Mode.Encrypt, key, iv);
} catch (Exception e) {
warn = true;
log.warn(e.getCause().getMessage());
i.remove();
log.info("Cipher [{}] disabled: {}", factory.getName(), e.getCause().getMessage());
factories.remove();
}
}
if (warn)
log.warn("Disabling high-strength ciphers: cipher strengths apparently limited by JCE policy");

setCipherFactories(avail);
log.debug("Available cipher factories: {}", avail);
log.debug("Available Ciphers {}", avail);
}

protected void initMACFactories() {
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/net/schmizz/sshj/DefaultSecurityProviderConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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;

import net.schmizz.sshj.common.SecurityUtils;

/**
* SSHJ Configuration that uses the default Security Provider configuration from java.security and disables Bouncy Castle registration
*/
public class DefaultSecurityProviderConfig extends DefaultConfig {
static {
// Disable Bouncy Castle Provider registration prior to invoking constructors
SecurityUtils.setRegisterBouncyCastle(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ class ECDSAVariationsAdapter {

static PublicKey readPubKeyFromBuffer(Buffer<?> buf, String variation) throws GeneralSecurityException {
String algorithm = BASE_ALGORITHM_NAME + variation;
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new GeneralSecurityException("BouncyCastle is required to read a key of type " + algorithm);
}
try {
// final String algo = buf.readString(); it has been already read
final String curveName = buf.readString();
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/net/schmizz/sshj/common/SecurityUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ public static synchronized boolean isBouncyCastleRegistered() {
return false;
}

/**
* Configure whether to register the Bouncy Castle Security Provider. Must be called prior to other methods
*
* @param registerBouncyCastle Enable or disable Bouncy Castle Provider registration on subsequent method invocation
*/
public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
SecurityUtils.registerBouncyCastle = registerBouncyCastle;
registrationDone = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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;

import net.schmizz.sshj.common.SecurityUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
import java.util.Optional;

import static org.junit.Assert.assertFalse;

public class DefaultSecurityProviderConfigTest {

private static Provider bouncyCastleProvider;

@BeforeClass
public static void removeProviders() {
bouncyCastleProvider = Security.getProvider(SecurityUtils.BOUNCY_CASTLE);
if (bouncyCastleProvider != null) {
Security.removeProvider(SecurityUtils.BOUNCY_CASTLE);
}
}

@AfterClass
public static void addProviders() {
if (bouncyCastleProvider != null) {
Security.addProvider(bouncyCastleProvider);
}
}

@Test
public void testBouncyCastleNotRegistered() {
new DefaultSecurityProviderConfig();

assertBouncyCastleNotRegistered();
}

private void assertBouncyCastleNotRegistered() {
final Optional<String> bouncyCastleFound = Arrays.stream(Security.getProviders())
.map(Provider::getName)
.filter(SecurityUtils.BOUNCY_CASTLE::contentEquals)
.findFirst();

assertFalse(bouncyCastleFound.isPresent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@
import com.hierynomus.sshj.userauth.certificate.Certificate;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
import net.schmizz.sshj.userauth.password.PasswordFinder;
import net.schmizz.sshj.userauth.password.PasswordUtils;
import net.schmizz.sshj.userauth.password.Resource;
import net.schmizz.sshj.util.KeyUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
Expand Down Expand Up @@ -452,13 +450,6 @@ public void emptyPrivateKey() {
assertThrows("This key is not in 'openssh-key-v1' format", IOException.class, keyProvider::getPrivate);
}

@Before
public void checkBCRegistration() {
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new AssertionError("bouncy castle needed");
}
}

private String readFile(String pathname)
throws IOException {
StringBuilder fileContents = new StringBuilder();
Expand Down

0 comments on commit 20fd4c4

Please sign in to comment.