Browse Source

Redis Security Store implementation, tests for RPK and PSK security and code cleaning

pull/4711/head
AndrewVolosytnykhThingsboard 5 years ago
parent
commit
d109941cf9
  1. 94
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  2. 81
      application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java
  3. 57
      application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java
  4. 137
      application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java
  5. 21
      common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java
  6. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java
  7. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  8. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java
  9. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java
  10. 17
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java
  11. 176
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java
  12. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java
  13. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java

94
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -17,24 +17,43 @@ package org.thingsboard.server.transport.lwm2m;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.io.IOUtils;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.core.util.Hex;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.controller.AbstractWebsocketTest;
import org.thingsboard.server.controller.TbTestWebSocketClient;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import java.io.IOException;
import java.io.InputStream;
@ -54,9 +73,13 @@ import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DaoSqlTest
public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
@ -137,6 +160,16 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
// certificates trustedby the server (should contain rootCA)
protected final Certificate[] trustedCertificates = new Certificate[1];
protected final int SECURE_PORT = 5686;
protected final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT));
protected final String SECURE_ENDPOINT = "deviceAEndpoint";
protected final String SECURE_URI = "coaps://localhost:" + SECURE_PORT;
private final int PORT = 5686;
private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(PORT));
private final String ENDPOINT = "deviceAEndpoint";
private final String SERVER_URI = "coaps://localhost:" + PORT;
public AbstractLwM2MIntegrationTest() {
// create client credentials
try {
@ -260,10 +293,71 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
Assert.assertNotNull(deviceProfile);
}
@NotNull
private Device createDevice(LwM2MClientCredentials clientCredentials) throws Exception {
Device device = new Device();
device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId());
device.setTenantId(tenantId);
device = doPost("/api/device", device, Device.class);
Assert.assertNotNull(device);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
LwM2MCredentials credentials = new LwM2MCredentials();
credentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
return device;
}
@After
public void after() {
executor.shutdownNow();
wsClient.close();
}
public void basicTestConnectionObserveTelemetry(Security security,
LwM2MClientCredentials credentials,
NetworkConfig coapConfig,
String endpoint) throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION);
Device device = createDevice(credentials);
SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
client.init(security, coapConfig);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
Assert.assertEquals(1, eData.size());
Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
client.destroy();
}
}

81
application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java

@ -17,32 +17,10 @@ package org.thingsboard.server.transport.lwm2m;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.client.object.Security;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import java.util.Collections;
import java.util.List;
import static org.eclipse.leshan.client.object.Security.noSec;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
@ -51,64 +29,11 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
private final String ENDPOINT = "deviceAEndpoint";
@NotNull
private Device createDevice() throws Exception {
Device device = new Device();
device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId());
device.setTenantId(tenantId);
device = doPost("/api/device", device, Device.class);
Assert.assertNotNull(device);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
LwM2MCredentials noSecCredentials = new LwM2MCredentials();
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint(ENDPOINT);
noSecCredentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
return device;
}
@Test
public void testConnectAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION);
Device device = createDevice();
SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);
client.init(SECURITY, COAP_CONFIG);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
Assert.assertEquals(1, eData.size());
Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
client.destroy();
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint(ENDPOINT);
super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT);
}
}

57
application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java

