From 468665ca75f3d68d29e13bb611f16a5f12647d6e Mon Sep 17 00:00:00 2001 From: Francesco Vaiani Date: Thu, 13 Oct 2022 18:15:43 +0200 Subject: [PATCH] Add support to ignore_ssl_error flag The SDK currently does not support self signed certificates on MQTT connections, but this functionality is heavily needed for some self hosted instances of Astarte. This commit adds an optional flag in the AstarteDevice constructor that enables a custom TrustManager that allows self signed certificates for SSL sockets in paho and disables hostname verifications on mqtt connection options. Signed-off-by: Francesco Vaiani --- .../devicesdk/AstartePairingHandler.java | 10 +++- .../devicesdk/AstartePairingService.java | 8 ++- .../devicesdk/crypto/AstarteCryptoStore.java | 2 +- .../transport/AstarteTransportFactory.java | 10 +++- ...alSSLAuthenticationMqttConnectionInfo.java | 9 +++- .../android/AstarteAndroidCryptoStore.java | 4 +- .../android/AstarteAndroidDevice.java | 22 +++++++- .../AstarteAndroidMutualSSLSocketFactory.java | 46 +++++++++++------ .../generic/AstarteGenericCryptoStore.java | 4 +- .../generic/AstarteGenericDevice.java | 29 +++++++++-- .../AstarteGenericMutualSSLSocketFactory.java | 50 +++++++++++++------ 11 files changed, 149 insertions(+), 45 deletions(-) diff --git a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingHandler.java b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingHandler.java index b671d4b..4939f14 100644 --- a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingHandler.java +++ b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingHandler.java @@ -15,12 +15,15 @@ public class AstartePairingHandler { private Certificate m_certificate; + private boolean ignoreSSLErrors; + public AstartePairingHandler( String pairingUrl, String astarteRealm, String deviceId, String credentialSecret, - AstarteCryptoStore cryptoStore) { + AstarteCryptoStore cryptoStore, + boolean ignoreSSLErrors) { m_astarteRealm = astarteRealm; m_deviceId = deviceId; m_credentialSecret = credentialSecret; @@ -29,6 +32,8 @@ public AstartePairingHandler( m_AstartePairingService = new AstartePairingService(pairingUrl, astarteRealm); m_certificate = m_cryptoStore.getCertificate(); + + this.ignoreSSLErrors = ignoreSSLErrors; } public void init() throws AstartePairingException { @@ -57,7 +62,8 @@ public boolean isCertificateAvailable() { private void reloadTransports() throws AstartePairingException { m_transports = - m_AstartePairingService.reloadTransports(m_credentialSecret, m_cryptoStore, m_deviceId); + m_AstartePairingService.reloadTransports( + m_credentialSecret, m_cryptoStore, m_deviceId, ignoreSSLErrors); } public void requestNewCertificate() throws AstartePairingException { diff --git a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingService.java b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingService.java index 4df3ad4..1269d30 100644 --- a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingService.java +++ b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/AstartePairingService.java @@ -105,7 +105,10 @@ public String registerDevice(String jwtToken, String deviceId) } protected List reloadTransports( - String credentialSecret, AstarteCryptoStore cryptoStore, String deviceId) + String credentialSecret, + AstarteCryptoStore cryptoStore, + String deviceId, + boolean ignoreSSLErrors) throws AstartePairingException { // Build the request URL for Astarte MQTT v1 HttpUrl requestUrl; @@ -161,7 +164,8 @@ protected List reloadTransports( m_astarteRealm, deviceId, transportObjects.getJSONObject(key), - cryptoStore); + cryptoStore, + ignoreSSLErrors); transports.add(supportedTransport); } catch (Exception e) { e.printStackTrace(); diff --git a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/crypto/AstarteCryptoStore.java b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/crypto/AstarteCryptoStore.java index 1e93ab4..66fcf93 100644 --- a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/crypto/AstarteCryptoStore.java +++ b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/crypto/AstarteCryptoStore.java @@ -21,7 +21,7 @@ public interface AstarteCryptoStore { String generateCSR(String directoryString) throws IOException, OperatorCreationException; - SSLSocketFactory getSSLSocketFactory() + SSLSocketFactory getSSLSocketFactory(boolean ignoreSSLErrors) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException; } diff --git a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/AstarteTransportFactory.java b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/AstarteTransportFactory.java index fead04a..4284a0a 100644 --- a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/AstarteTransportFactory.java +++ b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/AstarteTransportFactory.java @@ -1,5 +1,6 @@ package org.astarteplatform.devicesdk.transport; +import javax.net.ssl.*; import org.astarteplatform.devicesdk.crypto.AstarteCryptoStore; import org.astarteplatform.devicesdk.protocol.AstarteProtocolType; import org.astarteplatform.devicesdk.transport.mqtt.AstarteMqttV1Transport; @@ -12,7 +13,8 @@ public static AstarteTransport createAstarteTransportFromPairing( String astarteRealm, String deviceId, JSONObject protocolData, - AstarteCryptoStore cryptoStore) { + AstarteCryptoStore cryptoStore, + boolean ignoreSSLErrors) { switch (protocolType) { case ASTARTE_MQTT_V1: try { @@ -22,7 +24,11 @@ public static AstarteTransport createAstarteTransportFromPairing( } return new AstarteMqttV1Transport( new MutualSSLAuthenticationMqttConnectionInfo( - brokerUrl, astarteRealm, deviceId, cryptoStore.getSSLSocketFactory())); + brokerUrl, + astarteRealm, + deviceId, + cryptoStore.getSSLSocketFactory(ignoreSSLErrors), + ignoreSSLErrors)); } catch (Exception e) { e.printStackTrace(); return null; diff --git a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/mqtt/MutualSSLAuthenticationMqttConnectionInfo.java b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/mqtt/MutualSSLAuthenticationMqttConnectionInfo.java index 32570cc..8846cc5 100644 --- a/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/mqtt/MutualSSLAuthenticationMqttConnectionInfo.java +++ b/DeviceSDK/src/main/java/org/astarteplatform/devicesdk/transport/mqtt/MutualSSLAuthenticationMqttConnectionInfo.java @@ -9,7 +9,11 @@ public class MutualSSLAuthenticationMqttConnectionInfo implements MqttConnection private final String m_clientId; public MutualSSLAuthenticationMqttConnectionInfo( - String brokerUrl, String astarteRealm, String deviceId, SSLSocketFactory sslSocketFactory) { + String brokerUrl, + String astarteRealm, + String deviceId, + SSLSocketFactory sslSocketFactory, + boolean ignoreSSLErrors) { m_brokerUrl = brokerUrl; m_mqttConnectOptions = new MqttConnectOptions(); m_mqttConnectOptions.setConnectionTimeout(60); @@ -23,6 +27,9 @@ public MutualSSLAuthenticationMqttConnectionInfo( } catch (Exception e) { e.printStackTrace(); } + if (ignoreSSLErrors) { + m_mqttConnectOptions.setHttpsHostnameVerificationEnabled(false); + } m_clientId = astarteRealm + "/" + deviceId; } diff --git a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidCryptoStore.java b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidCryptoStore.java index 25bb272..9339dd8 100644 --- a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidCryptoStore.java +++ b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidCryptoStore.java @@ -139,10 +139,10 @@ public String generateCSR(String directoryString) throws IOException, OperatorCr } @Override - public SSLSocketFactory getSSLSocketFactory() + public SSLSocketFactory getSSLSocketFactory(boolean ignoreSSLErrors) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - return new AstarteAndroidMutualSSLSocketFactory(); + return new AstarteAndroidMutualSSLSocketFactory(ignoreSSLErrors); } private String csrToString(PKCS10CertificationRequest csr) throws IOException { diff --git a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidDevice.java b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidDevice.java index e794f5c..7a7c2e2 100644 --- a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidDevice.java +++ b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidDevice.java @@ -16,13 +16,33 @@ public AstarteAndroidDevice( String pairingBaseUrl, Context context) throws JSONException, AstarteInvalidInterfaceException { + this( + deviceId, + astarteRealm, + credentialSecret, + interfaceProvider, + pairingBaseUrl, + context, + false); + } + + public AstarteAndroidDevice( + String deviceId, + String astarteRealm, + String credentialSecret, + AstarteInterfaceProvider interfaceProvider, + String pairingBaseUrl, + Context context, + boolean ignoreSSLErrors) + throws JSONException, AstarteInvalidInterfaceException { super( new AstartePairingHandler( pairingBaseUrl, astarteRealm, deviceId, credentialSecret, - new AstarteAndroidCryptoStore()), + new AstarteAndroidCryptoStore(), + ignoreSSLErrors), new AstarteAndroidPropertyStorage(context, "astarte.property_store." + deviceId), new AstarteAndroidFailedMessageStorage( AstarteAndroidRoomDatabase.getDatabase(context).astarteFailedMessageDao()), diff --git a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidMutualSSLSocketFactory.java b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidMutualSSLSocketFactory.java index d144496..1b394d5 100644 --- a/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidMutualSSLSocketFactory.java +++ b/DeviceSDKAndroid/src/main/java/org/astarteplatform/devicesdk/android/AstarteAndroidMutualSSLSocketFactory.java @@ -11,25 +11,43 @@ import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.*; class AstarteAndroidMutualSSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory internalSSLSocketFactory; - public AstarteAndroidMutualSSLSocketFactory() + public AstarteAndroidMutualSSLSocketFactory(boolean ignoreSSLErrors) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - // CA certificate is used to authenticate server - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - KeyStore caStore = KeyStore.getInstance("AndroidCAStore"); - caStore.load(null); - trustManagerFactory.init(caStore); + TrustManager[] trustManagers; + if (ignoreSSLErrors) { + TrustManager[] trustAllCerts = + new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] chain, String authType) {} + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] chain, String authType) {} + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + } + }; + trustManagers = trustAllCerts; + } else { + // CA certificate is used to authenticate server + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + KeyStore caStore = KeyStore.getInstance("AndroidCAStore"); + caStore.load(null); + trustManagerFactory.init(caStore); + trustManagers = trustManagerFactory.getTrustManagers(); + } // client key and certificates are sent to server so it can authenticate us final KeyStore androidKeyStore = KeyStore.getInstance("AndroidKeyStore"); @@ -84,7 +102,7 @@ public PrivateKey getPrivateKey(String alias) { // finally, create SSL socket factory SSLContext context = SSLContext.getInstance("TLSv1.2"); - context.init(new KeyManager[] {keyManager}, trustManagerFactory.getTrustManagers(), null); + context.init(new KeyManager[] {keyManager}, trustManagers, null); internalSSLSocketFactory = context.getSocketFactory(); } diff --git a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericCryptoStore.java b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericCryptoStore.java index 03b23d3..c72fe7f 100644 --- a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericCryptoStore.java +++ b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericCryptoStore.java @@ -98,11 +98,11 @@ public String generateCSR(String directoryString) throws IOException, OperatorCr } @Override - public SSLSocketFactory getSSLSocketFactory() + public SSLSocketFactory getSSLSocketFactory(boolean ignoreSSLErrors) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { if (m_socketFactory == null) { - m_socketFactory = new AstarteGenericMutualSSLSocketFactory(this); + m_socketFactory = new AstarteGenericMutualSSLSocketFactory(this, ignoreSSLErrors); } return m_socketFactory; diff --git a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericDevice.java b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericDevice.java index d0d8061..7187ad6 100644 --- a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericDevice.java +++ b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericDevice.java @@ -22,6 +22,26 @@ public AstarteGenericDevice( ConnectionSource connectionSource) throws JSONException, AstarteInvalidInterfaceException, AstartePropertyStorageException, SQLException { + this( + deviceId, + astarteRealm, + credentialSecret, + interfaceProvider, + pairingBaseUrl, + connectionSource, + false); + } + + public AstarteGenericDevice( + String deviceId, + String astarteRealm, + String credentialSecret, + AstarteInterfaceProvider interfaceProvider, + String pairingBaseUrl, + ConnectionSource connectionSource, + boolean ignoreSSLErrors) + throws JSONException, AstarteInvalidInterfaceException, AstartePropertyStorageException, + SQLException { this( deviceId, astarteRealm, @@ -31,7 +51,8 @@ public AstarteGenericDevice( new AstarteGenericPropertyStorage( DaoManager.createDao(connectionSource, AstarteGenericPropertyEntry.class)), new AstarteGenericFailedMessageStorage( - DaoManager.createDao(connectionSource, AstarteGenericFailedMessage.class))); + DaoManager.createDao(connectionSource, AstarteGenericFailedMessage.class)), + ignoreSSLErrors); } public AstarteGenericDevice( @@ -41,7 +62,8 @@ public AstarteGenericDevice( AstarteInterfaceProvider interfaceProvider, String pairingBaseUrl, AstartePropertyStorage propertyStorage, - AstarteFailedMessageStorage failedMessageStorage) + AstarteFailedMessageStorage failedMessageStorage, + boolean ignoreSSLErrors) throws JSONException, AstarteInvalidInterfaceException { super( new AstartePairingHandler( @@ -49,7 +71,8 @@ public AstarteGenericDevice( astarteRealm, deviceId, credentialSecret, - new AstarteGenericCryptoStore()), + new AstarteGenericCryptoStore(), + ignoreSSLErrors), propertyStorage, failedMessageStorage, interfaceProvider); diff --git a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericMutualSSLSocketFactory.java b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericMutualSSLSocketFactory.java index 8ebe615..a1711e8 100644 --- a/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericMutualSSLSocketFactory.java +++ b/DeviceSDKGeneric/src/main/java/org/astarteplatform/devicesdk/generic/AstarteGenericMutualSSLSocketFactory.java @@ -13,27 +13,47 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.*; class AstarteGenericMutualSSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory internalSSLSocketFactory; private AstarteGenericCryptoStore mCryptoStore; - public AstarteGenericMutualSSLSocketFactory(AstarteGenericCryptoStore cryptoStore) + public AstarteGenericMutualSSLSocketFactory( + AstarteGenericCryptoStore cryptoStore, boolean ignoreSSLErrors) throws KeyManagementException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - // CA certificate is used to authenticate server - String caFile = System.getProperty("java.home") + "/lib/security/cacerts"; - KeyStore caStore = KeyStore.getInstance(KeyStore.getDefaultType()); - caStore.load(new FileInputStream(caFile), null); - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(caStore); + TrustManager[] trustManagers; + if (ignoreSSLErrors) { + TrustManager[] trustAllCerts = + new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] chain, String authType) {} + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] chain, String authType) {} + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + } + }; + trustManagers = trustAllCerts; + } else { + // CA certificate is used to authenticate server + String caFile = System.getProperty("java.home") + "/lib/security/cacerts"; + KeyStore caStore = KeyStore.getInstance(KeyStore.getDefaultType()); + caStore.load(new FileInputStream(caFile), null); + + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(caStore); + trustManagers = trustManagerFactory.getTrustManagers(); + } mCryptoStore = cryptoStore; @@ -78,7 +98,7 @@ public PrivateKey getPrivateKey(String alias) { // finally, create SSL socket factory SSLContext context = SSLContext.getInstance("TLSv1.2"); - context.init(new KeyManager[] {keyManager}, trustManagerFactory.getTrustManagers(), null); + context.init(new KeyManager[] {keyManager}, trustManagers, null); internalSSLSocketFactory = context.getSocketFactory(); }