added lwm2m x509 test
This commit is contained in:
		
							parent
							
								
									4ec25beeb3
								
							
						
					
					
						commit
						92719c2ac2
					
				@ -26,13 +26,13 @@ import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
@RunWith(ClasspathSuite.class)
 | 
			
		||||
@ClasspathSuite.ClassnameFilters({
 | 
			
		||||
        "org.thingsboard.server.transport.*.rpc.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.attributes.updates.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.attributes.request.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.claim.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.*.provision.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.rpc.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.attributes.updates.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.attributes.request.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.claim.sql.*Test",
 | 
			
		||||
//        "org.thingsboard.server.transport.*.provision.sql.*Test",
 | 
			
		||||
        "org.thingsboard.server.transport.lwm2m.*Test"
 | 
			
		||||
})
 | 
			
		||||
public class TransportSqlTestSuite {
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import org.apache.commons.io.IOUtils;
 | 
			
		||||
import org.eclipse.leshan.core.util.Hex;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Assert;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
@ -35,6 +36,23 @@ import org.thingsboard.server.controller.AbstractWebsocketTest;
 | 
			
		||||
import org.thingsboard.server.controller.TbTestWebSocketClient;
 | 
			
		||||
import org.thingsboard.server.dao.service.DaoSqlTest;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.math.BigInteger;
 | 
			
		||||
import java.security.AlgorithmParameters;
 | 
			
		||||
import java.security.GeneralSecurityException;
 | 
			
		||||
import java.security.KeyFactory;
 | 
			
		||||
import java.security.KeyStore;
 | 
			
		||||
import java.security.PrivateKey;
 | 
			
		||||
import java.security.PublicKey;
 | 
			
		||||
import java.security.cert.Certificate;
 | 
			
		||||
import java.security.cert.X509Certificate;
 | 
			
		||||
import java.security.spec.ECGenParameterSpec;
 | 
			
		||||
import java.security.spec.ECParameterSpec;
 | 
			
		||||
import java.security.spec.ECPoint;
 | 
			
		||||
import java.security.spec.ECPrivateKeySpec;
 | 
			
		||||
import java.security.spec.ECPublicKeySpec;
 | 
			
		||||
import java.security.spec.KeySpec;
 | 
			
		||||
import java.util.Base64;
 | 
			
		||||
import java.util.concurrent.Executors;
 | 
			
		||||
import java.util.concurrent.ScheduledExecutorService;
 | 
			
		||||
@ -46,6 +64,114 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
 | 
			
		||||
    protected ScheduledExecutorService executor;
 | 
			
		||||
    protected TbTestWebSocketClient wsClient;
 | 
			
		||||
 | 
			
		||||
    protected final PublicKey clientPublicKey; // client public key used for RPK
 | 
			
		||||
    protected final PrivateKey clientPrivateKey; // client private key used for RPK
 | 
			
		||||
    protected final PublicKey serverPublicKey; // server public key used for RPK
 | 
			
		||||
    protected final PrivateKey serverPrivateKey; // server private key used for RPK
 | 
			
		||||
 | 
			
		||||
    // client private key used for X509
 | 
			
		||||
    protected final PrivateKey clientPrivateKeyFromCert;
 | 
			
		||||
    // server private key used for X509
 | 
			
		||||
    protected final PrivateKey serverPrivateKeyFromCert;
 | 
			
		||||
    // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test)
 | 
			
		||||
    protected final X509Certificate clientX509Cert;
 | 
			
		||||
    // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test)
 | 
			
		||||
    protected final X509Certificate clientX509CertWithBadCN;
 | 
			
		||||
    // client certificate self-signed with a good CN (CN start by leshan_integration_test)
 | 
			
		||||
    protected final X509Certificate clientX509CertSelfSigned;
 | 
			
		||||
    // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test)
 | 
			
		||||
    protected final X509Certificate clientX509CertNotTrusted;
 | 
			
		||||
    // server certificate signed by rootCA
 | 
			
		||||
    protected final X509Certificate serverX509Cert;
 | 
			
		||||
    // self-signed server certificate
 | 
			
