diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 6ac41979d6..ae22aaca51 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -583,7 +583,24 @@ transport:
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
timeout: "${COAP_TIMEOUT:10000}"
-
+ dtls:
+ # Enable/disable DTLS 1.2 support
+ enabled: "${COAP_DTLS_ENABLED:false}"
+ # Secure mode. Allowed values: NO_AUTH, X509
+ mode: "${COAP_DTLS_SECURE_MODE:NO_AUTH}"
+ # Path to the key store that holds the certificate
+ key_store: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
+ # Password used to access the key store
+ key_store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
+ # Password used to access the key
+ key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
+ # Key alias
+ key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
+ # Skip certificate validity check for client certificates.
+ skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
+ x509:
+ dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
+ dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
swagger:
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"
security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}"
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
index b350e5661a..bd2f027e3e 100644
--- a/common/transport/coap/pom.xml
+++ b/common/transport/coap/pom.xml
@@ -44,6 +44,10 @@
org.eclipse.californium
californium-core
+
+ org.eclipse.californium
+ scandium
+
org.springframework
spring-context-support
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
index bf6c7c994d..83fc56bdc6 100644
--- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
@@ -46,6 +46,10 @@ public class CoapTransportContext extends TransportContext {
@Value("${transport.coap.timeout}")
private Long timeout;
+ @Getter
+ @Autowired(required = false)
+ private TbCoapDtlsSettings dtlsSettings;
+
@Getter
@Autowired
private JsonCoapAdaptor jsonCoapAdaptor;
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
index c1fd5b6a4a..074be3ae47 100644
--- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
@@ -27,6 +27,7 @@ import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserver;
+import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
@@ -63,15 +64,22 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
private static final int FEATURE_TYPE_POSITION = 4;
private static final int REQUEST_ID_POSITION = 5;
+ private static final int FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST = 3;
+ private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4;
+ private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID";
+
private final ConcurrentMap tokenToSessionIdMap = new ConcurrentHashMap<>();
private final ConcurrentMap tokenToNotificationCounterMap = new ConcurrentHashMap<>();
private final Set rpcSubscriptions = ConcurrentHashMap.newKeySet();
private final Set attributeSubscriptions = ConcurrentHashMap.newKeySet();
- public CoapTransportResource(CoapTransportContext coapTransportContext, String name) {
+ private ConcurrentMap dtlsSessionIdMap;
+
+ public CoapTransportResource(CoapTransportContext coapTransportContext, ConcurrentMap dtlsSessionIdMap, String name) {
super(coapTransportContext, name);
this.setObservable(true); // enable observing
this.addObserver(new CoapResourceObserver());
+ this.dtlsSessionIdMap = dtlsSessionIdMap;
// this.setObservable(false); // disable observing
// this.setObserveType(CoAP.Type.CON); // configure the notification type to CONs
// this.getAttributes().setObservable(); // mark observable in the Link-Format
@@ -187,107 +195,132 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
Exchange advanced = exchange.advanced();
Request request = advanced.getRequest();
+ String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY);
+ if (!StringUtils.isEmpty(dtlsSessionIdStr)) {
+ if (dtlsSessionIdMap != null) {
+ TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap
+ .computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> {
+ dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis());
+ return dtlsSessionInfo;
+ });
+ if (tbCoapDtlsSessionInfo != null) {
+ processRequest(exchange, type, request, tbCoapDtlsSessionInfo.getSessionInfoProto(), tbCoapDtlsSessionInfo.getDeviceProfile());
+ } else {
+ exchange.respond(CoAP.ResponseCode.UNAUTHORIZED);
+ }
+ } else {
+ processAccessTokenRequest(exchange, type, request);
+ }
+ } else {
+ processAccessTokenRequest(exchange, type, request);
+ }
+ }
+
+ private void processAccessTokenRequest(CoapExchange exchange, SessionMsgType type, Request request) {
Optional credentials = decodeCredentials(request);
if (credentials.isEmpty()) {
- exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
+ exchange.respond(CoAP.ResponseCode.UNAUTHORIZED);
return;
}
-
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> {
- UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
- try {
- TransportConfigurationContainer transportConfigurationContainer = getTransportConfigurationContainer(deviceProfile);
- CoapTransportAdaptor coapTransportAdaptor = getCoapTransportAdaptor(transportConfigurationContainer.isJsonPayload());
- switch (type) {
- case POST_ATTRIBUTES_REQUEST:
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToPostAttributes(sessionId, request,
- transportConfigurationContainer.getAttributesMsgDescriptor()),
- new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
- break;
- case POST_TELEMETRY_REQUEST:
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToPostTelemetry(sessionId, request,
- transportConfigurationContainer.getTelemetryMsgDescriptor()),
- new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
- break;
- case CLAIM_REQUEST:
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToClaimDevice(sessionId, request, sessionInfo),
- new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- break;
- case SUBSCRIBE_ATTRIBUTES_REQUEST:
- TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
- if (currentAttrSession == null) {
- attributeSubscriptions.add(sessionId);
- registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request));
- transportService.process(sessionInfo,
- TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));
- }
- break;
- case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
- TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
- if (attrSession != null) {
- UUID attrSessionId = new UUID(attrSession.getSessionIdMSB(), attrSession.getSessionIdLSB());
- attributeSubscriptions.remove(attrSessionId);
- transportService.process(attrSession,
- TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
- new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- closeAndDeregister(sessionInfo, sessionId);
- }
- break;
- case SUBSCRIBE_RPC_COMMANDS_REQUEST:
- TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
- if (currentRpcSession == null) {
- rpcSubscriptions.add(sessionId);
- registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request));
- transportService.process(sessionInfo,
- TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
- new CoapNoOpCallback(exchange));
- } else {
- UUID rpcSessionId = new UUID(currentRpcSession.getSessionIdMSB(), currentRpcSession.getSessionIdLSB());
- reportActivity(currentRpcSession, attributeSubscriptions.contains(rpcSessionId), rpcSubscriptions.contains(rpcSessionId));
- }
- break;
- case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
- TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
- if (rpcSession != null) {
- UUID rpcSessionId = new UUID(rpcSession.getSessionIdMSB(), rpcSession.getSessionIdLSB());
- rpcSubscriptions.remove(rpcSessionId);
- transportService.process(rpcSession,
- TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
- new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- closeAndDeregister(sessionInfo, sessionId);
- }
- break;
- case TO_DEVICE_RPC_RESPONSE:
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request),
- new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
- break;
- case TO_SERVER_RPC_REQUEST:
- transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout());
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToServerRpcRequest(sessionId, request),
- new CoapNoOpCallback(exchange));
- break;
- case GET_ATTRIBUTES_REQUEST:
- transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout());
- transportService.process(sessionInfo,
- coapTransportAdaptor.convertToGetAttributes(sessionId, request),
- new CoapNoOpCallback(exchange));
- break;
- }
- } catch (AdaptorException e) {
- log.trace("[{}] Failed to decode message: ", sessionId, e);
- exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
- }
+ processRequest(exchange, type, request, sessionInfo, deviceProfile);
}));
}
+ private void processRequest(CoapExchange exchange, SessionMsgType type, Request request, TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
+ UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
+ try {
+ TransportConfigurationContainer transportConfigurationContainer = getTransportConfigurationContainer(deviceProfile);
+ CoapTransportAdaptor coapTransportAdaptor = getCoapTransportAdaptor(transportConfigurationContainer.isJsonPayload());
+ switch (type) {
+ case POST_ATTRIBUTES_REQUEST:
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToPostAttributes(sessionId, request,
+ transportConfigurationContainer.getAttributesMsgDescriptor()),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
+ break;
+ case POST_TELEMETRY_REQUEST:
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToPostTelemetry(sessionId, request,
+ transportConfigurationContainer.getTelemetryMsgDescriptor()),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
+ break;
+ case CLAIM_REQUEST:
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToClaimDevice(sessionId, request, sessionInfo),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ break;
+ case SUBSCRIBE_ATTRIBUTES_REQUEST:
+ TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
+ if (currentAttrSession == null) {
+ attributeSubscriptions.add(sessionId);
+ registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request));
+ transportService.process(sessionInfo,
+ TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));
+ }
+ break;
+ case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
+ TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
+ if (attrSession != null) {
+ UUID attrSessionId = new UUID(attrSession.getSessionIdMSB(), attrSession.getSessionIdLSB());
+ attributeSubscriptions.remove(attrSessionId);
+ transportService.process(attrSession,
+ TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ closeAndDeregister(sessionInfo, sessionId);
+ }
+ break;
+ case SUBSCRIBE_RPC_COMMANDS_REQUEST:
+ TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
+ if (currentRpcSession == null) {
+ rpcSubscriptions.add(sessionId);
+ registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request));
+ transportService.process(sessionInfo,
+ TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
+ new CoapNoOpCallback(exchange));
+ } else {
+ UUID rpcSessionId = new UUID(currentRpcSession.getSessionIdMSB(), currentRpcSession.getSessionIdLSB());
+ reportActivity(currentRpcSession, attributeSubscriptions.contains(rpcSessionId), rpcSubscriptions.contains(rpcSessionId));
+ }
+ break;
+ case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
+ TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
+ if (rpcSession != null) {
+ UUID rpcSessionId = new UUID(rpcSession.getSessionIdMSB(), rpcSession.getSessionIdLSB());
+ rpcSubscriptions.remove(rpcSessionId);
+ transportService.process(rpcSession,
+ TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ closeAndDeregister(sessionInfo, sessionId);
+ }
+ break;
+ case TO_DEVICE_RPC_RESPONSE:
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request),
+ new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
+ break;
+ case TO_SERVER_RPC_REQUEST:
+ transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout());
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToServerRpcRequest(sessionId, request),
+ new CoapNoOpCallback(exchange));
+ break;
+ case GET_ATTRIBUTES_REQUEST:
+ transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout());
+ transportService.process(sessionInfo,
+ coapTransportAdaptor.convertToGetAttributes(sessionId, request),
+ new CoapNoOpCallback(exchange));
+ break;
+ }
+ } catch (AdaptorException e) {
+ log.trace("[{}] Failed to decode message: ", sessionId, e);
+ exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
+ }
+ }
+
private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
tokenToNotificationCounterMap.remove(token);
return tokenToSessionIdMap.remove(token);
@@ -310,7 +343,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
private Optional decodeCredentials(Request request) {
List uriPath = request.getOptions().getUriPath();
- if (uriPath.size() >= ACCESS_TOKEN_POSITION) {
+ if (uriPath.size() > ACCESS_TOKEN_POSITION) {
return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)));
} else {
return Optional.empty();
@@ -322,8 +355,11 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
try {
if (uriPath.size() >= FEATURE_TYPE_POSITION) {
return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase()));
- } else if (uriPath.size() == 3 && uriPath.contains(DataConstants.PROVISION)) {
- return Optional.of(FeatureType.valueOf(DataConstants.PROVISION.toUpperCase()));
+ } else if (uriPath.size() >= FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST) {
+ if (uriPath.contains(DataConstants.PROVISION)) {
+ return Optional.of(FeatureType.valueOf(DataConstants.PROVISION.toUpperCase()));
+ }
+ return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST - 1).toUpperCase()));
}
} catch (RuntimeException e) {
log.warn("Failed to decode feature type: {}", uriPath);
@@ -336,6 +372,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
try {
if (uriPath.size() >= REQUEST_ID_POSITION) {
return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION - 1)));
+ } else {
+ return Optional.of(Integer.valueOf(uriPath.get(REQUEST_ID_POSITION_CERTIFICATE_REQUEST - 1)));
}
} catch (RuntimeException e) {
log.warn("Failed to decode feature type: {}", uriPath);
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
index 452f661a80..a28019c6c0 100644
--- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
@@ -19,7 +19,10 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.network.CoapEndpoint;
+import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.server.resources.Resource;
+import org.eclipse.californium.scandium.DTLSConnector;
+import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
@@ -30,6 +33,11 @@ import javax.annotation.PreDestroy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
+import java.util.Random;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
@Service("CoapTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')")
@@ -44,34 +52,53 @@ public class CoapTransportService {
@Autowired
private CoapTransportContext coapTransportContext;
+ private TbCoapDtlsCertificateVerifier tbDtlsCertificateVerifier;
+
private CoapServer server;
+ private ScheduledExecutorService dtlsSessionsExecutor;
+
@PostConstruct
public void init() throws UnknownHostException {
log.info("Starting CoAP transport...");
log.info("Starting CoAP transport server");
this.server = new CoapServer();
+
+ CoapEndpoint.Builder capEndpointBuilder = new CoapEndpoint.Builder();
+
+ if (isDtlsEnabled()) {
+ TbCoapDtlsSettings dtlsSettings = coapTransportContext.getDtlsSettings();
+ DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig();
+ DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig);
+ capEndpointBuilder.setConnector(connector);
+ if (dtlsConnectorConfig.isClientAuthenticationRequired()) {
+ tbDtlsCertificateVerifier = (TbCoapDtlsCertificateVerifier) dtlsConnectorConfig.getAdvancedCertificateVerifier();
+ dtlsSessionsExecutor = Executors.newSingleThreadScheduledExecutor();
+ dtlsSessionsExecutor.scheduleAtFixedRate(this::evictTimeoutSessions, new Random().nextInt((int) getDtlsSessionReportTimeout()), getDtlsSessionReportTimeout(), TimeUnit.MILLISECONDS);
+ }
+ } else {
+ InetAddress addr = InetAddress.getByName(coapTransportContext.getHost());
+ InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort());
+ capEndpointBuilder.setInetSocketAddress(sockAddr);
+ capEndpointBuilder.setNetworkConfig(NetworkConfig.getStandard());
+ }
+ CoapEndpoint coapEndpoint = capEndpointBuilder.build();
+
+ server.addEndpoint(coapEndpoint);
+
createResources();
Resource root = this.server.getRoot();
TbCoapServerMessageDeliverer messageDeliverer = new TbCoapServerMessageDeliverer(root);
this.server.setMessageDeliverer(messageDeliverer);
- InetAddress addr = InetAddress.getByName(coapTransportContext.getHost());
- InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort());
-
- CoapEndpoint.Builder coapEndpoitBuilder = new CoapEndpoint.Builder();
- coapEndpoitBuilder.setInetSocketAddress(sockAddr);
- CoapEndpoint coapEndpoint = coapEndpoitBuilder.build();
-
- server.addEndpoint(coapEndpoint);
server.start();
log.info("CoAP transport started!");
}
private void createResources() {
CoapResource api = new CoapResource(API);
- api.add(new CoapTransportResource(coapTransportContext, V1));
+ api.add(new CoapTransportResource(coapTransportContext, getDtlsSessionsMap(), V1));
CoapResource efento = new CoapResource(EFENTO);
CoapEfentoTransportResource efentoMeasurementsTransportResource = new CoapEfentoTransportResource(coapTransportContext, MEASUREMENTS);
@@ -81,8 +108,27 @@ public class CoapTransportService {
server.add(efento);
}
+ private boolean isDtlsEnabled() {
+ return coapTransportContext.getDtlsSettings() != null;
+ }
+
+ private ConcurrentMap getDtlsSessionsMap() {
+ return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionIdsMap() : null;
+ }
+
+ private void evictTimeoutSessions() {
+ tbDtlsCertificateVerifier.evictTimeoutSessions();
+ }
+
+ private long getDtlsSessionReportTimeout() {
+ return tbDtlsCertificateVerifier.getDtlsSessionReportTimeout();
+ }
+
@PreDestroy
public void shutdown() {
+ if (dtlsSessionsExecutor != null) {
+ dtlsSessionsExecutor.shutdownNow();
+ }
log.info("Stopping CoAP transport!");
this.server.destroy();
log.info("CoAP transport stopped!");
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsCertificateVerifier.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsCertificateVerifier.java
new file mode 100644
index 0000000000..a94ed6caeb
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsCertificateVerifier.java
@@ -0,0 +1,161 @@
+/**
+ * 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.coap;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.elements.util.CertPathUtil;
+import org.eclipse.californium.scandium.dtls.AlertMessage;
+import org.eclipse.californium.scandium.dtls.CertificateMessage;
+import org.eclipse.californium.scandium.dtls.CertificateType;
+import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
+import org.eclipse.californium.scandium.dtls.ConnectionId;
+import org.eclipse.californium.scandium.dtls.DTLSSession;
+import org.eclipse.californium.scandium.dtls.HandshakeException;
+import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
+import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
+import org.eclipse.californium.scandium.util.ServerNames;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.DeviceTransportType;
+import org.thingsboard.server.common.msg.EncryptionUtil;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
+import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
+import org.thingsboard.server.common.transport.util.SslUtil;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
+
+import javax.security.auth.x500.X500Principal;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Data
+public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
+
+ private final TbCoapDtlsSessionInMemoryStorage tbCoapDtlsSessionInMemoryStorage;
+
+ private TransportService transportService;
+ private TbServiceInfoProvider serviceInfoProvider;
+ private boolean skipValidityCheckForClientCert;
+
+ public TbCoapDtlsCertificateVerifier(TransportService transportService, TbServiceInfoProvider serviceInfoProvider, long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout, boolean skipValidityCheckForClientCert) {
+ this.transportService = transportService;
+ this.serviceInfoProvider = serviceInfoProvider;
+ this.skipValidityCheckForClientCert = skipValidityCheckForClientCert;
+ this.tbCoapDtlsSessionInMemoryStorage = new TbCoapDtlsSessionInMemoryStorage(dtlsSessionInactivityTimeout, dtlsSessionReportTimeout);
+ }
+
+ @Override
+ public List getSupportedCertificateType() {
+ return Collections.singletonList(CertificateType.X_509);
+ }
+
+ @Override
+ public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) {
+ try {
+ String credentialsBody = null;
+ CertPath certpath = message.getCertificateChain();
+ X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]);
+ for (X509Certificate cert : chain) {
+ try {
+ if (!skipValidityCheckForClientCert) {
+ cert.checkValidity();
+ }
+ String strCert = SslUtil.getCertificateString(cert);
+ String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
+ final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),
+ new TransportServiceCallback<>() {
+ @Override
+ public void onSuccess(ValidateDeviceCredentialsResponse msg) {
+ if (!StringUtils.isEmpty(msg.getCredentials())) {
+ deviceCredentialsResponse[0] = msg;
+ }
+ latch.countDown();
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.error(e.getMessage(), e);
+ latch.countDown();
+ }
+ });
+ latch.await(10, TimeUnit.SECONDS);
+ ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
+ if (msg != null && strCert.equals(msg.getCredentials())) {
+ credentialsBody = msg.getCredentials();
+ DeviceProfile deviceProfile = msg.getDeviceProfile();
+ if (msg.hasDeviceInfo() && deviceProfile != null) {
+ TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, serviceInfoProvider.getServiceId(), UUID.randomUUID());
+ tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(sessionInfoProto, deviceProfile));
+ }
+ break;
+ }
+ } catch (InterruptedException |
+ CertificateEncodingException |
+ CertificateExpiredException |
+ CertificateNotYetValidException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ if (credentialsBody == null) {
+ AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.BAD_CERTIFICATE,
+ session.getPeer());
+ throw new HandshakeException("Certificate chain could not be validated", alert);
+ } else {
+ return new CertificateVerificationResult(cid, certpath, null);
+ }
+ } catch (HandshakeException e) {
+ log.trace("Certificate validation failed!", e);
+ return new CertificateVerificationResult(cid, e, null);
+ }
+ }
+
+ @Override
+ public List getAcceptedIssuers() {
+ return CertPathUtil.toSubjects(null);
+ }
+
+ @Override
+ public void setResultHandler(HandshakeResultHandler resultHandler) {
+ // empty implementation
+ }
+
+ public ConcurrentMap getTbCoapDtlsSessionIdsMap() {
+ return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionIdMap();
+ }
+
+ public void evictTimeoutSessions() {
+ tbCoapDtlsSessionInMemoryStorage.evictTimeoutSessions();
+ }
+
+ public long getDtlsSessionReportTimeout() {
+ return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionReportTimeout();
+ }
+}
\ No newline at end of file
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInMemoryStorage.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInMemoryStorage.java
new file mode 100644
index 0000000000..d7dd9c1829
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInMemoryStorage.java
@@ -0,0 +1,55 @@
+/**
+ * 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.coap;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+@Slf4j
+@Data
+public class TbCoapDtlsSessionInMemoryStorage {
+
+ private final ConcurrentMap dtlsSessionIdMap = new ConcurrentHashMap<>();
+ private long dtlsSessionInactivityTimeout;
+ private long dtlsSessionReportTimeout;
+
+
+ public TbCoapDtlsSessionInMemoryStorage(long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout) {
+ this.dtlsSessionInactivityTimeout = dtlsSessionInactivityTimeout;
+ this.dtlsSessionReportTimeout = dtlsSessionReportTimeout;
+ }
+
+ public void put(String dtlsSessionId, TbCoapDtlsSessionInfo dtlsSessionInfo) {
+ log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", dtlsSessionId, dtlsSessionInfo.getLastActivityTime());
+ dtlsSessionIdMap.putIfAbsent(dtlsSessionId, dtlsSessionInfo);
+ }
+
+ public void evictTimeoutSessions() {
+ long expTime = System.currentTimeMillis() - dtlsSessionInactivityTimeout;
+ dtlsSessionIdMap.entrySet().removeIf(entry -> {
+ if (entry.getValue().getLastActivityTime() < expTime) {
+ log.trace("DTLS session was removed from in-memory store: [{}]", entry.getKey());
+ return true;
+ } else {
+ return false;
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInfo.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInfo.java
new file mode 100644
index 0000000000..452c5eb792
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSessionInfo.java
@@ -0,0 +1,35 @@
+/**
+ * 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.coap;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.gen.transport.TransportProtos;
+
+@Data
+public class TbCoapDtlsSessionInfo {
+
+ private TransportProtos.SessionInfoProto sessionInfoProto;
+ private DeviceProfile deviceProfile;
+ private long lastActivityTime;
+
+
+ public TbCoapDtlsSessionInfo(TransportProtos.SessionInfoProto sessionInfoProto, DeviceProfile deviceProfile) {
+ this.sessionInfoProto = sessionInfoProto;
+ this.deviceProfile = deviceProfile;
+ this.lastActivityTime = System.currentTimeMillis();
+ }
+}
\ No newline at end of file
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSettings.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSettings.java
new file mode 100644
index 0000000000..d7cd0b3f39
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapDtlsSettings.java
@@ -0,0 +1,162 @@
+/**
+ * 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.coap;
+
+import com.google.common.io.Resources;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.elements.util.SslContextUtil;
+import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
+import org.eclipse.californium.scandium.dtls.CertificateType;
+import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportService;
+import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Optional;
+
+@Slf4j
+@ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false)
+@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')")
+@Component
+public class TbCoapDtlsSettings {
+
+ @Value("${transport.coap.bind_address}")
+ private String host;
+
+ @Value("${transport.coap.bind_port}")
+ private Integer port;
+
+ @Value("${transport.coap.dtls.mode}")
+ private String mode;
+
+ @Value("${transport.coap.dtls.key_store}")
+ private String keyStoreFile;
+
+ @Value("${transport.coap.dtls.key_store_password}")
+ private String keyStorePassword;
+
+ @Value("${transport.coap.dtls.key_password}")
+ private String keyPassword;
+
+ @Value("${transport.coap.dtls.key_alias}")
+ private String keyAlias;
+
+ @Value("${transport.coap.dtls.skip_validity_check_for_client_cert}")
+ private boolean skipValidityCheckForClientCert;
+
+ @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}")
+ private long dtlsSessionInactivityTimeout;
+
+ @Value("${transport.coap.dtls.x509.dtls_session_report_timeout}")
+ private long dtlsSessionReportTimeout;
+
+ @Autowired
+ private TransportService transportService;
+
+ @Autowired
+ private TbServiceInfoProvider serviceInfoProvider;
+
+ public DtlsConnectorConfig dtlsConnectorConfig() throws UnknownHostException {
+ Optional securityModeOpt = SecurityMode.parse(mode);
+ if (securityModeOpt.isEmpty()) {
+ log.warn("Incorrect configuration of securityMode {}", mode);
+ throw new RuntimeException("Failed to parse mode property: " + mode + "!");
+ } else {
+ DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder();
+ configBuilder.setAddress(getInetSocketAddress());
+ String keyStoreFilePath = Resources.getResource(keyStoreFile).getPath();
+ SslContextUtil.Credentials serverCredentials = loadServerCredentials(keyStoreFilePath);
+ SecurityMode securityMode = securityModeOpt.get();
+ if (securityMode.equals(SecurityMode.NO_AUTH)) {
+ configBuilder.setClientAuthenticationRequired(false);
+ configBuilder.setServerOnly(true);
+ } else {
+ configBuilder.setAdvancedCertificateVerifier(
+ new TbCoapDtlsCertificateVerifier(
+ transportService,
+ serviceInfoProvider,
+ dtlsSessionInactivityTimeout,
+ dtlsSessionReportTimeout,
+ skipValidityCheckForClientCert
+ )
+ );
+ }
+ configBuilder.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(),
+ Collections.singletonList(CertificateType.X_509));
+ return configBuilder.build();
+ }
+ }
+
+ private SslContextUtil.Credentials loadServerCredentials(String keyStoreFilePath) {
+ try {
+ return SslContextUtil.loadCredentials(keyStoreFilePath, keyAlias, keyStorePassword.toCharArray(),
+ keyPassword.toCharArray());
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Failed to load serverCredentials due to: ", e);
+ }
+ }
+
+ private void loadTrustedCertificates(DtlsConnectorConfig.Builder config, String keyStoreFilePath) {
+ StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder();
+ try {
+ Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates(
+ keyStoreFilePath, keyAlias,
+ keyStorePassword.toCharArray());
+ trustBuilder.setTrustedCertificates(trustedCertificates);
+ if (trustBuilder.hasTrusts()) {
+ config.setAdvancedCertificateVerifier(trustBuilder.build());
+ }
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException("Failed to load trusted certificates due to: ", e);
+ }
+ }
+
+ private InetSocketAddress getInetSocketAddress() throws UnknownHostException {
+ InetAddress addr = InetAddress.getByName(host);
+ return new InetSocketAddress(addr, port);
+ }
+
+ private enum SecurityMode {
+ X509,
+ NO_AUTH;
+
+ static Optional parse(String name) {
+ SecurityMode mode = null;
+ if (name != null) {
+ for (SecurityMode securityMode : SecurityMode.values()) {
+ if (securityMode.name().equalsIgnoreCase(name)) {
+ mode = securityMode;
+ break;
+ }
+ }
+ }
+ return Optional.ofNullable(mode);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/NoSecClient.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/NoSecClient.java
new file mode 100644
index 0000000000..f9a31d0513
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/NoSecClient.java
@@ -0,0 +1,97 @@
+/**
+ * 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.coap.client;
+
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapResponse;
+import org.eclipse.californium.core.Utils;
+import org.eclipse.californium.elements.DtlsEndpointContext;
+import org.eclipse.californium.elements.EndpointContext;
+import org.eclipse.californium.elements.exception.ConnectorException;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class NoSecClient {
+
+ private ExecutorService executor = Executors.newFixedThreadPool(1);
+ private CoapClient coapClient;
+
+ public NoSecClient(String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException {
+ URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys));
+ this.coapClient = new CoapClient(uri);
+ }
+
+ public void test() {
+ executor.submit(() -> {
+ try {
+ while (!Thread.interrupted()) {
+ CoapResponse response = null;
+ try {
+ response = coapClient.get();
+ } catch (ConnectorException | IOException e) {
+ System.err.println("Error occurred while sending request: " + e);
+ System.exit(-1);
+ }
+ if (response != null) {
+
+ System.out.println(response.getCode() + " - " + response.getCode().name());
+ System.out.println(response.getOptions());
+ System.out.println(response.getResponseText());
+ System.out.println();
+ System.out.println("ADVANCED:");
+ EndpointContext context = response.advanced().getSourceContext();
+ Principal identity = context.getPeerIdentity();
+ if (identity != null) {
+ System.out.println(context.getPeerIdentity());
+ } else {
+ System.out.println("anonymous");
+ }
+ System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER));
+ System.out.println(Utils.prettyPrint(response));
+ } else {
+ System.out.println("No response received.");
+ }
+ Thread.sleep(5000);
+ }
+ } catch (Exception e) {
+ System.out.println("Error occurred while sending COAP requests.");
+ }
+ });
+ }
+
+ private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) {
+ return "coap://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys;
+ }
+
+ public static void main(String[] args) throws URISyntaxException {
+ System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.NoSecClient " +
+ "host port accessToken clientKeys sharedKeys");
+
+ String host = args[0];
+ int port = Integer.parseInt(args[1]);
+ String accessToken = args[2];
+ String clientKeys = args[3];
+ String sharedKeys = args[4];
+
+ NoSecClient client = new NoSecClient(host, port, accessToken, clientKeys, sharedKeys);
+ client.test();
+ }
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientNoAuth.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientNoAuth.java
new file mode 100644
index 0000000000..7bbb1f55cf
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientNoAuth.java
@@ -0,0 +1,145 @@
+/**
+ * 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.coap.client;
+
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapResponse;
+import org.eclipse.californium.core.Utils;
+import org.eclipse.californium.core.network.CoapEndpoint;
+import org.eclipse.californium.elements.DtlsEndpointContext;
+import org.eclipse.californium.elements.EndpointContext;
+import org.eclipse.californium.elements.exception.ConnectorException;
+import org.eclipse.californium.elements.util.SslContextUtil;
+import org.eclipse.californium.scandium.DTLSConnector;
+import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
+import org.eclipse.californium.scandium.dtls.CertificateType;
+import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class SecureClientNoAuth {
+
+ private final DTLSConnector dtlsConnector;
+ private ExecutorService executor = Executors.newFixedThreadPool(1);
+ private CoapClient coapClient;
+
+ public SecureClientNoAuth(DTLSConnector dtlsConnector, String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException {
+ this.dtlsConnector = dtlsConnector;
+ this.coapClient = getCoapClient(host, port, accessToken, clientKeys, sharedKeys);
+ }
+
+ public void test() {
+ executor.submit(() -> {
+ try {
+ while (!Thread.interrupted()) {
+ CoapResponse response = null;
+ try {
+ response = coapClient.get();
+ } catch (ConnectorException | IOException e) {
+ System.err.println("Error occurred while sending request: " + e);
+ System.exit(-1);
+ }
+ if (response != null) {
+
+ System.out.println(response.getCode() + " - " + response.getCode().name());
+ System.out.println(response.getOptions());
+ System.out.println(response.getResponseText());
+ System.out.println();
+ System.out.println("ADVANCED:");
+ EndpointContext context = response.advanced().getSourceContext();
+ Principal identity = context.getPeerIdentity();
+ if (identity != null) {
+ System.out.println(context.getPeerIdentity());
+ } else {
+ System.out.println("anonymous");
+ }
+ System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER));
+ System.out.println(Utils.prettyPrint(response));
+ } else {
+ System.out.println("No response received.");
+ }
+ Thread.sleep(5000);
+ }
+ } catch (Exception e) {
+ System.out.println("Error occurred while sending COAP requests.");
+ }
+ });
+ }
+
+ private CoapClient getCoapClient(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException {
+ URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys));
+ CoapClient client = new CoapClient(uri);
+ CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
+ builder.setConnector(dtlsConnector);
+
+ client.setEndpoint(builder.build());
+ return client;
+ }
+
+ private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) {
+ return "coaps://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys;
+ }
+
+ public static void main(String[] args) throws URISyntaxException {
+ System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientNoAuth " +
+ "host port accessToken keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys");
+
+ String host = args[0];
+ int port = Integer.parseInt(args[1]);
+ String accessToken = args[2];
+ String clientKeys = args[7];
+ String sharedKeys = args[8];
+
+ String keyStoreUriPath = args[3];
+ String keyStoreAlias = args[4];
+ String trustedAliasPattern = args[5];
+ String keyStorePassword = args[6];
+
+
+ DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder();
+ setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword);
+ DTLSConnector dtlsConnector = new DTLSConnector(builder.build());
+ SecureClientNoAuth client = new SecureClientNoAuth(dtlsConnector, host, port, accessToken, clientKeys, sharedKeys);
+ client.test();
+ }
+
+ private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) {
+ StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder();
+ try {
+ SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials(
+ keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray());
+ Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates(
+ keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray());
+ trustBuilder.setTrustedCertificates(trustedCertificates);
+ config.setAdvancedCertificateVerifier(trustBuilder.build());
+ config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509));
+ } catch (GeneralSecurityException e) {
+ System.err.println("certificates are invalid!");
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (IOException e) {
+ System.err.println("certificates are missing!");
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientX509.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientX509.java
new file mode 100644
index 0000000000..31dd628b40
--- /dev/null
+++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/SecureClientX509.java
@@ -0,0 +1,144 @@
+/**
+ * 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.coap.client;
+
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapResponse;
+import org.eclipse.californium.core.Utils;
+import org.eclipse.californium.core.network.CoapEndpoint;
+import org.eclipse.californium.elements.DtlsEndpointContext;
+import org.eclipse.californium.elements.EndpointContext;
+import org.eclipse.californium.elements.exception.ConnectorException;
+import org.eclipse.californium.elements.util.SslContextUtil;
+import org.eclipse.californium.scandium.DTLSConnector;
+import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
+import org.eclipse.californium.scandium.dtls.CertificateType;
+import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class SecureClientX509 {
+
+ private final DTLSConnector dtlsConnector;
+ private ExecutorService executor = Executors.newFixedThreadPool(1);
+ private CoapClient coapClient;
+
+ public SecureClientX509(DTLSConnector dtlsConnector, String host, int port, String clientKeys, String sharedKeys) throws URISyntaxException {
+ this.dtlsConnector = dtlsConnector;
+ this.coapClient = getCoapClient(host, port, clientKeys, sharedKeys);
+ }
+
+ public void test() {
+ executor.submit(() -> {
+ try {
+ while (!Thread.interrupted()) {
+ CoapResponse response = null;
+ try {
+ response = coapClient.get();
+ } catch (ConnectorException | IOException e) {
+ System.err.println("Error occurred while sending request: " + e);
+ System.exit(-1);
+ }
+ if (response != null) {
+
+ System.out.println(response.getCode() + " - " + response.getCode().name());
+ System.out.println(response.getOptions());
+ System.out.println(response.getResponseText());
+ System.out.println();
+ System.out.println("ADVANCED:");
+ EndpointContext context = response.advanced().getSourceContext();
+ Principal identity = context.getPeerIdentity();
+ if (identity != null) {
+ System.out.println(context.getPeerIdentity());
+ } else {
+ System.out.println("anonymous");
+ }
+ System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER));
+ System.out.println(Utils.prettyPrint(response));
+ } else {
+ System.out.println("No response received.");
+ }
+ Thread.sleep(5000);
+ }
+ } catch (Exception e) {
+ System.out.println("Error occurred while sending COAP requests.");
+ }
+ });
+ }
+
+ private CoapClient getCoapClient(String host, Integer port, String clientKeys, String sharedKeys) throws URISyntaxException {
+ URI uri = new URI(getFutureUrl(host, port, clientKeys, sharedKeys));
+ CoapClient client = new CoapClient(uri);
+ CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
+ builder.setConnector(dtlsConnector);
+
+ client.setEndpoint(builder.build());
+ return client;
+ }
+
+ private String getFutureUrl(String host, Integer port, String clientKeys, String sharedKeys) {
+ return "coaps://" + host + ":" + port + "/api/v1/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys;
+ }
+
+ public static void main(String[] args) throws URISyntaxException {
+ System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientX509 " +
+ "host port keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys");
+
+ String host = args[0];
+ int port = Integer.parseInt(args[1]);
+ String clientKeys = args[6];
+ String sharedKeys = args[7];
+
+ String keyStoreUriPath = args[2];
+ String keyStoreAlias = args[3];
+ String trustedAliasPattern = args[4];
+ String keyStorePassword = args[5];
+
+
+ DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder();
+ setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword);
+ DTLSConnector dtlsConnector = new DTLSConnector(builder.build());
+ SecureClientX509 client = new SecureClientX509(dtlsConnector, host, port, clientKeys, sharedKeys);
+ client.test();
+ }
+
+ private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) {
+ StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder();
+ try {
+ SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials(
+ keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray());
+ Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates(
+ keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray());
+ trustBuilder.setTrustedCertificates(trustedCertificates);
+ config.setAdvancedCertificateVerifier(trustBuilder.build());
+ config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509));
+ } catch (GeneralSecurityException e) {
+ System.err.println("certificates are invalid!");
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (IOException e) {
+ System.err.println("certificates are missing!");
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
index c2cf3686e9..1c0801c64a 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
@@ -30,7 +30,7 @@ import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.transport.mqtt.util.SslUtil;
+import org.thingsboard.server.common.transport.util.SslUtil;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
@@ -41,7 +41,6 @@ import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
index 06a8dcacdc..6056aa293d 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
+++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
@@ -66,7 +66,7 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
-import org.thingsboard.server.transport.mqtt.util.SslUtil;
+import org.thingsboard.server.common.transport.util.SslUtil;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.security.cert.Certificate;
diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java
index ab18b930f9..b175ca8580 100644
--- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/SessionInfoCreator.java
@@ -25,7 +25,15 @@ import java.util.UUID;
public class SessionInfoCreator {
public static TransportProtos.SessionInfoProto create(ValidateDeviceCredentialsResponse msg, TransportContext context, UUID sessionId) {
- return TransportProtos.SessionInfoProto.newBuilder().setNodeId(context.getNodeId())
+ return getSessionInfoProto(msg, context.getNodeId(), sessionId);
+ }
+
+ public static TransportProtos.SessionInfoProto create(ValidateDeviceCredentialsResponse msg, String nodeId, UUID sessionId) {
+ return getSessionInfoProto(msg, nodeId, sessionId);
+ }
+
+ private static TransportProtos.SessionInfoProto getSessionInfoProto(ValidateDeviceCredentialsResponse msg, String nodeId, UUID sessionId) {
+ return TransportProtos.SessionInfoProto.newBuilder().setNodeId(nodeId)
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceId().getId().getMostSignificantBits())
diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/SslUtil.java
similarity index 93%
rename from common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
rename to common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/SslUtil.java
index f376077b84..77e4045655 100644
--- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/SslUtil.java
+++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/SslUtil.java
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.thingsboard.server.transport.mqtt.util;
+package org.thingsboard.server.common.transport.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import org.thingsboard.server.common.msg.EncryptionUtil;
-import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
diff --git a/pom.xml b/pom.xml
index d1edef19fb..d9af0abf12 100755
--- a/pom.xml
+++ b/pom.xml
@@ -1152,6 +1152,11 @@
californium-core
${californium.version}
+
+ org.eclipse.californium
+ scandium
+ ${californium.version}
+
com.google.code.gson
gson
diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml
index a9fe673b28..0ffcd27f21 100644
--- a/transport/coap/src/main/resources/tb-coap-transport.yml
+++ b/transport/coap/src/main/resources/tb-coap-transport.yml
@@ -46,6 +46,24 @@ transport:
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
timeout: "${COAP_TIMEOUT:10000}"
+ dtls:
+ # Enable/disable DTLS 1.2 support
+ enabled: "${COAP_DTLS_ENABLED:false}"
+ # Secure mode. Allowed values: NO_AUTH, X509
+ mode: "${COAP_DTLS_SECURE_MODE:NO_AUTH}"
+ # Path to the key store that holds the certificate
+ key_store: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
+ # Password used to access the key store
+ key_store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
+ # Password used to access the key
+ key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
+ # Key alias
+ key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
+ # Skip certificate validity check for client certificates.
+ skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
+ x509:
+ dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
+ dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
sessions:
inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}"
report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:30000}"
diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts
index 0373f79737..f3ae969911 100644
--- a/ui-ngx/src/app/shared/models/device.models.ts
+++ b/ui-ngx/src/app/shared/models/device.models.ts
@@ -518,7 +518,7 @@ export enum DeviceCredentialsType {
export const credentialTypeNames = new Map(
[
[DeviceCredentialsType.ACCESS_TOKEN, 'Access token'],
- [DeviceCredentialsType.X509_CERTIFICATE, 'MQTT X.509'],
+ [DeviceCredentialsType.X509_CERTIFICATE, 'X.509'],
[DeviceCredentialsType.MQTT_BASIC, 'MQTT Basic']
]
);