Browse Source

lwm2m: Security key validate in dao

pull/5171/head
nickAS21 5 years ago
parent
commit
d491e0f664
  1. 8
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 1
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  3. 1
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  4. 2
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  5. 24
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java
  6. 44
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractLwM2MIntegrationTest.java
  7. 2
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  8. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java
  9. 6
      common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java
  10. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  11. 38
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java
  12. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java
  13. 1
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
  14. 1
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
  15. 4
      dao/pom.xml
  16. 170
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  17. 6
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  18. 2
      transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh
  19. 8
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

8
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -126,7 +126,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.EdgeLicenseService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.resource.TbResourceService;
@ -185,6 +185,9 @@ public abstract class BaseController {
@Autowired
protected DeviceProfileService deviceProfileService;
@Autowired
protected LwM2MServiceImpl lwM2MService;
@Autowired
protected AssetService assetService;
@ -263,9 +266,6 @@ public abstract class BaseController {
@Autowired
protected TbDeviceProfileCache deviceProfileCache;
@Autowired
protected LwM2MServerSecurityInfoRepository lwM2MServerSecurityInfoRepository;
@Autowired(required = false)
protected EdgeService edgeService;

1
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -291,7 +291,6 @@ public class DeviceController extends BaseController {
public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException {
checkNotNull(deviceCredentials);
try {
lwM2MServerSecurityInfoRepository.verifySecurityKeyDevice(deviceCredentials);
Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);

1
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -157,7 +157,6 @@ public class DeviceProfileController extends BaseController {
isSoftwareChanged = true;
}
}
lwM2MServerSecurityInfoRepository.verifySecurityKeyDeviceProfile(deviceProfile);
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);

2
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -46,7 +46,7 @@ public class Lwm2mController extends BaseController {
@ResponseBody
public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("isBootstrapServer") boolean bootstrapServer) throws ThingsboardException {
try {
return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(bootstrapServer);
return lwM2MService.getServerSecurityInfo(bootstrapServer);
} catch (Exception e) {
throw handleException(e);
}

24
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MService.java

@ -0,0 +1,24 @@
/**
* 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.service.lwm2m;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
public interface LwM2MService {
ServerSecurityConfig getServerSecurityInfo(boolean bootstrapServer);
}

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

@ -57,6 +57,9 @@ 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.bootstrap.secure.LwM2MBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapServers;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MServerBootstrap;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
@ -83,9 +86,18 @@ import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST_BS;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT_BS;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT_BS;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS;
@DaoSqlTest
public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
@ -312,7 +324,13 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
LwM2MCredentials credentials = new LwM2MCredentials();
credentials.setClient(clientCredentials);
// TODO
/**
* Without: credentials.setBootstrap(createBootstrapConfig());
* doPost("/api/device/credentials", deviceCredentials).andExpect(status().isBadRequest())
* .andExpect(statusReason(containsString("Device credentials are missing fields or mandatory value in these fields: bootstrap, bootstrapServer, lwm2mServer")));
*/
credentials.setBootstrap(createBootstrapConfig());
deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
return device;
@ -351,6 +369,30 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256");
}
protected LwM2MBootstrapConfig createBootstrapConfig() {
LwM2MBootstrapConfig bootstrap = new LwM2MBootstrapConfig();
LwM2MBootstrapServers servers = new LwM2MBootstrapServers();
servers.setShortId(SHORT_SERVER_ID);
bootstrap.setServers(servers);
LwM2MServerBootstrap server = new LwM2MServerBootstrap();
server.setHost(HOST);
server.setPort(PORT);
server.setSecurityHost(HOST);
server.setSecurityPort(SECURE_PORT);
server.setServerId(servers.getShortId());
server.setBootstrapServerIs(false);
bootstrap.setLwm2mServer(server);
LwM2MServerBootstrap serverBS = new LwM2MServerBootstrap();
serverBS.setHost(HOST_BS);
serverBS.setPort(PORT_BS);
serverBS.setSecurityHost(HOST_BS);
serverBS.setSecurityPort(SECURE_PORT_BS);
serverBS.setServerId(SHORT_SERVER_ID_BS);
serverBS.setBootstrapServerIs(true);
bootstrap.setBootstrapServer(serverBS);
return bootstrap;
}
protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
postRequest.file(content);

2
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java

@ -33,7 +33,7 @@ public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest {
@Test
public void testConnectWithPSKAndObserveTelemetry() throws Exception {
String pskIdentity = "SOME_PSK_ID";
String pskKey = "73656372657450534b";
String pskKey = "0123456789ABCDEF0123456789ABCDEF";
PSKClientCredentials clientCredentials = new PSKClientCredentials();
clientCredentials.setEndpoint(ENDPOINT);
clientCredentials.setKey(pskKey);

4
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.data.security.DeviceCredentials;
public interface DeviceCredentialsService {
@ -30,4 +32,6 @@ public interface DeviceCredentialsService {
DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials);
void verifyLwm2mSecurityKeyDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) ;
}

6
common/data/src/main/java/org/thingsboard/server/common/data/security/DeviceCredentials.java

@ -89,10 +89,4 @@ public class DeviceCredentials extends BaseData<DeviceCredentialsId> implements
+ credentialsId + ", credentialsValue=" + credentialsValue + ", createdTime=" + createdTime + ", id="
+ id + "]";
}
public JsonNode getNodeCredentialsValue () throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(this.credentialsValue);
}
}

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -40,7 +40,7 @@ public class LwM2mServerListener {
public final RegistrationListener registrationListener = new RegistrationListener() {
/**
* Register – запрос, представленный в виде POST /rd?…
* Register – query represented as POST /rd?…
*/
@Override
public void registered(Registration registration, Registration previousReg,
@ -49,7 +49,7 @@ public class LwM2mServerListener {
}
/**
* Update – представляет из себя CoAP POST запрос на URL, полученный в ответ на Register.
* Update – query represented as CoAP POST request for the URL received in response to Register.
*/
@Override
public void updated(RegistrationUpdate update, Registration updatedRegistration,
@ -58,7 +58,7 @@ public class LwM2mServerListener {
}
/**
* De-register (CoAP DELETE) – отправляется клиентом в случае инициирования процедуры выключения.
* De-register (CoAP DELETE) – Sent by the client when a shutdown procedure is initiated.
*/
@Override
public void unregistered(Registration registration, Collection<Observation> observations, boolean expired,

38
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java

@ -345,13 +345,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
@Override
public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback) {
try {
LwM2mPath resultIds = new LwM2mPath(request.getObjectId());
if (resultIds.isObjectInstance() || resultIds.isResource()) {
validateVersionedId(client, request);
ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
if (resourceModelWrite != null) {
WriteRequest downlink = null;
WriteRequest downlink = null;
ContentFormat contentFormat = getWriteRequestContentFormat(client, request, this.config.getModelProvider());
if (resultIds.isObjectInstance()) {
/*
@ -365,15 +362,21 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} else {
callback.onValidationError(toString(request), "No resources to update!");
}
} else if (resultIds.isResource()) {
if (resourceModelWrite.multiple) {
if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
value, resourceModelWrite.type);
} else {
callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
}
else if (resultIds.isResource()) {
ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
if (resourceModelWrite != null) {
if (resourceModelWrite.multiple) {
if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
value, resourceModelWrite.type);
} else {
callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
}
}
} else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!");
}
}
if (downlink != null) {
@ -381,21 +384,10 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
} else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Multi-Instance Resource !");
}
}
else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!");
}
}
else {
callback.onValidationError(toString(request), "Resource " + request.getVersionedId() + ". This operation can only be used for ObjectInstance or Resource (multiple)");
}
} catch (Exception e) {
callback.onValidationError(toString(request), e.getMessage());
}

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java

@ -418,8 +418,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
* #1 clientOnlyObserveAfterConnect == true
* - Only Observe Request to the client marked as observe from the profile configuration.
* #2. clientOnlyObserveAfterConnect == false
* После регистрации отправляю запрос на read всех ресурсов, которые после регистрации есть у клиента,
* а затем запрос на observe (edited)
* - Read Request to the client after registration to read all resource values for all objects
* - then Observe Request to the client marked as observe from the profile configuration.
*

1
common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh

@ -321,7 +321,6 @@ cert_end_point() {
}
if [ "$client_start" -lt "$client_finish" ]; then
echo "Файл содержит, как минимум, одно слово Bash."
echo
echo "==Start Client=="
while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do

1
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java

@ -899,7 +899,6 @@ public class DefaultTransportService implements TransportService {
String resourceId = msg.getResourceKey();
transportResourceCache.update(tenantId, resourceType, resourceId);
sessions.forEach((id, mdRez) -> {
log.warn("ResourceUpdate - [{}] [{}]", id, mdRez);
transportCallbackExecutor.submit(() -> mdRez.getListener().onResourceUpdate(Optional.ofNullable(msg)));
});

4
dao/pom.xml

@ -227,6 +227,10 @@
<groupId>org.elasticsearch.client</groupId>
<artifactId>rest</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-cf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

170
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -15,9 +15,14 @@
*/
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.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
@ -30,6 +35,7 @@ import org.thingsboard.server.common.data.device.credentials.BasicMqttCredential
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -39,10 +45,20 @@ import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
public class DeviceCredentialsServiceImpl extends AbstractEntityService implements DeviceCredentialsService {
@ -92,6 +108,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
break;
case LWM2M_CREDENTIALS:
formatSimpleLwm2mCredentials(deviceCredentials);
verifySecurityKeyDevice(deviceCredentials);
break;
}
log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
@ -237,4 +254,157 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
}
};
private void verifySecurityKeyDevice(DeviceCredentials deviceCredentials) {
try {
// LwM2MCredentialsValid credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), LwM2MCredentialsValid.class);
JsonNode nodeCredentialsValue = JacksonUtil.toJsonNode(deviceCredentials.getCredentialsValue());
String [] fields = {"client", "bootstrap", "bootstrap:bootstrapServer", "bootstrap:lwm2mServer"};
String validateMsg = validateNodeCredentials (nodeCredentialsValue, fields);
if (validateMsg.isEmpty()) {
checkClientKey(nodeCredentialsValue.get("client"));
checkServerKey(nodeCredentialsValue.get("bootstrap").get("bootstrapServer"), "Client`s by bootstrapServer");
checkServerKey(nodeCredentialsValue.get("bootstrap").get("lwm2mServer"), "Client`s by lwm2mServer");
}
else {
throw new DataValidationException(validateMsg);
}
} catch (DataValidationException | DecoderException e) {
throw new DataValidationException(e.getMessage());
}
}
public void verifyLwm2mSecurityKeyDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) {
try {
Map serverBs = transportConfiguration.getBootstrap().getBootstrapServer();
checkDeviceProfileServer (serverBs, "Servers: BootstrapServer`s");
Map serverLwm2m = transportConfiguration.getBootstrap().getLwm2mServer();
checkDeviceProfileServer (serverLwm2m, "Servers: Lwm2mServer`s");
} catch (DataValidationException e) {
throw new DataValidationException(e.getMessage());
}
}
private String validateNodeCredentials (JsonNode nodeCredentialsValue, String [] fields) {
Set msgSet = ConcurrentHashMap.newKeySet();
String msg = "";
for (String field : fields) {
if (field.contains(":")) {
String [] keys = field.split(":");
if (!nodeCredentialsValue.hasNonNull(keys[0])) {
msgSet.add(keys[1]);
}
else {
if (!nodeCredentialsValue.get(keys[0]).hasNonNull(keys[1])) msgSet.add(keys[1]);
}
}
else {
if (!nodeCredentialsValue.hasNonNull(field)) msgSet.add(field);
}
}
if (msgSet.size() > 0) msg = "Device credentials are missing fields or mandatory value in this fields: " + String.join(", ", msgSet);
return msg;
}
private void checkClientKey (JsonNode node) throws DataValidationException, DecoderException {
String modeName = node.get("securityConfigClientMode").asText();
// checks security config
// HexDec Len = 32,64,128
if (SecurityMode.PSK.name().equals(modeName)) {
String key = node.get("key").textValue();
assertIf(key == null || key.isEmpty(),
"pre-shared-key mode, Client`s private key or id must be not null or empty.");
assertIf(!key.matches("-?[0-9a-fA-F]+"),
"pre-shared-key mode, Client`s private key or id must be HexDecimal format.");
assertIf(key.length()%32 != 0 ,
"pre-shared-key mode, Client`s private key or id must be a multiple of 32.");
assertIf(key.length() > 128,
"pre-shared-key mode, Client`s private key or id must be not more than 128.");
String identity = node.get("identity").textValue();
assertIf(identity == null || identity.isEmpty(),
"pre-shared-key mode, Client`s identity key must be not null or empty.");
} else if (SecurityMode.RPK.name().equals(modeName)) {
String value = node.get("key").textValue();
assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(((String) value).toCharArray())) == null,
"raw-public-key mode, Client`s public key or id must be RFC7250 encoded public key");
} else if (SecurityMode.X509.name().equals(modeName)) {
String value = node.get("cert").textValue();
if (value != null && !value.isEmpty()) {
assertIf(decodeCertificate(Hex.decodeHex(((String) value).toCharArray())) == null,
"x509 mode, Client`s public key must be DER encoded X.509 certificate");
}
}
}
private void checkServerKey (JsonNode node, String serverType) throws DataValidationException {
String modeName = node.get("securityMode").asText();
// checks security config
if (SecurityMode.RPK.name().equals(modeName)) {
checkRPKServer(node, serverType);
} else if (SecurityMode.X509.name().equals(modeName)) {
checkX509Server(node, serverType);
}
}
protected void checkRPKServer(JsonNode node, String serverType) throws DataValidationException {
String value = node.get("clientSecretKey").textValue();
assertIf(decodeRfc5958PrivateKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"raw-public-key mode, " + serverType + " secret key must be RFC5958 encoded private key");
value = node.get("clientPublicKeyOrId").textValue();
assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key");
}
protected void checkX509Server(JsonNode node, String serverType) throws DataValidationException {
String value = node.get("clientSecretKey").textValue();
assertIf(decodeRfc5958PrivateKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"x509 mode " + serverType + " secret key must be RFC5958 encoded private key");
value = node.get("clientPublicKeyOrId").textValue();
assertIf(decodeCertificate(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"x509 mode " + serverType + " public key must be DER encoded X.509 certificate");
}
protected void checkDeviceProfileServer (Map server, String serverType) throws DataValidationException {
// checks security config
String value = (String) server.get("serverPublicKey");
if (SecurityMode.RPK.name().equals(server.get("securityMode"))) {
assertIf(decodeRfc7250PublicKey(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"raw-public-key mode, " + serverType + " public key or id must be RFC7250 encoded public key");
} else if (SecurityMode.X509.name().equals(server.get("securityMode"))) {
assertIf(decodeCertificate(org.eclipse.leshan.core.util.Hex.decodeHex(value.toCharArray())) == null,
"x509 mode, " + serverType + " public key must be DER encoded X.509 certificate");
}
}
protected PrivateKey decodeRfc5958PrivateKey(byte[] encodedKey) {
try {
return SecurityUtil.privateKey.decode(encodedKey);
} catch (IOException | GeneralSecurityException e) {
return null;
}
}
protected PublicKey decodeRfc7250PublicKey(byte[] encodedKey) {
try {
return SecurityUtil.publicKey.decode(encodedKey);
} catch (IOException | GeneralSecurityException e) {
return null;
}
}
protected Certificate decodeCertificate(byte[] encodedCert) {
try {
return SecurityUtil.certificate.decode(encodedCert);
} catch (IOException | GeneralSecurityException e) {
return null;
}
}
protected static void assertIf(boolean condition, String message) throws DataValidationException {
if (condition) {
throw new DataValidationException(message);
}
}
}

6
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -54,6 +54,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
@ -132,6 +133,9 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
@Autowired
private DashboardService dashboardService;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
private final Lock findOrCreateLock = new ReentrantLock();
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")
@ -409,6 +413,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration);
}
}
} else if (transportConfiguration instanceof Lwm2mDeviceProfileTransportConfiguration) {
deviceCredentialsService.verifyLwm2mSecurityKeyDeviceProfile((Lwm2mDeviceProfileTransportConfiguration) transportConfiguration);
}
List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms();

2
transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh

@ -321,7 +321,7 @@ cert_end_point() {
}
if [ "$client_start" -lt "$client_finish" ]; then
echo "Файл содержит, как минимум, одно слово Bash."
echo
echo "==Start Client=="
while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do

8
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

@ -185,13 +185,13 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor,
* If the Object field “Mandatory” is “Mandatory” and the Object field “Instances” is “Single” then -
* the number of Object Instance MUST be 1.
* 1. <MultipleInstances> == Multiple (true), <Mandatory> == Optional (false) -
* Object Instance ID MIN_ID=0 MAX_ID=65535 (может ни одного не быть)
* Object Instance ID MIN_ID=0 MAX_ID=65535 (there may be none)
* 2. <MultipleInstances> == Multiple (true), <Mandatory> == Mandatory (true) -
* Object Instance ID MIN_ID=0 MAX_ID=65535 (min один обязательный)
* Object Instance ID MIN_ID=0 MAX_ID=65535 (min one mandatory)
* 3. <MultipleInstances> == Single (false), <Mandatory> == Optional (false) -
* Object Instance ID cnt_max = 1 cnt_min = 0 (может ни одного не быть)
* Object Instance ID cnt_max = 1 cnt_min = 0 (there may be none )
* 4. <MultipleInstances> == Single (false), <Mandatory> == Mandatory (true) -
* Object Instance ID cnt_max = cnt_min = 1 (всегда есть один)
* Object Instance ID cnt_max = cnt_min = 1 (there must always be one)
*/
addInstances = ($event: Event, control: AbstractControl): void => {

Loading…
Cancel
Save