		||||
    protected final X509Certificate serverX509CertSelfSigned;
 | 
			
		||||
    // rootCA used by the server
 | 
			
		||||
    protected final X509Certificate rootCAX509Cert;
 | 
			
		||||
    // certificates trustedby the server (should contain rootCA)
 | 
			
		||||
    protected final Certificate[] trustedCertificates = new Certificate[1];
 | 
			
		||||
 | 
			
		||||
    public AbstractLwM2MIntegrationTest() {
 | 
			
		||||
// create client credentials
 | 
			
		||||
        try {
 | 
			
		||||
            // Get point values
 | 
			
		||||
            byte[] publicX = Hex
 | 
			
		||||
                    .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray());
 | 
			
		||||
            byte[] publicY = Hex
 | 
			
		||||
                    .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray());
 | 
			
		||||
            byte[] privateS = Hex
 | 
			
		||||
                    .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray());
 | 
			
		||||
 | 
			
		||||
            // Get Elliptic Curve Parameter spec for secp256r1
 | 
			
		||||
            AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
 | 
			
		||||
            algoParameters.init(new ECGenParameterSpec("secp256r1"));
 | 
			
		||||
            ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
 | 
			
		||||
 | 
			
		||||
            // Create key specs
 | 
			
		||||
            KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
 | 
			
		||||
                    parameterSpec);
 | 
			
		||||
            KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
 | 
			
		||||
 | 
			
		||||
            // Get keys
 | 
			
		||||
            clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
 | 
			
		||||
            clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
 | 
			
		||||
 | 
			
		||||
            // Get certificates from key store
 | 
			
		||||
            char[] clientKeyStorePwd = "client".toCharArray();
 | 
			
		||||
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
 | 
			
		||||
            try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) {
 | 
			
		||||
                clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd);
 | 
			
		||||
            clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client");
 | 
			
		||||
            clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn");
 | 
			
		||||
            clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed");
 | 
			
		||||
            clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted");
 | 
			
		||||
        } catch (GeneralSecurityException | IOException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // create server credentials
 | 
			
		||||
        try {
 | 
			
		||||
            // Get point values
 | 
			
		||||
            byte[] publicX = Hex
 | 
			
		||||
                    .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray());
 | 
			
		||||
            byte[] publicY = Hex
 | 
			
		||||
                    .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray());
 | 
			
		||||
            byte[] privateS = Hex
 | 
			
		||||
                    .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray());
 | 
			
		||||
 | 
			
		||||
            // Get Elliptic Curve Parameter spec for secp256r1
 | 
			
		||||
            AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
 | 
			
		||||
            algoParameters.init(new ECGenParameterSpec("secp256r1"));
 | 
			
		||||
            ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
 | 
			
		||||
 | 
			
		||||
            // Create key specs
 | 
			
		||||
            KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
 | 
			
		||||
                    parameterSpec);
 | 
			
		||||
            KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
 | 
			
		||||
 | 
			
		||||
//            // Get keys
 | 
			
		||||
            serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
 | 
			
		||||
            serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
 | 
			
		||||
 | 
			
		||||
            // Get certificates from key store
 | 
			
		||||
            char[] serverKeyStorePwd = "server".toCharArray();
 | 
			
		||||
            KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
 | 
			
		||||
            try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) {
 | 
			
		||||
                serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd);
 | 
			
		||||
            rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA");
 | 
			
		||||
            serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server");
 | 
			
		||||
            serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed");
 | 
			
		||||
            trustedCertificates[0] = rootCAX509Cert;
 | 
			
		||||
        } catch (GeneralSecurityException | IOException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void beforeTest() throws Exception {
 | 
			
		||||
        executor = Executors.newScheduledThreadPool(10);
 | 
			
		||||
@ -60,7 +186,8 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
 | 
			
		||||
            lwModel.setTenantId(tenantId);
 | 
			
		||||
            byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName));
 | 
			
		||||
            lwModel.setData(Base64.getEncoder().encodeToString(bytes));
 | 
			
		||||
            lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>(){});
 | 
			
		||||
            lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() {
 | 
			
		||||
            });
 | 
			
		||||
            Assert.assertNotNull(lwModel);
 | 
			
		||||
        }
 | 
			
		||||
        wsClient = buildAndConnectWebSocketClient();
 | 
			
		||||