@ -0,0 +1,57 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* 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.thingsboard.server.transport.lwm2m;
import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.core.util.Hex;
import org.junit.Test;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import java.nio.charset.StandardCharsets;
import static org.eclipse.leshan.client.object.Security.psk;
import static org.eclipse.leshan.client.object.Security.rpk;
public class PskRpkLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest {
@Test
public void testConnectWithRPKAndObserveTelemetry() throws Exception {
RPKClientCredentials rpkClientCredentials = new RPKClientCredentials();
rpkClientCredentials.setEndpoint(SECURE_ENDPOINT);
rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded()));
Security security = rpk(SECURE_URI,
123,
clientPublicKey.getEncoded(),
clientPrivateKey.getEncoded(),
serverX509Cert.getPublicKey().getEncoded());
super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT);
}
@Test
public void testConnectWithPSKAndObserveTelemetry() throws Exception {
String pskIdentity = "SOME_PSK_ID";
String pskKey = "73656372657450534b";
PSKClientCredentials clientCredentials = new PSKClientCredentials();
clientCredentials.setEndpoint(SECURE_ENDPOINT);
clientCredentials.setKey(pskKey);
clientCredentials.setIdentity(pskIdentity);
Security security = psk(SECURE_URI,
123,
pskIdentity.getBytes(StandardCharsets.UTF_8),
Hex.decodeHex(pskKey.toCharArray()));
super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT);
}
}

137
application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java

@ -15,145 +15,36 @@
*/
package org.thingsboard.server.transport.lwm2m;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.client.object.Security;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import java.util.Collections;
import java.util.List;
import static org.eclipse.leshan.client.object.Security.x509;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
private final int port = 5686;
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));
private final String endpoint = "deviceAEndpoint";
private final String serverUri = "coaps://localhost:" + port;
@NotNull
private Device createDevice(X509ClientCredentials clientCredentials) throws Exception {
Device device = new Device();
device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId());
device.setTenantId(tenantId);
device = doPost("/api/device", device, Device.class);
Assert.assertNotNull(device);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
LwM2MCredentials credentials = new LwM2MCredentials();
credentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
return device;
}
@Test
public void testConnectAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION);
X509ClientCredentials credentials = new X509ClientCredentials();
credentials.setEndpoint(endpoint);
Device device = createDevice(credentials);
SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
client.init(security, coapConfig);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
Assert.assertEquals(1, eData.size());
Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
client.destroy();
credentials.setEndpoint(SECURE_ENDPOINT);
Security security = x509(SECURE_URI,
123,
clientX509Cert.getEncoded(),
clientPrivateKeyFromCert.getEncoded(),
serverX509Cert.getEncoded());
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT);
}
@Test
public void testConnectWithCertAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION);
X509ClientCredentials credentials = new X509ClientCredentials();
credentials.setEndpoint(endpoint);
credentials.setEndpoint(SECURE_ENDPOINT);
credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted));
Device device = createDevice(credentials);
SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
Security security = x509(serverUri, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
client.init(security, coapConfig);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
Assert.assertEquals(1, eData.size());
Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
client.destroy();
Security security = x509(SECURE_URI,
123,
clientX509CertNotTrusted.getEncoded(),
clientPrivateKeyFromCert.getEncoded(),
serverX509Cert.getEncoded());
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT);
}
}

21
common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java

