diff --git a/smack-android-extensions/build.gradle b/smack-android-extensions/build.gradle index 3279bdeb1..2a57ed2d7 100644 --- a/smack-android-extensions/build.gradle +++ b/smack-android-extensions/build.gradle @@ -6,4 +6,7 @@ Extra Smack extensions for Android.""" dependencies { compile project(':smack-android') compile project(':smack-extensions') + + // Add the Android jar to the Eclipse .classpath. + compileClasspath files(androidBootClasspath) } diff --git a/smack-android/build.gradle b/smack-android/build.gradle index 46d83633d..34b3a784a 100644 --- a/smack-android/build.gradle +++ b/smack-android/build.gradle @@ -24,5 +24,5 @@ dependencies { } // Add the Android jar to the Eclipse .classpath. - compileOnly files(androidBootClasspath) + compileClasspath files(androidBootClasspath) } diff --git a/smack-core/build.gradle b/smack-core/build.gradle index 40e9026d6..8d29cfd89 100644 --- a/smack-core/build.gradle +++ b/smack-core/build.gradle @@ -24,6 +24,7 @@ dependencies { testCompile "org.assertj:assertj-core:3.11.1" testCompile "org.xmlunit:xmlunit-assertj:$xmlUnitVersion" testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2' + testCompile 'org.bouncycastle:bcprov-jdk15on:1.64' } class CreateFileTask extends DefaultTask { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 4d344376d..0e13c28c7 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -135,6 +135,7 @@ import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.Predicate; import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.TLSUtils; import org.jivesoftware.smack.util.dns.HostAddress; import org.jivesoftware.smack.util.dns.SmackDaneProvider; import org.jivesoftware.smack.util.dns.SmackDaneVerifier; @@ -2303,7 +2304,28 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { ks = null; } } else { - ks.load(null, null); + InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous + // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 + char[] password = "changeit".toCharArray(); + try { + ks.load(stream, password); + } finally { + stream.close(); + } + } catch (IOException e) { + LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); + + ks = KeyStore.getInstance("jks"); + // Open the stream again, so that we read it from the beginning. + stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + ks.load(stream, null); + } finally { + stream.close(); + } + } } } @@ -2340,16 +2362,16 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { context = SSLContext.getInstance("TLS"); final SecureRandom secureRandom = new java.security.SecureRandom(); - X509TrustManager customTrustManager = config.getCustomX509TrustManager(); + X509TrustManager trustManager = config.getCustomX509TrustManager(); + if (trustManager == null) { + trustManager = TLSUtils.getDefaultX509TrustManager(ks); + } if (daneVerifier != null) { // User requested DANE verification. - daneVerifier.init(context, kms, customTrustManager, secureRandom); + daneVerifier.init(context, kms, trustManager, secureRandom); } else { - TrustManager[] customTrustManagers = null; - if (customTrustManager != null) { - customTrustManagers = new TrustManager[] { customTrustManager }; - } + TrustManager[] customTrustManagers = new TrustManager[] { trustManager }; context.init(kms, customTrustManagers, secureRandom); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java index 3539d22bd..cff077e29 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StanzaBuilder.java @@ -162,6 +162,14 @@ public abstract class StanzaBuilder> implements Stanz return getThis(); } + public final B addOptExtensions(Collection extensionElements) { + if (extensionElements == null) { + return getThis(); + } + + return addExtensions(extensionElements); + } + public final B addExtensions(Collection extensionElements) { for (ExtensionElement extensionElement : extensionElements) { addExtension(extensionElement); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java index 5c3527482..cba78280f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2014-2016 Florian Schmaus + * Copyright 2014-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,15 @@ */ package org.jivesoftware.smack.util; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -27,6 +35,8 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; @@ -34,6 +44,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import org.jivesoftware.smack.ConnectionConfiguration; @@ -42,6 +53,8 @@ import org.jivesoftware.smack.SmackException.SecurityNotPossibleException; public class TLSUtils { + private static final Logger LOGGER = Logger.getLogger(TLSUtils.class.getName()); + public static final String SSL = "SSL"; public static final String TLS = "TLS"; public static final String PROTO_SSL3 = SSL + "v3"; @@ -240,4 +253,86 @@ public class TLSUtils { return new X509Certificate[0]; } } + + public static X509TrustManager getDefaultX509TrustManager(KeyStore keyStore) { + String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory trustManagerFactory; + try { + trustManagerFactory = TrustManagerFactory.getInstance(defaultAlgorithm); + trustManagerFactory.init(keyStore); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + throw new AssertionError(e); + } + + for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { + if (trustManager instanceof X509TrustManager) { + return (X509TrustManager) trustManager; + } + } + throw new AssertionError("No trust manager for the default algorithm " + defaultAlgorithm + " found"); + } + + private static final File DEFAULT_TRUSTSTORE_PATH; + + static { + String javaHome = System.getProperty("java.home"); + String defaultTruststorePath = javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts"; + DEFAULT_TRUSTSTORE_PATH = new File(defaultTruststorePath); + } + + public static FileInputStream getDefaultTruststoreStreamIfPossible() { + try { + return new FileInputStream(DEFAULT_TRUSTSTORE_PATH); + } catch (FileNotFoundException e) { + LOGGER.log(Level.WARNING, "Could not open default truststore at " + DEFAULT_TRUSTSTORE_PATH, e); + return null; + } + } + + enum DefaultTrustStoreType { + jks, + unknown, + no_default, + } + + private static final int JKS_MAGIC = 0xfeedfeed; + private static final int JKS_VERSION_1 = 1; + private static final int JKS_VERSION_2 = 2; + + public static DefaultTrustStoreType getDefaultTruststoreType() throws IOException { + try (InputStream inputStream = getDefaultTruststoreStreamIfPossible()) { + if (inputStream == null) { + return DefaultTrustStoreType.no_default; + } + + DataInputStream dis = new DataInputStream(inputStream); + int magic = dis.readInt(); + int version = dis.readInt(); + + if (magic == JKS_MAGIC && (version == JKS_VERSION_1 || version == JKS_VERSION_2)) { + return DefaultTrustStoreType.jks; + } + } + + return DefaultTrustStoreType.unknown; + } + + /** + * Tries to determine if the default truststore type is of type jks and sets the javax.net.ssl.trustStoreType system + * property to 'JKS' if so. This is meant as workaround in situations where the default truststore type is (still) + * 'jks' but we run on a newer JRE/JDK which uses PKCS#12 as type. See for example Gentoo bug #712290. + */ + public static void setDefaultTrustStoreTypeToJksIfRequired() { + DefaultTrustStoreType defaultTrustStoreType; + try { + defaultTrustStoreType = getDefaultTruststoreType(); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Could not set keystore type to jks if required", e); + return; + } + + if (defaultTrustStoreType == DefaultTrustStoreType.jks) { + System.setProperty("javax.net.ssl.trustStoreType", "JKS"); + } + } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java b/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java index 155954c0e..8a34b991b 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2019 Florian Schmaus + * Copyright © 2014-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,14 @@ */ package org.jivesoftware.smack.test.util; +import java.security.Security; import java.util.Base64; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.util.stringencoder.Base64.Encoder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + /** * The SmackTestSuite takes care of initializing Smack for the unit tests. For example the Base64 * encoder is configured. @@ -52,5 +55,7 @@ public class SmackTestSuite { } }); + + Security.addProvider(new BouncyCastleProvider()); } } diff --git a/smack-experimental/build.gradle b/smack-experimental/build.gradle index f1078bcc4..f64702d10 100644 --- a/smack-experimental/build.gradle +++ b/smack-experimental/build.gradle @@ -10,6 +10,5 @@ dependencies { testCompile project(path: ":smack-core", configuration: "testRuntime") testCompile project(path: ":smack-extensions", configuration: "testRuntime") - compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion" compile "org.hsluv:hsluv:0.2" } 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 096a7b85b..b3f0c3db4 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 @@ -1,6 +1,6 @@ /** * - * Copyright © 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright © 2017 Paul Schaub, 2019-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,13 +42,10 @@ 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; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - /** * Manager that can be used to determine support for hash functions. By default the Manager announces support for * XEP-0300, as well as for the recommended set of hash algorithms. Those contain SHA256, SHA384, SHA512, SHA3-256, @@ -57,12 +54,6 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; */ public final class HashManager extends Manager { - 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); - } - public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:"; public enum NAMESPACE { diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/hashes/HashTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/hashes/HashTest.java index 355f1a45a..91a9852e7 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/hashes/HashTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/hashes/HashTest.java @@ -18,9 +18,12 @@ package org.jivesoftware.smackx.hashes; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.security.Security; + import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.util.StringUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Test; /** @@ -30,6 +33,10 @@ import org.junit.jupiter.api.Test; */ public class HashTest extends SmackTestSuite { + static { + Security.addProvider(new BouncyCastleProvider()); + } + private static final String testString = "Hello World!"; private static final String md5sum = "ed076287532e86365e841e92bfc50d8c"; private static final String sha1sum = "2ef7bde608ce5404e97d5f042f95f89f1c232871"; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java index eaf525f11..10a6c56aa 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus. + * Copyright 2018-2020 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public abstract class DiscoInfoLookupShortcutMechanism implements Comparable error since specified node was not found responseBuilder.ofType(IQ.Type.error); diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java index a2c07772a..36e9afda2 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java @@ -1,6 +1,6 @@ /** * - * Copyright 2015-2019 Florian Schmaus + * Copyright 2015-2020 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,7 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.tcp.XMPPTCPConnection; import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.TLSUtils; import org.jivesoftware.smackx.debugger.EnhancedDebuggerWindow; import org.jivesoftware.smackx.iqregister.AccountManager; @@ -71,6 +72,10 @@ import org.reflections.scanners.TypeAnnotationsScanner; public class SmackIntegrationTestFramework { + static { + TLSUtils.setDefaultTrustStoreTypeToJksIfRequired(); + } + private static final Logger LOGGER = Logger.getLogger(SmackIntegrationTestFramework.class.getName()); public static boolean SINTTEST_UNIT_TEST = false; diff --git a/smack-omemo-signal/build.gradle b/smack-omemo-signal/build.gradle index e2f21dc54..3c81d2c9d 100644 --- a/smack-omemo-signal/build.gradle +++ b/smack-omemo-signal/build.gradle @@ -1,5 +1,6 @@ -apply plugin: 'checkstyle' -apply plugin: 'maven' +description=""" +Smack API for XEP-0384: OMEMO Encryption using libsignal +""" dependencies { compile project(":smack-im") diff --git a/smack-omemo/build.gradle b/smack-omemo/build.gradle index 354c3db20..89ec8a473 100644 --- a/smack-omemo/build.gradle +++ b/smack-omemo/build.gradle @@ -1,9 +1,11 @@ +description=""" +Smack API for XEP-0384: OMEMO Encryption +""" + dependencies { compile project(":smack-im") compile project(":smack-extensions") compile project(":smack-experimental") - compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion" - testCompile project(path: ":smack-core", configuration: "testRuntime") } 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 243257b99..5f293490f 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,9 +17,6 @@ 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. @@ -29,12 +26,6 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; @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/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java b/smack-omemo/src/test/java/org/jivesoftware/smackx/omemo/WrapperObjectsTest.java index 268b021d8..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 @@ -27,7 +27,6 @@ import static org.junit.Assert.assertNotNull; import java.security.NoSuchAlgorithmException; 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; diff --git a/smack-openpgp/build.gradle b/smack-openpgp/build.gradle index 5ef546976..665f141d2 100644 --- a/smack-openpgp/build.gradle +++ b/smack-openpgp/build.gradle @@ -1,10 +1,6 @@ description = """\ Smack API for XEP-0373: OpenPGP for XMPP.""" -repositories { - mavenCentral() -} - // Note that the test dependencies (junit, …) are inferred from the // sourceSet.test of the core subproject dependencies { diff --git a/version.gradle b/version.gradle index 72ccec1ac..c55ce5484 100644 --- a/version.gradle +++ b/version.gradle @@ -10,7 +10,6 @@ allprojects { // - https://issues.igniterealtime.org/browse/SMACK-858 jxmppVersion = '0.7.0-alpha5' miniDnsVersion = '0.4.0-alpha3' - bouncyCastleVersion = '1.62' smackMinAndroidSdk = 19 } }