@ -69,7 +196,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
 | 
			
		||||
    protected void createDeviceProfile(String transportConfiguration) throws Exception {
 | 
			
		||||
        deviceProfile = new DeviceProfile();
 | 
			
		||||
 | 
			
		||||
        deviceProfile.setName("LwM2M No Security");
 | 
			
		||||
        deviceProfile.setName("LwM2M");
 | 
			
		||||
        deviceProfile.setType(DeviceProfileType.DEFAULT);
 | 
			
		||||
        deviceProfile.setTenantId(tenantId);
 | 
			
		||||
        deviceProfile.setTransportType(DeviceTransportType.LWM2M);
 | 
			
		||||
@ -87,7 +214,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @After
 | 
			
		||||
    public void after() {
 | 
			
		||||
    public void after() throws InterruptedException {
 | 
			
		||||
        executor.shutdownNow();
 | 
			
		||||
        wsClient.close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,8 @@
 | 
			
		||||
 */
 | 
			
		||||
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;
 | 
			
		||||
@ -39,6 +41,7 @@ import org.thingsboard.server.transport.lwm2m.secure.credentials.NoSecClientCred
 | 
			
		||||
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 {
 | 
			
		||||
@ -91,6 +94,10 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
 | 
			
		||||
            "  }\n" +
 | 
			
		||||
            "}";
 | 
			
		||||
 | 
			
		||||
    private final int port = 5685;
 | 
			
		||||
    private final Security security =  noSec("coap://localhost:" + port, 123);
 | 
			
		||||
    private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port));
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    private Device createDevice(String deviceAEndpoint) throws Exception {
 | 
			
		||||
        Device device = new Device();
 | 
			
		||||
@ -138,7 +145,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
 | 
			
		||||
 | 
			
		||||
        wsClient.registerWaitForUpdate();
 | 
			
		||||
        LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint);
 | 
			
		||||
        client.init();
 | 
			
		||||
        client.init(security, coapConfig);
 | 
			
		||||
        String msg = wsClient.waitForUpdate();
 | 
			
		||||
 | 
			
		||||
        EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,205 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.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.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 org.thingsboard.server.transport.lwm2m.secure.credentials.X509ClientCredentialsConfig;
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
    protected final String TRANSPORT_CONFIGURATION = "{\n" +
 | 
			
		||||
            "  \"type\": \"LWM2M\",\n" +
 | 
			
		||||
            "  \"observeAttr\": {\n" +
 | 
			
		||||
            "    \"keyName\": {\n" +
 | 
			
		||||
            "      \"/3_1.0/0/9\": \"batteryLevel\"\n" +
 | 
			
		||||
            "    },\n" +
 | 
			
		||||
            "    \"observe\": [],\n" +
 | 
			
		||||
            "    \"attribute\": [\n" +
 | 
			
		||||
            "    ],\n" +
 | 
			
		||||
            "    \"telemetry\": [\n" +
 | 
			
		||||
            "      \"/3_1.0/0/9\"\n" +
 | 
			
		||||
            "    ],\n" +
 | 
			
		||||
            "    \"attributeLwm2m\": {}\n" +
 | 
			
		||||
            "  },\n" +
 | 
			
		||||
            "  \"bootstrap\": {\n" +
 | 
			
		||||
            "    \"servers\": {\n" +
 | 
			
		||||
            "      \"binding\": \"UQ\",\n" +
 | 
			
		||||
            "      \"shortId\": 123,\n" +
 | 
			
		||||
            "      \"lifetime\": 300,\n" +
 | 
			
		||||
            "      \"notifIfDisabled\": true,\n" +
 | 
			
		||||
            "      \"defaultMinPeriod\": 1\n" +
 | 
			
		||||
            "    },\n" +
 | 
			
		||||
            "    \"lwm2mServer\": {\n" +
 | 
			
		||||
            "      \"host\": \"localhost\",\n" +
 | 
			
		||||
            "      \"port\": 5686,\n" +
 | 
			
		||||
            "      \"serverId\": 123,\n" +
 | 
			
		||||
            "      \"serverPublicKey\": \"\",\n" +
 | 
			
		||||
            "      \"bootstrapServerIs\": false,\n" +
 | 
			
		||||
            "      \"clientHoldOffTime\": 1,\n" +
 | 
			
		||||
            "      \"bootstrapServerAccountTimeout\": 0\n" +
 | 
			
		||||
            "    },\n" +
 | 
			
		||||
            "    \"bootstrapServer\": {\n" +
 | 
			
		||||
            "      \"host\": \"localhost\",\n" +
 | 
			
		||||
            "      \"port\": 5687,\n" +
 | 
			
		||||
            "      \"serverId\": 111,\n" +
 | 
			
		||||
            "      \"securityMode\": \"NO_SEC\",\n" +
 | 
			
		||||
            "      \"serverPublicKey\": \"\",\n" +
 | 
			
		||||
            "      \"bootstrapServerIs\": true,\n" +
 | 
			
		||||
            "      \"clientHoldOffTime\": 1,\n" +
 | 
			
		||||
            "      \"bootstrapServerAccountTimeout\": 0\n" +
 | 
			
		||||
            "    }\n" +
 | 
			
		||||
            "  },\n" +
 | 
			
		||||
            "  \"clientLwM2mSettings\": {\n" +
 | 
			
		||||
            "    \"clientOnlyObserveAfterConnect\": 1\n" +
 | 
			
		||||
            "  }\n" +
 | 
			
		||||
            "}";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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(String credentialsId, X509ClientCredentialsConfig credentialsConfig) 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);
 | 
			
		||||
 | 
			
		||||
        deviceCredentials.setCredentialsId(credentialsId);
 | 
			
		||||
 | 
			
		||||
        LwM2MCredentials X509Credentials = new LwM2MCredentials();
 | 
			
		||||
 | 
			
		||||
        X509Credentials.setClient(credentialsConfig);
 | 
			
		||||
 | 
			
		||||
        deviceCredentials.setCredentialsValue(JacksonUtil.toString(X509Credentials));
 | 
			
		||||
        doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
 | 
			
		||||
        return device;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testConnectAndObserveTelemetry() throws Exception {
 | 
			
		||||
        createDeviceProfile(TRANSPORT_CONFIGURATION);
 | 
			
		||||
 | 
			
		||||
        Device device = createDevice(endpoint, new X509ClientCredentialsConfig(null, null));
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testConnectWithCertAndObserveTelemetry() throws Exception {
 | 
			
		||||
        createDeviceProfile(TRANSPORT_CONFIGURATION);
 | 
			
		||||
        Device device = createDevice(null, new X509ClientCredentialsConfig(SslUtil.getCertificateString(clientX509CertNotTrusted), endpoint));
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -32,6 +32,7 @@ import org.eclipse.californium.scandium.dtls.SessionAdapter;
 | 
			
		||||
import org.eclipse.leshan.client.californium.LeshanClient;
 | 
			
		||||
import org.eclipse.leshan.client.californium.LeshanClientBuilder;
 | 
			
		||||
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
 | 
			
		||||
import org.eclipse.leshan.client.object.Security;
 | 
			
		||||
import org.eclipse.leshan.client.object.Server;
 | 
			
		||||
import org.eclipse.leshan.client.observer.LwM2mClientObserver;
 | 
			
		||||
import org.eclipse.leshan.client.resource.ObjectsInitializer;
 | 
			
		||||
@ -54,7 +55,6 @@ import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.ScheduledExecutorService;
 | 
			
		||||
 | 
			
		||||
import static org.eclipse.leshan.client.object.Security.noSec;
 | 
			
		||||
import static org.eclipse.leshan.core.LwM2mId.DEVICE;
 | 
			
		||||
import static org.eclipse.leshan.core.LwM2mId.SECURITY;
 | 
			
		||||
import static org.eclipse.leshan.core.LwM2mId.SERVER;
 | 
			
		||||
@ -67,7 +67,7 @@ public class LwM2MTestClient {
 | 
			
		||||
    private final String endpoint;
 | 
			
		||||
    private LeshanClient client;
 | 
			
		||||
 | 
			
		||||
    public void init() {
 | 
			
		||||
    public void init(Security security, NetworkConfig coapConfig) {
 | 
			
		||||
        String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
 | 
			
		||||
        List<ObjectModel> models = new ArrayList<>();
 | 
			
		||||
        for (String resourceName : resources) {
 | 
			
		||||
@ -75,13 +75,10 @@ public class LwM2MTestClient {
 | 
			
		||||
        }
 | 
			
		||||
        LwM2mModel model = new StaticModel(models);
 | 
			
		||||
        ObjectsInitializer initializer = new ObjectsInitializer(model);
 | 
			
		||||
        initializer.setInstancesForObject(SECURITY, noSec("coap://localhost:5685", 123));
 | 
			
		||||
        initializer.setInstancesForObject(SECURITY, security);
 | 
			
		||||
        initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false));
 | 
			
		||||
        initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice());
 | 
			
		||||
 | 
			
		||||
        NetworkConfig coapConfig = new NetworkConfig();
 | 
			
		||||
        coapConfig.setString("COAP_PORT", Integer.toString(5685));
 | 
			
		||||
 | 
			
		||||
        DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
 | 
			
		||||
        dtlsConfig.setRecommendedCipherSuitesOnly(true);
 | 
			
		||||
 | 
			
		||||
@ -256,7 +253,7 @@ public class LwM2MTestClient {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
        client.stop(false);
 | 
			
		||||
        client.destroy(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,2 @@
 | 
			
		||||
transport.lwm2m.security.key_store=lwm2m/credentials/serverKeyStore.jks
 | 
			
		||||
transport.lwm2m.security.key_store_password=server
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@ -43,7 +43,7 @@ public class TbLwM2MAuthorizer implements Authorizer {
 | 
			
		||||
        if (senderIdentity.isX509()) {
 | 
			
		||||
            TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
 | 
			
		||||
            if (sessionInfo != null) {
 | 
			
		||||
                if (senderIdentity.getX509CommonName().equals(sessionInfo.getX509CommonName())) {
 | 
			
		||||
                if (sessionInfo.getX509CommonName().endsWith(senderIdentity.getX509CommonName())) {
 | 
			
		||||
                    clientContext.registerClient(registration, sessionInfo.getCredentials());
 | 
			
		||||
                    // X509 certificate is valid and matches endpoint.
 | 
			
		||||
                    return registration;
 | 
			
		||||
 | 
			
		||||
@ -15,14 +15,17 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.transport.lwm2m.secure.credentials;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.eclipse.leshan.core.SecurityMode;
 | 
			
		||||
 | 
			
		||||
import static org.eclipse.leshan.core.SecurityMode.X509;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class X509ClientCredentialsConfig implements LwM2MClientCredentialsConfig {
 | 
			
		||||
    private boolean allowTrustedOnly;
 | 
			
		||||
    private String cert;
 | 
			
		||||
    private String endpoint;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -63,6 +63,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
 | 
			
		||||
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
@ -129,12 +130,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
 | 
			
		||||
    private final LwM2MJsonAdaptor adaptor;
 | 
			
		||||
    private final LwM2mClientContext clientContext;
 | 
			
		||||
    private final LwM2mTransportRequest lwM2mTransportRequest;
 | 
			
		||||
    private final TbLwM2MDtlsSessionStore sessionStore;
 | 
			
		||||
 | 
			
		||||
    public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
 | 
			
		||||
                                           LwM2mClientContext clientContext,
 | 
			
		||||
                                           @Lazy LwM2mTransportRequest lwM2mTransportRequest,
 | 
			
		||||
                                           FirmwareDataCache firmwareDataCache,
 | 
			
		||||
                                           LwM2mTransportContext context, LwM2MJsonAdaptor adaptor) {
 | 
			
		||||
                                           LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) {
 | 
			
		||||
        this.transportService = transportService;
 | 
			
		||||
        this.config = config;
 | 
			
		||||
        this.helper = helper;
 | 
			
		||||
@ -143,6 +145,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
 | 
			
		||||
        this.firmwareDataCache = firmwareDataCache;
 | 
			
		||||
        this.context = context;
 | 
			
		||||
        this.adaptor = adaptor;
 | 
			
		||||
        this.sessionStore = sessionStore;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
@ -243,6 +246,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
 | 
			
		||||
        SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
 | 
			
		||||
        if (sessionInfo != null) {
 | 
			
		||||
            transportService.deregisterSession(sessionInfo);
 | 
			
		||||
            sessionStore.remove(registration.getEndpoint());
 | 
			
		||||
            this.doCloseSession(sessionInfo);
 | 
			
		||||
            clientContext.removeClientByRegistrationId(registration.getId());
 | 
			
		||||
            log.info("Client close session: [{}] unReg [{}] name  [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
 | 
			
		||||
 | 
			
		||||
@ -141,6 +141,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
 | 
			
		||||
        LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
 | 
			
		||||
        lwM2mClientsByEndpoint.put(registration.getEndpoint(), client);
 | 
			
		||||
        lwM2mClientsByRegistrationId.put(registration.getId(), client);
 | 
			
		||||
        toClientProfile(credentials.getDeviceProfile());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -36,4 +36,9 @@ public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore
 | 
			
		||||
    public TbX509DtlsSessionInfo get(String endpoint) {
 | 
			
		||||
        return store.get(endpoint);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void remove(String endpoint) {
 | 
			
		||||
        store.remove(endpoint);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,9 @@ public interface TbLwM2MDtlsSessionStore {
 | 
			
		||||
 | 
			
		||||
    TbX509DtlsSessionInfo get(String endpoint);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void remove(String endpoint);
 | 
			
		||||
 | 
			
		||||
    //TODO: add way to delete the session by endpoint.
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@
 | 
			
		||||
package org.thingsboard.server.dao.device;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.hibernate.exception.ConstraintViolationException;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
@ -23,6 +25,7 @@ import org.springframework.cache.annotation.CacheEvict;
 | 
			
		||||
import org.springframework.cache.annotation.Cacheable;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
@ -33,7 +36,6 @@ import org.thingsboard.server.common.msg.EncryptionUtil;
 | 
			
		||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
 | 
			
		||||
import static org.thingsboard.server.dao.service.Validator.validateId;
 | 
			
		||||
@ -76,7 +78,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
 | 
			
		||||
        if(deviceCredentials.getCredentialsType() == null){
 | 
			
		||||
        if (deviceCredentials.getCredentialsType() == null) {
 | 
			
		||||
            throw new DataValidationException("Device credentials type should be specified");
 | 
			
		||||
        }
 | 
			
		||||
        switch (deviceCredentials.getCredentialsType()) {
 | 
			
		||||
@ -140,7 +142,18 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void formatSimpleLwm2mCredentials(DeviceCredentials deviceCredentials) {
 | 
			
		||||
 | 
			
		||||
        ObjectNode json = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), ObjectNode.class);
 | 
			
		||||
        JsonNode client = json.get("client");
 | 
			
		||||
        if (client != null && client.get("securityConfigClientMode").asText().equals("X509") && client.has("cert")) {
 | 
			
		||||
            JsonNode certJson = client.get("cert");
 | 
			
		||||
            if (!certJson.isNull()) {
 | 
			
		||||
                String cert = EncryptionUtil.trimNewLines(certJson.asText());
 | 
			
		||||
                String sha3Hash = EncryptionUtil.getSha3Hash(cert);
 | 
			
		||||
                deviceCredentials.setCredentialsId(sha3Hash);
 | 
			
		||||
                ((ObjectNode) client).put("cert", cert);
 | 
			
		||||
                deviceCredentials.setCredentialsValue(JacksonUtil.toString(json));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user