@ -15,20 +15,25 @@
*/
package org.thingsboard.server.common.data.device.credentials.lwm2m;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Hex;
public abstract class HasKey extends AbstractLwM2MClientCredentials {
private byte[] key;
@Getter
@Setter
private String key;
private byte[] keyInBytes;
@SneakyThrows
public void setKey(String key) {
if (key != null) {
this.key = Hex.decodeHex(key.toLowerCase().toCharArray());
@JsonIgnore
public byte[] getDecodedKey() {
if (keyInBytes == null) {
keyInBytes = Hex.decodeHex(key.toLowerCase().toCharArray());
}
}
public byte[] getKey() {
return key;
return keyInBytes;
}
}

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java

@ -21,10 +21,11 @@ import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
@Data
public class LwM2MBootstrapConfig {
public class LwM2MBootstrapConfig implements Serializable {
/*
interface BootstrapSecurityConfig
servers: BootstrapServersSecurityConfig,

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java

@ -135,10 +135,10 @@ public class LwM2mCredentialsSecurityInfoValidator {
PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
try {
if (pskConfig.getKey() != null && pskConfig.getKey().length > 0) {
if (pskConfig.getDecodedKey() != null && pskConfig.getDecodedKey().length > 0) {
endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint;
if (endpoint != null && !endpoint.isEmpty()) {
result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getKey()));
result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getDecodedKey()));
result.setSecurityMode(PSK);
}
}
@ -153,8 +153,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
try {
if (rpkConfig.getKey() != null) {
PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getKey());
if (rpkConfig.getDecodedKey() != null) {
PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getDecodedKey());
result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key));
result.setSecurityMode(RPK);
} else {

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java

@ -23,8 +23,10 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
import java.io.Serializable;
@Data
public class TbLwM2MSecurityInfo {
public class TbLwM2MSecurityInfo implements Serializable {
private ValidateDeviceCredentialsResponse msg;
private SecurityInfo securityInfo;
private SecurityMode securityMode;

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java

@ -18,8 +18,10 @@ package org.thingsboard.server.transport.lwm2m.secure;
import lombok.Data;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import java.io.Serializable;
@Data
public class TbX509DtlsSessionInfo {
public class TbX509DtlsSessionInfo implements Serializable {
private final String x509CommonName;
private final ValidateDeviceCredentialsResponse credentials;

17
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java

@ -15,28 +15,29 @@
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import com.fasterxml.jackson.databind.JsonNode;
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
private static final String SESSION_EP = "SESSION#EP#";
RedisConnectionFactory connectionFactory;
private final RedisConnectionFactory connectionFactory;
private final FSTConfiguration serializer;
public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) {
this.connectionFactory = redisConnectionFactory;
this.serializer = FSTConfiguration.createDefaultConfiguration();
}
@Override
public void put(String endpoint, TbX509DtlsSessionInfo msg) {
try (var c = connectionFactory.getConnection()) {
var msgJson = JacksonUtil.convertValue(msg, JsonNode.class);
if (msgJson != null) {
c.set(getKey(endpoint), msgJson.toString().getBytes());
var serializedMsg = serializer.asByteArray(msg);
if (serializedMsg != null) {
c.set(getKey(endpoint), serializedMsg);
} else {
throw new RuntimeException("Problem with serialization of message: " + msg.toString());
throw new RuntimeException("Problem with serialization of message: " + msg);
}
}
}
@ -46,7 +47,7 @@ public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
try (var c = connectionFactory.getConnection()) {
var data = c.get(getKey(endpoint));
if (data != null) {
return JacksonUtil.fromString(new String(data), TbX509DtlsSessionInfo.class);
return (TbX509DtlsSessionInfo) serializer.asObject(data);
} else {
return null;
}

176
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java

@ -15,49 +15,55 @@
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.redis.serialization.SecurityInfoSerDes;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
private static final String SEC_EP = "SEC#EP#";
private static final String LOCK_EP = "LOCK#EP#";
private static final String PSKID_SEC = "PSKID#SEC";
private final RedisConnectionFactory connectionFactory;
private SecurityStoreListener listener;
private final FSTConfiguration serializer;
private final RedisLockRegistry redisLock;
public TbLwM2mRedisSecurityStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
redisLock = new RedisLockRegistry(connectionFactory, "Security");
serializer = FSTConfiguration.createDefaultConfiguration();
}
@Override
public SecurityInfo getByEndpoint(String endpoint) {
Lock lock = null;
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(toLockKey(endpoint));
lock.lock();
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data == null) {
return null;
} else {
return deserialize(data);
return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
}
} finally {
if (lock != null) {
lock.unlock();
}
}
}
@Override
public SecurityInfo getByIdentity(String identity) {
Lock lock = null;
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(toLockKey(identity));
lock.lock();
byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes());
if (ep == null) {
return null;
@ -66,102 +72,86 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
if (data == null) {
return null;
} else {
return deserialize(data);
return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
}
}
} finally {
if (lock != null) {
lock.unlock();
}
}
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
//TODO: implement
SecurityInfo info = tbSecurityInfo.getSecurityInfo();
byte[] tbSecurityInfoSerialized = serializer.asByteArray(tbSecurityInfo);
Lock lock = null;
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(tbSecurityInfo.getEndpoint());
lock.lock();
if (info != null && info.getIdentity() != null) {
byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
if (oldEndpointBytes != null) {
String oldEndpoint = new String(oldEndpointBytes);
if (!oldEndpoint.equals(info.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
}
connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
}
}
byte[] previousData = connection.getSet((SEC_EP + tbSecurityInfo.getEndpoint()).getBytes(), tbSecurityInfoSerialized);
if (previousData != null && info != null) {
String previousIdentity = ((TbLwM2MSecurityInfo) serializer.asObject(previousData)).getSecurityInfo().getIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
}
}
} finally {
if (lock != null) {
lock.unlock();
}
}
}
@Override
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
//TODO: implement
return null;
Lock lock = null;
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(endpoint);
lock.lock();
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
return (TbLwM2MSecurityInfo) serializer.asObject(data);
} finally {
if (lock != null) {
lock.unlock();
}
}
}
@Override
public void remove(String endpoint) {
//TODO: implement
}
// @Override
// public Collection<SecurityInfo> getAll() {
// try (var connection = connectionFactory.getConnection()) {
// Collection<SecurityInfo> list = new LinkedList<>();
// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
// List<Cursor<byte[]>> scans = new ArrayList<>();
// if (connection instanceof RedisClusterConnection) {
// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
// });
// } else {
// scans.add(connection.scan(scanOptions));
// }
//
// scans.forEach(scan -> {
// scan.forEachRemaining(key -> {
// byte[] element = connection.get(key);
// list.add(deserialize(element));
// });
// });
// return list;
// }
// }
//
// @Override
// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
// byte[] data = serialize(info);
// try (var connection = connectionFactory.getConnection()) {
// if (info.getIdentity() != null) {
// // populate the secondary index (security info by PSK id)
// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
// if (!oldEndpoint.equals(info.getEndpoint())) {
// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
// }
// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
// }
//
// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
// SecurityInfo previous = previousData == null ? null : deserialize(previousData);
// String previousIdentity = previous == null ? null : previous.getIdentity();
// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
// }
//
// return previous;
// }
// }
//
// @Override
// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
// try (var connection = connectionFactory.getConnection()) {
// byte[] data = connection.get((SEC_EP + endpoint).getBytes());
//
// if (data != null) {
// SecurityInfo info = deserialize(data);
// if (info.getIdentity() != null) {
// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
// }
// connection.del((SEC_EP + endpoint).getBytes());
// if (listener != null) {
// listener.securityInfoRemoved(infosAreCompromised, info);
// }
// return info;
// }
// }
// return null;
// }
private byte[] serialize(SecurityInfo secInfo) {
return SecurityInfoSerDes.serialize(secInfo);
Lock lock = null;
try (var connection = connectionFactory.getConnection()) {
lock = redisLock.obtain(endpoint);
lock.lock();
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null) {
SecurityInfo info = ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo();
if (info != null && info.getIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
}
connection.del((SEC_EP + endpoint).getBytes());
}
} finally {
if (lock != null) {
lock.unlock();
}
}
}
private SecurityInfo deserialize(byte[] data) {
return SecurityInfoSerDes.deserialize(data);
private String toLockKey(String endpoint) {
return LOCK_EP + endpoint;
}
}

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java

@ -21,8 +21,10 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serializable;
@Data
public class TransportDeviceInfo {
public class TransportDeviceInfo implements Serializable {
private TenantId tenantId;
private CustomerId customerId;

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java

@ -19,9 +19,11 @@ import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceProfile;
import java.io.Serializable;
@Data
@Builder
public class ValidateDeviceCredentialsResponse implements DeviceProfileAware {
public class ValidateDeviceCredentialsResponse implements DeviceProfileAware, Serializable {
private final TransportDeviceInfo deviceInfo;
private final DeviceProfile deviceProfile;

Loading…
Cancel
Save