mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[mynice] Enable legacy TLS protocols (#20205)
* Enabling legacy TLS protocols Signed-off-by: gael@lhopital.org <gael@lhopital.org>
This commit is contained in:
@@ -14,4 +14,29 @@
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: MyNice Binding</name>
|
||||
|
||||
<properties>
|
||||
<bouncycastle.version>1.83</bouncycastle.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bctls-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+17
-19
@@ -14,27 +14,24 @@ package org.openhab.binding.mynice.internal;
|
||||
|
||||
import static org.openhab.binding.mynice.internal.MyNiceBindingConstants.*;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mynice.internal.handler.GateHandler;
|
||||
import org.openhab.binding.mynice.internal.handler.It4WifiHandler;
|
||||
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
||||
import org.openhab.binding.mynice.internal.ssl.BcJsseSocketFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
|
||||
/**
|
||||
* The {@link MyNiceHandlerFactory} is responsible for creating thing handlers.
|
||||
@@ -46,18 +43,7 @@ import org.osgi.service.component.annotations.Component;
|
||||
public class MyNiceHandlerFactory extends BaseThingHandlerFactory {
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(BRIDGE_TYPE_IT4WIFI, THING_TYPE_SWING,
|
||||
THING_TYPE_SLIDING);
|
||||
private final SSLSocketFactory socketFactory;
|
||||
|
||||
@Activate
|
||||
public MyNiceHandlerFactory() {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
|
||||
socketFactory = sslContext.getSocketFactory();
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
private @Nullable SSLSocketFactory socketFactory;
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
@@ -69,7 +55,7 @@ public class MyNiceHandlerFactory extends BaseThingHandlerFactory {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (BRIDGE_TYPE_IT4WIFI.equals(thingTypeUID)) {
|
||||
return new It4WifiHandler((Bridge) thing, socketFactory);
|
||||
return new It4WifiHandler((Bridge) thing, getSocketFactory());
|
||||
} else if (THING_TYPE_SWING.equals(thingTypeUID)) {
|
||||
return new GateHandler(thing);
|
||||
} else if (THING_TYPE_SLIDING.equals(thingTypeUID)) {
|
||||
@@ -78,4 +64,16 @@ public class MyNiceHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SSLSocketFactory getSocketFactory() {
|
||||
if (socketFactory == null) {
|
||||
socketFactory = BcJsseSocketFactory.get();
|
||||
}
|
||||
return Objects.requireNonNull(socketFactory);
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void dispose() {
|
||||
BcJsseSocketFactory.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
+40
-2
@@ -18,6 +18,7 @@ import static org.openhab.core.types.RefreshType.REFRESH;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -59,6 +60,8 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class It4WifiHandler extends BaseBridgeHandler {
|
||||
private static final String NEEDED_PROTOCOL = "TLSv1.2";
|
||||
private static final String NEEDED_CIPHER = "TLS_RSA_WITH_AES_256_CBC_SHA256";
|
||||
private static final int SERVER_PORT = 443;
|
||||
private static final int MAX_HANDSHAKE_ATTEMPTS = 3;
|
||||
private static final int KEEPALIVE_DELAY_S = 235; // Timeout seems to be at 6 min
|
||||
@@ -133,12 +136,38 @@ public class It4WifiHandler extends BaseBridgeHandler {
|
||||
private void startConnector() {
|
||||
It4WifiConfiguration config = getConfigAs(It4WifiConfiguration.class);
|
||||
freeKeepAlive();
|
||||
SSLSocket localSocket = null;
|
||||
try {
|
||||
logger.debug("Initiating connection to IT4Wifi {} on port {}...", config.hostname, SERVER_PORT);
|
||||
|
||||
SSLSocket localSocket = (SSLSocket) socketFactory.createSocket(config.hostname, SERVER_PORT);
|
||||
sslSocket = Optional.of(localSocket);
|
||||
localSocket = (SSLSocket) socketFactory.createSocket(config.hostname, SERVER_PORT);
|
||||
|
||||
// We require an older TLS protocol and a static RSA cipher suite to avoid the problematic
|
||||
// DHE exchange that causes 'insufficient_security' errors. If these are not available on
|
||||
// the current JVM, we abort the connection.
|
||||
String[] enabledLegacyProtocols = Arrays.stream(localSocket.getSupportedProtocols())
|
||||
.filter(NEEDED_PROTOCOL::equals).toArray(String[]::new);
|
||||
String[] legacyCiphers = Arrays.stream(localSocket.getSupportedCipherSuites())
|
||||
.filter(suite -> NEEDED_CIPHER.equals(suite)).toArray(String[]::new);
|
||||
|
||||
if (enabledLegacyProtocols.length > 0) {
|
||||
localSocket.setEnabledProtocols(enabledLegacyProtocols);
|
||||
} else {
|
||||
throw new IOException(
|
||||
"Required TLS protocol " + NEEDED_PROTOCOL + " is not supported by this JVM for IT4Wifi");
|
||||
}
|
||||
|
||||
if (legacyCiphers.length > 0) {
|
||||
localSocket.setEnabledCipherSuites(legacyCiphers);
|
||||
} else {
|
||||
throw new IOException(
|
||||
"Required TLS cipher suite " + NEEDED_CIPHER + " is not supported by this JVM for IT4Wifi");
|
||||
}
|
||||
|
||||
localSocket.startHandshake();
|
||||
logger.debug("Handshake successful. Protocol: {}, Cipher: {}", localSocket.getSession().getProtocol(),
|
||||
localSocket.getSession().getCipherSuite());
|
||||
sslSocket = Optional.of(localSocket);
|
||||
|
||||
It4WifiConnector localConnector = new It4WifiConnector(this, localSocket);
|
||||
connector = Optional.of(localConnector);
|
||||
@@ -149,7 +178,16 @@ public class It4WifiHandler extends BaseBridgeHandler {
|
||||
} catch (UnknownHostException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-hostname");
|
||||
} catch (IOException e) {
|
||||
if (localSocket != null) {
|
||||
try {
|
||||
localSocket.close();
|
||||
} catch (IOException closeException) {
|
||||
logger.debug("Error closing socket after failed handshake", closeException);
|
||||
}
|
||||
}
|
||||
sslSocket = Optional.empty();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error-handshake-init");
|
||||
logger.warn("Error in IT4Wifi handshake: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.mynice.internal.ssl;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
||||
|
||||
/**
|
||||
* Factory to create SSLSocketFactory.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BcJsseSocketFactory {
|
||||
private static volatile boolean bcAdded = false;
|
||||
private static volatile boolean bcJsseAdded = false;
|
||||
private static volatile boolean initialized = false;
|
||||
|
||||
private BcJsseSocketFactory() {
|
||||
}
|
||||
|
||||
public static SSLSocketFactory get() {
|
||||
initializeOnce();
|
||||
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS", "BCJSSE");
|
||||
context.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, new SecureRandom());
|
||||
return context.getSocketFactory();
|
||||
} catch (NoSuchAlgorithmException | NoSuchProviderException | KeyManagementException e) {
|
||||
throw new IllegalStateException("Unable to initialize BCJSSE SSLContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dispose() {
|
||||
if (bcJsseAdded) {
|
||||
Security.removeProvider("BCJSSE");
|
||||
bcJsseAdded = false;
|
||||
}
|
||||
if (bcAdded) {
|
||||
Security.removeProvider("BC");
|
||||
bcAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeOnce() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
synchronized (BcJsseSocketFactory.class) {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the existing BC provider from the platform (provided by bcprov bundle)
|
||||
if (Security.getProvider("BC") == null) {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
bcAdded = true;
|
||||
}
|
||||
|
||||
// Get or register the BCJSSE provider
|
||||
if (Security.getProvider("BCJSSE") == null) {
|
||||
// Initialize BCJSSE in non-FIPS mode. It will find the existing "BC" provider.
|
||||
Security.addProvider(new BouncyCastleJsseProvider(false));
|
||||
bcJsseAdded = true;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user