diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java new file mode 100644 index 000000000..3521279df --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/SecurityUtil.java @@ -0,0 +1,53 @@ +/** + * + * Copyright 2019 Florian Schmaus. + * + * 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 org.jivesoftware.smack.util; + +import java.lang.reflect.InvocationTargetException; +import java.security.Provider; +import java.security.Security; + +import org.jxmpp.util.cache.LruCache; + +public class SecurityUtil { + + private static final LruCache, Void> INSERTED_PROVIDERS_CACHE = new LruCache<>(8); + + public static void ensureProviderAtFirstPosition(Class providerClass) { + if (INSERTED_PROVIDERS_CACHE.containsKey(providerClass)) { + return; + } + + Provider provider; + try { + provider = providerClass.getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { + throw new IllegalArgumentException(e); + } + + String providerName = provider.getName(); + + int installedPosition ; + synchronized (Security.class) { + Security.removeProvider(providerName); + installedPosition = Security.insertProviderAt(provider, 1); + } + assert installedPosition == 1; + + INSERTED_PROVIDERS_CACHE.put(providerClass, null); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java index a872b376c..888c2c1ac 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java @@ -35,7 +35,6 @@ import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_512; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.Security; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -43,7 +42,7 @@ import java.util.WeakHashMap; import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.XMPPConnection; - +import org.jivesoftware.smack.util.SecurityUtil; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.hashes.element.HashElement; @@ -58,7 +57,9 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class HashManager extends Manager { static { - Security.addProvider(new BouncyCastleProvider()); + // Remove any BC providers and add a fresh one. + // This is done, since older Android versions ship with a crippled BC provider. + SecurityUtil.ensureProviderAtFirstPosition(BouncyCastleProvider.class); } public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:"; diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoInitializer.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoInitializer.java index 5f293490f..243257b99 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoInitializer.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoInitializer.java @@ -17,6 +17,9 @@ package org.jivesoftware.smackx.omemo; import org.jivesoftware.smack.initializer.UrlInitializer; +import org.jivesoftware.smack.util.SecurityUtil; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * Initializer class that registers omemo providers. @@ -26,6 +29,12 @@ import org.jivesoftware.smack.initializer.UrlInitializer; @SuppressWarnings("unused") public class OmemoInitializer extends UrlInitializer { + static { + // Remove any BC providers and add a fresh one. + // This is done, since older Android versions ship with a crippled BC provider. + SecurityUtil.ensureProviderAtFirstPosition(BouncyCastleProvider.class); + } + @Override protected String getProvidersUri() { return "classpath:org.jivesoftware.smackx.omemo/omemo.providers"; diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index 8cf2aac6f..91eb51034 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -22,7 +22,6 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.Security; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -45,7 +44,6 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StanzaError; - import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.mam.MamManager; import org.jivesoftware.smackx.muc.MultiUserChat; @@ -80,7 +78,6 @@ import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubManager; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.jxmpp.jid.BareJid; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.Jid; @@ -102,10 +99,6 @@ import org.jxmpp.jid.Jid; public abstract class OmemoService implements OmemoCarbonCopyStanzaReceivedListener, OmemoMessageStanzaReceivedListener { - static { - Security.addProvider(new BouncyCastleProvider()); - } - protected static final Logger LOGGER = Logger.getLogger(OmemoService.class.getName()); private static final long MILLIS_PER_HOUR = 1000L * 60 * 60; diff --git a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java index 43eb4353c..66c538bf8 100644 --- a/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java +++ b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java @@ -25,21 +25,20 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import java.security.NoSuchAlgorithmException; -import java.security.Security; +import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smackx.omemo.element.OmemoElement; import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag; import org.jivesoftware.smackx.omemo.internal.CiphertextTuple; import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Test; /** * Test the identityKeyWrapper. */ -public class WrapperObjectsTest { +public class WrapperObjectsTest extends SmackTestSuite { @Test public void ciphertextTupleTest() { @@ -56,7 +55,6 @@ public class WrapperObjectsTest { @Test public void cipherAndAuthTagTest() throws NoSuchAlgorithmException, CryptoFailedException { - Security.addProvider(new BouncyCastleProvider()); byte[] key = OmemoMessageBuilder.generateKey(KEYTYPE, KEYLENGTH); byte[] iv = OmemoMessageBuilder.generateIv(); byte[] authTag = OmemoMessageBuilder.generateIv(); diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index cc15f9beb..35ef3eae9 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -12,7 +12,7 @@ dependencies { compile project(':smack-extensions') compile project(':smack-experimental') - compile 'org.pgpainless:pgpainless-core:0.0.1-alpha4' + compile 'org.pgpainless:pgpainless-core:0.0.1-alpha7' testCompile project(path: ":smack-core", configuration: "testRuntime") testCompile project(path: ":smack-core", configuration: "archives") diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java index 08011b2f5..ec72bd8b5 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/crypto/PainlessOpenPgpProvider.java @@ -19,7 +19,6 @@ package org.jivesoftware.smackx.ox.crypto; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.security.Security; import java.util.ArrayList; import java.util.Collection; import java.util.logging.Level; @@ -38,7 +37,6 @@ import org.jivesoftware.smackx.ox.element.SignElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -53,13 +51,6 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider { private static final Logger LOGGER = Logger.getLogger(PainlessOpenPgpProvider.class.getName()); - static { - // Remove any BC providers and add a fresh one. - // This is done, since older Android versions ship with a crippled BC provider. - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - Security.addProvider(new BouncyCastleProvider()); - } - private final XMPPConnection connection; private final OpenPgpStore store; diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java index df90e6cdf..ce723a3a4 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpInitializer.java @@ -17,12 +17,21 @@ package org.jivesoftware.smackx.ox.util; import org.jivesoftware.smack.initializer.UrlInitializer; +import org.jivesoftware.smack.util.SecurityUtil; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * Initializer class which registers ExtensionElementProviders on startup. */ public class OpenPgpInitializer extends UrlInitializer { + static { + // Remove any BC providers and add a fresh one. + // This is done, since older Android versions ship with a crippled BC provider. + SecurityUtil.ensureProviderAtFirstPosition(BouncyCastleProvider.class); + } + @Override protected String getProvidersUri() { return "classpath:org.jivesoftware.smackx.ox/openpgp.providers"; diff --git a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java index 6be88ac0f..ca11a53b4 100644 --- a/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java +++ b/smack-openpgp/src/test/java/org/jivesoftware/smackx/ox/OpenPgpStoreTest.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.Security; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -45,7 +44,6 @@ import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jivesoftware.smackx.ox.store.filebased.FileBasedOpenPgpStore; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; @@ -81,7 +79,6 @@ public class OpenPgpStoreTest extends SmackTestSuite { static { storagePath = FileTestUtil.getTempDir("storeTest"); - Security.addProvider(new BouncyCastleProvider()); } @Parameterized.Parameters