fix_bug_coaps_x509_docker_many_devices (#12327)
* coaps: x509 - tests * coaps: x509 - tests add sever cert.pem * coaps: x509 - tests add sever cert.pem -2 * coaps: x509 - tests add sever cert.pem -3 * coaps: x509 - tests add sever cert.pem -4 * fix bug: coaps x509 - ddocker many devices with re-port * fix bug: coaps - add test, connect client with X509 * fix bug: coaps - add two test devices with the same port * fix bug: coaps - add two test devices with the same port (FeatureType.ATTRIBUTES) * fix bug: coaps - add two test devices with the same port (FeatureType.ATTRIBUTES) - 1 * fix bug: coap comments 1
This commit is contained in:
parent
0a1f377347
commit
c5ca395844
@ -54,6 +54,14 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg
|
||||
|
||||
protected final byte[] EMPTY_PAYLOAD = new byte[0];
|
||||
protected CoapTestClient client;
|
||||
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
|
||||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
|
||||
protected static final String PAYLOAD_VALUES_STR_01 = "{\"key2\":\"value2\", \"key3\":false, \"key4\": 4.0, \"key5\": 5," +
|
||||
" \"key6\": {\"someNumber_02\": 52, \"someArray_02\": [1,2,3,4], \"someNestedObject_02\": {\"key_02\": \"value_02\"}}}";
|
||||
|
||||
protected void processBeforeTest() throws Exception {
|
||||
loginTenantAdmin();
|
||||
}
|
||||
|
||||
protected void processAfterTest() throws Exception {
|
||||
if (client != null) {
|
||||
|
||||
@ -63,8 +63,6 @@ import static org.thingsboard.server.common.data.query.EntityKeyType.SHARED_ATTR
|
||||
@DaoSqlTest
|
||||
public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest {
|
||||
|
||||
private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
|
||||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
|
||||
private static final List<String> EXPECTED_KEYS = Arrays.asList("key1", "key2", "key3", "key4", "key5");
|
||||
private static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}";
|
||||
|
||||
|
||||
@ -0,0 +1,291 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.security;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.californium.core.CoapResponse;
|
||||
import org.eclipse.californium.core.coap.CoAP;
|
||||
import org.junit.Assert;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.CoapDeviceType;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
|
||||
import org.thingsboard.server.common.data.TransportPayloadType;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||
import org.thingsboard.server.common.msg.session.FeatureType;
|
||||
import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
|
||||
import org.thingsboard.server.transport.coap.x509.CertPrivateKey;
|
||||
import org.thingsboard.server.transport.coap.x509.CoapClientX509Test;
|
||||
import org.thingsboard.server.transport.coap.CoapTestConfigProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@Slf4j
|
||||
@TestPropertySource(properties = {
|
||||
"coap.enabled=true",
|
||||
"coap.dtls.enabled=true",
|
||||
"coap.dtls.credentials.pem.cert_file=coap/credentials/server/cert.pem",
|
||||
"device.connectivity.coaps.enabled=true",
|
||||
"service.integrations.supported=ALL",
|
||||
"transport.coap.enabled=true",
|
||||
})
|
||||
public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest {
|
||||
private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/";
|
||||
protected final String CREDENTIALS_PATH = "coap/credentials/";
|
||||
protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/";
|
||||
protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem";
|
||||
protected final String CREDENTIALS_PATH_CLIENT_KEY_PEM = CREDENTIALS_PATH_CLIENT + "key.pem";
|
||||
protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name")
|
||||
protected final PrivateKey clientPrivateKeyFromCertTrustNo;
|
||||
|
||||
protected static final String CLIENT_JKS_FOR_TEST = "coapclientTest";
|
||||
protected static final String CLIENT_STORE_PWD = "client_ks_password";
|
||||
protected static final String CLIENT_ALIAS_CERT_TRUST_NO = "client_alias_trust_no";
|
||||
|
||||
protected AbstractCoapSecurityIntegrationTest() {
|
||||
|
||||
try {
|
||||
// Get certificates from key store
|
||||
char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray();
|
||||
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
try (InputStream clientKeyStoreFile =
|
||||
this.getClass().getClassLoader().
|
||||
getResourceAsStream(CREDENTIALS_PATH + CLIENT_JKS_FOR_TEST + ".jks")) {
|
||||
clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd);
|
||||
}
|
||||
// No trust
|
||||
clientPrivateKeyFromCertTrustNo = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST_NO, clientKeyStorePwd);
|
||||
clientX509CertTrustNo = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST_NO);
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Device createDeviceWithX509(String deviceName, DeviceProfileId deviceProfileId, X509Certificate clientX509Cert) throws Exception {
|
||||
Device device = new Device();
|
||||
device.setName(deviceName);
|
||||
device.setType(deviceName);
|
||||
device.setDeviceProfileId(deviceProfileId);
|
||||
|
||||
DeviceCredentials deviceCredentials = new DeviceCredentials();
|
||||
deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
|
||||
String pemFormatCert = CertPrivateKey.convertCertToPEM(clientX509Cert);
|
||||
deviceCredentials.setCredentialsValue(pemFormatCert);
|
||||
|
||||
SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, deviceCredentials);
|
||||
Device deviceX509 = readResponse(doPost("/api/device-with-credentials", saveRequest)
|
||||
.andExpect(status().isOk()), Device.class);
|
||||
DeviceCredentials savedDeviceCredentials =
|
||||
doGet("/api/device/" + deviceX509.getId().getId() + "/credentials", DeviceCredentials.class);
|
||||
Assert.assertNotNull(savedDeviceCredentials);
|
||||
Assert.assertNotNull(savedDeviceCredentials.getId());
|
||||
Assert.assertEquals(deviceX509.getId(), savedDeviceCredentials.getDeviceId());
|
||||
Assert.assertEquals(DeviceCredentialsType.X509_CERTIFICATE, savedDeviceCredentials.getCredentialsType());
|
||||
accessToken = savedDeviceCredentials.getCredentialsId();
|
||||
assertNotNull(accessToken);
|
||||
return deviceX509;
|
||||
}
|
||||
|
||||
protected void clientX509FromJksUpdateAttributesTest() throws Exception {
|
||||
CertPrivateKey certPrivateKey = new CertPrivateKey(clientX509CertTrustNo, clientPrivateKeyFromCertTrustNo);
|
||||
CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder()
|
||||
.coapDeviceType(CoapDeviceType.DEFAULT)
|
||||
.transportPayloadType(TransportPayloadType.JSON)
|
||||
.build();
|
||||
DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties);
|
||||
assertNotNull(deviceProfile);
|
||||
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
|
||||
"CoapX509TrustNo_" + FeatureType.ATTRIBUTES.name(), deviceProfile.getId(), null);
|
||||
clientX509.disconnect();
|
||||
}
|
||||
|
||||
protected void clientX509FromPathUpdateFeatureTypeTest(FeatureType featureType) throws Exception {
|
||||
CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM);
|
||||
CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder()
|
||||
.coapDeviceType(CoapDeviceType.DEFAULT)
|
||||
.transportPayloadType(TransportPayloadType.JSON)
|
||||
.build();
|
||||
DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties);
|
||||
assertNotNull(deviceProfile);
|
||||
CoapClientX509Test clientX509 = clientX509UpdateTest(featureType, certPrivateKey,
|
||||
"CoapX509TrustNo_" + featureType.name(), deviceProfile.getId(), null);
|
||||
clientX509.disconnect();
|
||||
}
|
||||
protected void twoClientWithSamePortX509FromPathConnectTest() throws Exception {
|
||||
CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder()
|
||||
.coapDeviceType(CoapDeviceType.DEFAULT)
|
||||
.transportPayloadType(TransportPayloadType.JSON)
|
||||
.build();
|
||||
DeviceProfile deviceProfile = createCoapDeviceProfile(configProperties);
|
||||
CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM);
|
||||
CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem",
|
||||
CREDENTIALS_PATH_CLIENT + "key_01.pem");
|
||||
Integer fixedPort = getFreePort();
|
||||
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
|
||||
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort);
|
||||
clientX509.disconnect();
|
||||
await("Need to make port " + fixedPort + " free")
|
||||
.atMost(40, TimeUnit.SECONDS)
|
||||
.until(() -> isPortAvailable(fixedPort));
|
||||
CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01,
|
||||
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(),
|
||||
fixedPort, PAYLOAD_VALUES_STR_01);
|
||||
clientX509_01.disconnect();
|
||||
}
|
||||
|
||||
private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey,
|
||||
String deviceName, DeviceProfileId deviceProfileId, Integer fixedPort) throws Exception {
|
||||
return clientX509UpdateTest(featureType, certPrivateKey, deviceName, deviceProfileId, fixedPort, null);
|
||||
}
|
||||
|
||||
private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey,
|
||||
String deviceName, DeviceProfileId deviceProfileId, Integer fixedPort, String payload) throws Exception {
|
||||
String payloadValuesStr = payload == null ? PAYLOAD_VALUES_STR : payload;
|
||||
Device deviceX509 = createDeviceWithX509(deviceName, deviceProfileId, certPrivateKey.getCert());
|
||||
CoapClientX509Test clientX509 = new CoapClientX509Test(certPrivateKey, featureType, COAPS_BASE_URL, fixedPort);
|
||||
CoapResponse coapResponseX509 = clientX509.postMethod(payloadValuesStr);
|
||||
assertNotNull(coapResponseX509);
|
||||
assertEquals(CoAP.ResponseCode.CREATED, coapResponseX509.getCode());
|
||||
|
||||
if (FeatureType.ATTRIBUTES.equals(featureType)) {
|
||||
DeviceId deviceId = deviceX509.getId();
|
||||
JsonNode expectedNode = JacksonUtil.toJsonNode(payloadValuesStr);
|
||||
List<String> expectedKeys = getKeysFromNode(expectedNode);
|
||||
List<String> actualKeys = getActualKeysList(deviceId, expectedKeys, "attributes/CLIENT_SCOPE");
|
||||
assertNotNull(actualKeys);
|
||||
|
||||
Set<String> actualKeySet = new HashSet<>(actualKeys);
|
||||
Set<String> expectedKeySet = new HashSet<>(expectedKeys);
|
||||
assertEquals(expectedKeySet, actualKeySet);
|
||||
|
||||
String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet, "attributes/CLIENT_SCOPE");
|
||||
List<Map<String, Object>> actualValues = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {
|
||||
});
|
||||
assertValuesList(actualValues, expectedNode);
|
||||
}
|
||||
return clientX509;
|
||||
}
|
||||
|
||||
private List<String> getActualKeysList(DeviceId deviceId, List<String> expectedKeys, String apiSuffix) throws Exception {
|
||||
long start = System.currentTimeMillis();
|
||||
long end = System.currentTimeMillis() + 5000;
|
||||
|
||||
List<String> actualKeys = null;
|
||||
while (start <= end) {
|
||||
actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/" + apiSuffix, new TypeReference<>() {
|
||||
});
|
||||
if (actualKeys.size() == expectedKeys.size()) {
|
||||
break;
|
||||
}
|
||||
Thread.sleep(100);
|
||||
start += 100;
|
||||
}
|
||||
return actualKeys;
|
||||
}
|
||||
|
||||
private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet, String apiSuffix) {
|
||||
return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/" + apiSuffix + "?keys=" + String.join(",", actualKeySet);
|
||||
}
|
||||
|
||||
private List getKeysFromNode(JsonNode jNode) {
|
||||
List<String> jKeys = new ArrayList<>();
|
||||
Iterator<String> fieldNames = jNode.fieldNames();
|
||||
while (fieldNames.hasNext()) {
|
||||
jKeys.add(fieldNames.next());
|
||||
}
|
||||
return jKeys;
|
||||
}
|
||||
|
||||
protected void assertValuesList(List<Map<String, Object>> actualValues, JsonNode expectedValues) {
|
||||
assertTrue(actualValues.size() > 0);
|
||||
assertEquals(expectedValues.size(), actualValues.size());
|
||||
for (Map<String, Object> map : actualValues) {
|
||||
String key = (String) map.get("key");
|
||||
Object actualValue = map.get("value");
|
||||
assertTrue(expectedValues.has(key));
|
||||
JsonNode expectedValue = expectedValues.get(key);
|
||||
assertExpectedActualValue(expectedValue, actualValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertExpectedActualValue(JsonNode expectedValue, Object actualValue) {
|
||||
switch (expectedValue.getNodeType()) {
|
||||
case STRING:
|
||||
assertEquals(expectedValue.asText(), actualValue);
|
||||
break;
|
||||
case NUMBER:
|
||||
if (expectedValue.isInt()) {
|
||||
assertEquals(expectedValue.asInt(), actualValue);
|
||||
} else if (expectedValue.isLong()) {
|
||||
assertEquals(expectedValue.asLong(), actualValue);
|
||||
} else if (expectedValue.isFloat() || expectedValue.isDouble()) {
|
||||
assertEquals(expectedValue.asDouble(), actualValue);
|
||||
}
|
||||
break;
|
||||
case BOOLEAN:
|
||||
assertEquals(expectedValue.asBoolean(), actualValue);
|
||||
break;
|
||||
case ARRAY:
|
||||
case OBJECT:
|
||||
expectedValue.toString().equals(JacksonUtil.toString(actualValue));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getFreePort() throws IOException {
|
||||
try (ServerSocket socket = new ServerSocket(0)) {
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPortAvailable(int port) {
|
||||
try (ServerSocket serverSocket = new ServerSocket(port)) {
|
||||
serverSocket.setReuseAddress(true);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.security.sql;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
import org.thingsboard.server.transport.coap.security.AbstractCoapSecurityIntegrationTest;
|
||||
|
||||
@Slf4j
|
||||
@DaoSqlTest
|
||||
@TestPropertySource(properties = {
|
||||
"coap.dtls.credentials.type=KEYSTORE",
|
||||
"coap.dtls.credentials.keystore.store_file=coap/credentials/coapserverTest.jks",
|
||||
"coap.dtls.credentials.keystore.key_password=server_ks_password",
|
||||
"coap.dtls.credentials.keystore.key_alias=server",
|
||||
})
|
||||
public class CoapClientX509SecurityJksIntegrationTest extends AbstractCoapSecurityIntegrationTest {
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
processBeforeTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX509NoTrustFromJksConnectCoapSuccessUpdateAttributesSuccess() throws Exception {
|
||||
clientX509FromJksUpdateAttributesTest();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.security.sql;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.server.common.msg.session.FeatureType;
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
import org.thingsboard.server.transport.coap.security.AbstractCoapSecurityIntegrationTest;
|
||||
|
||||
@Slf4j
|
||||
@DaoSqlTest
|
||||
public class CoapClientX509SecurityPemIntegrationTest extends AbstractCoapSecurityIntegrationTest {
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
processBeforeTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX509NoTrustFromPathConnectCoapSuccessUpdateAttributesSuccess() throws Exception {
|
||||
clientX509FromPathUpdateFeatureTypeTest(FeatureType.ATTRIBUTES);
|
||||
}
|
||||
@Test
|
||||
public void testX509NoTrustFromPathConnectCoapSuccessUpdateTelemetrySuccess() throws Exception {
|
||||
clientX509FromPathUpdateFeatureTypeTest(FeatureType.TELEMETRY);
|
||||
} @Test
|
||||
public void testTwoDevicesWithSamePortX509NoTrustFromPathConnectCoapSuccess() throws Exception {
|
||||
twoClientWithSamePortX509FromPathConnectTest();
|
||||
}
|
||||
}
|
||||
@ -44,9 +44,6 @@ import static org.junit.Assert.assertTrue;
|
||||
@DaoSqlTest
|
||||
public class CoapAttributesIntegrationTest extends AbstractCoapIntegrationTest {
|
||||
|
||||
private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
|
||||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder()
|
||||
|
||||
@ -40,9 +40,6 @@ import static org.junit.Assert.assertNotNull;
|
||||
@Slf4j
|
||||
public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoapIntegrationTest {
|
||||
|
||||
private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
|
||||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder()
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.x509;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.thingsboard.common.util.SslUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
public class CertPrivateKey {
|
||||
private final X509Certificate cert;
|
||||
private PrivateKey privateKey;
|
||||
|
||||
public CertPrivateKey(String certFilePathPem, String keyFilePathPem) throws Exception {
|
||||
List<X509Certificate> certs = SslUtil.readCertFile(fileRead(certFilePathPem));
|
||||
this.cert = certs.get(0);
|
||||
this.privateKey = SslUtil.readPrivateKey(fileRead(keyFilePathPem), null);
|
||||
if (this.privateKey instanceof BCECPrivateKey) {
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(this.privateKey.getEncoded());
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
this.privateKey = keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
if (!(this.privateKey instanceof ECPrivateKey)) {
|
||||
throw new RuntimeException("Private key generation must be of type java.security.interfaces.ECPrivateKey, which is used in the standard Java API!");
|
||||
}
|
||||
}
|
||||
|
||||
public CertPrivateKey(X509Certificate cert, PrivateKey privateKey) {
|
||||
this.cert = cert;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public X509Certificate getCert() {
|
||||
return this.cert;
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey() {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
private String fileRead(String fileName) throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
File file = new File(classLoader.getResource(fileName).getFile());
|
||||
return FileUtils.readFileToString(file, "UTF-8");
|
||||
}
|
||||
|
||||
public static String convertCertToPEM(X509Certificate certificate) throws Exception {
|
||||
StringBuilder pemBuilder = new StringBuilder();
|
||||
pemBuilder.append("-----BEGIN CERTIFICATE-----\n");
|
||||
// Copy cert to Base64
|
||||
String base64EncodedCert = Base64.getEncoder().encodeToString(certificate.getEncoded());
|
||||
int index = 0;
|
||||
while (index < base64EncodedCert.length()) {
|
||||
pemBuilder.append(base64EncodedCert, index, Math.min(index + 64, base64EncodedCert.length()));
|
||||
pemBuilder.append("\n");
|
||||
index += 64;
|
||||
}
|
||||
pemBuilder.append("-----END CERTIFICATE-----\n");
|
||||
return pemBuilder.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.x509;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.californium.core.CoapClient;
|
||||
import org.eclipse.californium.core.CoapHandler;
|
||||
import org.eclipse.californium.core.CoapObserveRelation;
|
||||
import org.eclipse.californium.core.CoapResponse;
|
||||
import org.eclipse.californium.core.coap.CoAP;
|
||||
import org.eclipse.californium.core.coap.MediaTypeRegistry;
|
||||
import org.eclipse.californium.core.coap.Request;
|
||||
import org.eclipse.californium.core.config.CoapConfig;
|
||||
import org.eclipse.californium.core.network.CoapEndpoint;
|
||||
import org.eclipse.californium.elements.config.Configuration;
|
||||
import org.eclipse.californium.elements.config.ValueException;
|
||||
import org.eclipse.californium.elements.exception.ConnectorException;
|
||||
import org.eclipse.californium.scandium.DTLSConnector;
|
||||
import org.eclipse.californium.scandium.config.DtlsConfig.SignatureAndHashAlgorithmsDefinition;
|
||||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
|
||||
import org.eclipse.californium.scandium.dtls.CertificateType;
|
||||
import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm;
|
||||
import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.HashAlgorithm;
|
||||
import org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SignatureAlgorithm;
|
||||
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
|
||||
import org.eclipse.californium.scandium.dtls.x509.CertificateProvider;
|
||||
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
|
||||
import org.thingsboard.server.common.msg.session.FeatureType;
|
||||
import org.thingsboard.server.transport.coap.CoapTestCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_FRAGMENTED_HANDSHAKE_MESSAGE_LENGTH;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_PENDING_HANDSHAKE_RESULT_JOBS;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_RETRANSMISSIONS;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_RETRANSMISSION_TIMEOUT;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECEIVE_BUFFER_SIZE;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_SIGNATURE_AND_HASH_ALGORITHMS;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_USE_HELLO_VERIFY_REQUEST;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_USE_MULTI_HANDSHAKE_MESSAGE_RECORDS;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.CLIENT_ONLY;
|
||||
import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE;
|
||||
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA;
|
||||
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA;
|
||||
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA;
|
||||
|
||||
@Slf4j
|
||||
public class CoapClientX509Test {
|
||||
|
||||
private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
|
||||
|
||||
private final CoapClient clientX509;
|
||||
private final DTLSConnector dtlsConnector;
|
||||
private final Configuration config;
|
||||
private final CertPrivateKey certPrivateKey;
|
||||
private final String coapsBaseUrl;
|
||||
|
||||
@Getter
|
||||
private CoAP.Type type = CoAP.Type.CON;
|
||||
|
||||
public CoapClientX509Test(CertPrivateKey certPrivateKey, FeatureType featureType, String coapsBaseUrl, Integer fixedPort) {
|
||||
this.certPrivateKey = certPrivateKey;
|
||||
this.coapsBaseUrl = coapsBaseUrl;
|
||||
this.config = createConfiguration();
|
||||
this.dtlsConnector = createDTLSConnector(fixedPort);
|
||||
this.clientX509 = createClient(getFeatureTokenUrl(featureType));
|
||||
}
|
||||
public void disconnect() {
|
||||
if (clientX509 != null) {
|
||||
clientX509.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException {
|
||||
return this.postMethod(requestBody.getBytes());
|
||||
}
|
||||
|
||||
public CoapResponse postMethod(byte[] requestBodyBytes) throws ConnectorException, IOException {
|
||||
return clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(requestBodyBytes, MediaTypeRegistry.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
public void postMethod(CoapHandler handler, String payload, int format) {
|
||||
clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, format);
|
||||
}
|
||||
|
||||
public void postMethod(CoapHandler handler, byte[] payload) {
|
||||
clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, MediaTypeRegistry.APPLICATION_JSON);
|
||||
}
|
||||
public void postMethod(CoapHandler handler, byte[] payload, int format) {
|
||||
clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).post(handler, payload, format);
|
||||
}
|
||||
|
||||
public CoapResponse getMethod() throws ConnectorException, IOException {
|
||||
return clientX509.setTimeout(CLIENT_REQUEST_TIMEOUT).get();
|
||||
}
|
||||
|
||||
public CoapObserveRelation getObserveRelation(CoapTestCallback callback) {
|
||||
return getObserveRelation(callback, true);
|
||||
}
|
||||
|
||||
public CoapObserveRelation getObserveRelation(CoapTestCallback callback, boolean confirmable) {
|
||||
Request request = Request.newGet().setObserve();
|
||||
request.setType(confirmable ? CoAP.Type.CON : CoAP.Type.NON);
|
||||
return clientX509.observe(request, callback);
|
||||
}
|
||||
|
||||
public void setURI(String featureTokenUrl) {
|
||||
if (clientX509 == null) {
|
||||
throw new RuntimeException("Failed to connect! CoapClient is not initialized!");
|
||||
}
|
||||
clientX509.setURI(featureTokenUrl);
|
||||
}
|
||||
|
||||
public void setURI(String accessToken, FeatureType featureType) {
|
||||
if (featureType == null) {
|
||||
featureType = FeatureType.ATTRIBUTES;
|
||||
}
|
||||
setURI(getFeatureTokenUrl(accessToken, featureType));
|
||||
}
|
||||
|
||||
public void useCONs() {
|
||||
if (clientX509 == null) {
|
||||
throw new RuntimeException("Failed to connect! CoapClient is not initialized!");
|
||||
}
|
||||
type = CoAP.Type.CON;
|
||||
clientX509.useCONs();
|
||||
}
|
||||
|
||||
public void useNONs() {
|
||||
if (clientX509 == null) {
|
||||
throw new RuntimeException("Failed to connect! CoapClient is not initialized!");
|
||||
}
|
||||
type = CoAP.Type.NON;
|
||||
clientX509.useNONs();
|
||||
}
|
||||
|
||||
private Configuration createConfiguration() {
|
||||
Configuration clientCoapConfig = new Configuration();
|
||||
clientCoapConfig.set(CoapConfig.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
|
||||
clientCoapConfig.set(CoapConfig.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
|
||||
clientCoapConfig.set(CoapConfig.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS, TimeUnit.SECONDS);
|
||||
clientCoapConfig.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
|
||||
clientCoapConfig.set(CoapConfig.RESPONSE_MATCHING, CoapConfig.MatcherMode.RELAXED);
|
||||
clientCoapConfig.set(CoapConfig.PREFERRED_BLOCK_SIZE, 1024);
|
||||
clientCoapConfig.set(CoapConfig.MAX_MESSAGE_SIZE, 1024);
|
||||
clientCoapConfig.set(DTLS_ROLE, CLIENT_ONLY);
|
||||
clientCoapConfig.set(DTLS_MAX_RETRANSMISSIONS, 2);
|
||||
clientCoapConfig.set(DTLS_RETRANSMISSION_TIMEOUT, 5000, MILLISECONDS);
|
||||
clientCoapConfig.set(DTLS_MAX_RETRANSMISSION_TIMEOUT, 60000, TimeUnit.MILLISECONDS);
|
||||
clientCoapConfig.set(DTLS_USE_HELLO_VERIFY_REQUEST, false);
|
||||
clientCoapConfig.set(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT, false);
|
||||
clientCoapConfig.set(DTLS_MAX_FRAGMENTED_HANDSHAKE_MESSAGE_LENGTH, 22490);
|
||||
clientCoapConfig.set(DTLS_AUTO_HANDSHAKE_TIMEOUT, 100000, TimeUnit.MILLISECONDS);
|
||||
clientCoapConfig.set(DTLS_MAX_PENDING_HANDSHAKE_RESULT_JOBS, 64);
|
||||
clientCoapConfig.set(DTLS_USE_MULTI_HANDSHAKE_MESSAGE_RECORDS, false);
|
||||
clientCoapConfig.set(DTLS_RECEIVE_BUFFER_SIZE, 8192);
|
||||
clientCoapConfig.setTransient(DTLS_RECOMMENDED_CIPHER_SUITES_ONLY);
|
||||
SignatureAndHashAlgorithmsDefinition algorithmsDefinition = new SignatureAndHashAlgorithmsDefinition(MODULE + "SIGNATURE_AND_HASH_ALGORITHMS", "List of DTLS signature- and hash-algorithms.\nValues e.g SHA256withECDSA or ED25519.");
|
||||
SignatureAndHashAlgorithm SHA384_WITH_RSA = new SignatureAndHashAlgorithm(HashAlgorithm.SHA384,
|
||||
SignatureAlgorithm.RSA);
|
||||
List<SignatureAndHashAlgorithm> algorithms = null;
|
||||
try {
|
||||
algorithms = algorithmsDefinition.checkValue(Arrays.asList(SHA256_WITH_ECDSA, SHA256_WITH_RSA, SHA384_WITH_ECDSA, SHA384_WITH_RSA));
|
||||
} catch (ValueException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
clientCoapConfig.setTransient(DTLS_SIGNATURE_AND_HASH_ALGORITHMS);
|
||||
clientCoapConfig.set(DTLS_SIGNATURE_AND_HASH_ALGORITHMS, algorithms);
|
||||
clientCoapConfig.setTransient(DTLS_CIPHER_SUITES);
|
||||
clientCoapConfig.set(DTLS_CIPHER_SUITES, Arrays.asList(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256));
|
||||
clientCoapConfig.setTransient(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT);
|
||||
clientCoapConfig.set(DTLS_VERIFY_SERVER_CERTIFICATES_SUBJECT, false);
|
||||
return clientCoapConfig;
|
||||
}
|
||||
|
||||
private DTLSConnector createDTLSConnector(Integer fixedPort) {
|
||||
try {
|
||||
// Create DTLS config client
|
||||
DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(this.config);
|
||||
configBuilder.setAdvancedCertificateVerifier(new TbAdvancedCertificateVerifier());
|
||||
X509Certificate[] certificateChainClient = new X509Certificate[]{this.certPrivateKey.getCert()};
|
||||
CertificateProvider certificateProvider = new SingleCertificateProvider(this.certPrivateKey.getPrivateKey(), certificateChainClient, Collections.singletonList(CertificateType.X_509));
|
||||
configBuilder.setCertificateIdentityProvider(certificateProvider);
|
||||
if (fixedPort != null) {
|
||||
InetSocketAddress localAddress = new InetSocketAddress("0.0.0.0", fixedPort);
|
||||
configBuilder.setAddress(localAddress);
|
||||
configBuilder.setReuseAddress(true);
|
||||
}
|
||||
return new DTLSConnector(configBuilder.build());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("", e);
|
||||
}
|
||||
}
|
||||
|
||||
private CoapClient createClient(String featureTokenUrl) {
|
||||
CoapClient client = new CoapClient(featureTokenUrl);
|
||||
CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
|
||||
builder.setConnector(dtlsConnector);
|
||||
client.setEndpoint(builder.build());
|
||||
return client;
|
||||
}
|
||||
|
||||
public String getFeatureTokenUrl(FeatureType featureType) {
|
||||
return this.coapsBaseUrl + featureType.name().toLowerCase();
|
||||
}
|
||||
|
||||
public String getFeatureTokenUrl(String token, FeatureType featureType) {
|
||||
return this.coapsBaseUrl + token + "/" + featureType.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.x509;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.californium.scandium.dtls.AlertMessage;
|
||||
import org.eclipse.californium.scandium.dtls.AlertMessage.AlertDescription;
|
||||
import org.eclipse.californium.scandium.dtls.AlertMessage.AlertLevel;
|
||||
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.HandshakeException;
|
||||
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
|
||||
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
|
||||
import org.eclipse.californium.scandium.util.ServerNames;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertPath;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class TbAdvancedCertificateVerifier implements NewAdvancedCertificateVerifier {
|
||||
|
||||
private HandshakeResultHandler resultHandler;
|
||||
/**
|
||||
* Get the list of supported certificate types in order of preference.
|
||||
*
|
||||
* @return the list of supported certificate types.
|
||||
* @since 3.0 (renamed from getSupportedCertificateType)
|
||||
*/
|
||||
@Override
|
||||
public List<CertificateType> getSupportedCertificateTypes() {
|
||||
return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the certificate provided by the the peer as part of the
|
||||
* certificate message.
|
||||
* <p>
|
||||
* If a x509 certificate chain is provided in the certificate message,
|
||||
* validate the chain and key usage. If a RawPublicKey certificate is
|
||||
* provided, check, if this public key is trusted.
|
||||
*
|
||||
* @param cid connection ID
|
||||
* @param serverName indicated server names. May be {@code null}, if not
|
||||
* available or SNI is not enabled.
|
||||
* @param remotePeer socket address of remote peer
|
||||
* @param clientUsage indicator to check certificate usage. {@code true},
|
||||
* check key usage for client, {@code false} for server.
|
||||
* @param verifySubject {@code true} to verify the certificate's subjects,
|
||||
* {@code false}, if not.
|
||||
* @param truncateCertificatePath {@code true} truncate certificate path at
|
||||
* a trusted certificate before validation.
|
||||
* @param message certificate message to be validated
|
||||
* @return certificate verification result, or {@code null}, if result is
|
||||
* provided asynchronous.
|
||||
* @since 3.0 (removed DTLSSession session, added remotePeer and
|
||||
* verifySubject)
|
||||
*/
|
||||
@Override
|
||||
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, InetSocketAddress remotePeer,
|
||||
boolean clientUsage, boolean verifySubject, boolean truncateCertificatePath,
|
||||
CertificateMessage message) {
|
||||
CertPath certChain = message.getCertificateChain();
|
||||
CertificateVerificationResult result;
|
||||
|
||||
if (certChain == null) {
|
||||
PublicKey publicKey = message.getPublicKey();
|
||||
result = new CertificateVerificationResult(cid, publicKey, null);
|
||||
} else {
|
||||
if (message.getCertificateChain().getCertificates().isEmpty()) {
|
||||
result = new CertificateVerificationResult(cid, new HandshakeException("Empty certificate chain",
|
||||
new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE)), null);
|
||||
} else {
|
||||
result = new CertificateVerificationResult(cid, certChain, null);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an list of certificate authorities which are trusted
|
||||
* for authenticating peers.
|
||||
*
|
||||
* @return a non-null (possibly empty) list of accepted CA issuers.
|
||||
*/
|
||||
@Override
|
||||
public List<X500Principal> getAcceptedIssuers() {
|
||||
log.trace("getAcceptedIssuers: return null");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the handler for asynchronous handshake results.
|
||||
* <p>
|
||||
* Called during initialization of the {link DTLSConnector}. Synchronous
|
||||
* implementations may just ignore this using an empty implementation.
|
||||
*
|
||||
* @param resultHandler handler for asynchronous master secret results. This
|
||||
* handler MUST NOT be called from the thread calling
|
||||
* {@link #verifyCertificate(ConnectionId, ServerNames, InetSocketAddress, boolean, boolean, boolean, CertificateMessage)},
|
||||
* instead just return the result there.
|
||||
*/
|
||||
@Override
|
||||
public void setResultHandler(HandshakeResultHandler resultHandler) {
|
||||
if (this.resultHandler != null && resultHandler != null && this.resultHandler != resultHandler) {
|
||||
throw new IllegalStateException("handshake result handler already set!");
|
||||
}
|
||||
this.resultHandler = resultHandler;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB/TCCAaOgAwIBAgIIVNrVgKT9OE8wCgYIKoZIzj0EAwIwWjEOMAwGA1UEAxMF
|
||||
Y2YtY2ExFDASBgNVBAsTC0NhbGlmb3JuaXVtMRQwEgYDVQQKEwtFY2xpcHNlIElv
|
||||
VDEPMA0GA1UEBxMGT3R0YXdhMQswCQYDVQQGEwJDQTAeFw0yMzEwMjYwODA4MjJa
|
||||
Fw0yNTEwMjUwODA4MjJaMF4xEjAQBgNVBAMTCWNmLWNsaWVudDEUMBIGA1UECxML
|
||||
Q2FsaWZvcm5pdW0xFDASBgNVBAoTC0VjbGlwc2UgSW9UMQ8wDQYDVQQHEwZPdHRh
|
||||
d2ExCzAJBgNVBAYTAkNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQxYO5/M5
|
||||
ie6+3QPOaAy5MD6CkFILZwIb2rOBCX/EWPaocX1H+eynUnaEEbmqxeN6rnI/pH19
|
||||
j4PtsegfHLrzzaNPME0wHQYDVR0OBBYEFKwEDLTJ+5cQoZfbjWN1vJ2ssgK+MAsG
|
||||
A1UdDwQEAwIHgDAfBgNVHSMEGDAWgBSxVzoI1TL87++hsUb9vQwqODzgUTAKBggq
|
||||
hkjOPQQDAgNIADBFAiA2KCOw3n2AK9Vm8u2u1bQREIEs3tKAU7eFjpNFn929NwIh
|
||||
AInhBGoEwS2Xlu5bdZSfWnujoRrEQiIiQpStmLxVcIsH
|
||||
-----END CERTIFICATE-----
|
||||
@ -0,0 +1,14 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICIzCCAcmgAwIBAgIUZZCGYm65c9vU0Xfvd/pAnLVDouUwCgYIKoZIzj0EAwIw
|
||||
ZzELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtpeXYxDTALBgNVBAcMBEtpeXYxFDAS
|
||||
BgNVBAoMC1RoaW5nc2JvYXJkMRIwEAYDVQQLDAlkZXZlbG9wZXIxEDAOBgNVBAMM
|
||||
B2NlcnRfMDEwHhcNMjQxMjE4MTU1NjE1WhcNMjUxMjE4MTU1NjE1WjBnMQswCQYD
|
||||
VQQGEwJVQTENMAsGA1UECAwES2l5djENMAsGA1UEBwwES2l5djEUMBIGA1UECgwL
|
||||
VGhpbmdzYm9hcmQxEjAQBgNVBAsMCWRldmVsb3BlcjEQMA4GA1UEAwwHY2VydF8w
|
||||
MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNU1tE6o/QpqJJqpy+m+UoPuQe5g
|
||||
eTgS4M3x0iQS6pzNEJBhzbnOp/BysGMB4wKiAWTRuKdH/gcRXDBTjLd/d7ijUzBR
|
||||
MB0GA1UdDgQWBBSiao1iNWYzlsrSbxYqbda116HG1jAfBgNVHSMEGDAWgBSiao1i
|
||||
NWYzlsrSbxYqbda116HG1jAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gA
|
||||
MEUCIB2aCM/nvDqic9NkoSX/71GwksLiAKiFNkt2BZQykrcHAiEAr2h5IMdkyurN
|
||||
Jy/idx2y44CP0tMq/3QV0QLCQFJIi6s=
|
||||
-----END CERTIFICATE-----
|
||||
@ -0,0 +1,4 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDn0+4CuLeX7xwBs0ts
|
||||
UUEDB3+HRwRKdIPeJlIbKuvvEQ==
|
||||
-----END PRIVATE KEY-----
|
||||
@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BggqhkjOPQMBBw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIJldU1MBuJUJnNHa9Ob5NGlXc/Os6put9eh1TlIbuScnoAoGCCqGSM49
|
||||
AwEHoUQDQgAE1TW0Tqj9CmokmqnL6b5Sg+5B7mB5OBLgzfHSJBLqnM0QkGHNuc6n
|
||||
8HKwYwHjAqIBZNG4p0f+BxFcMFOMt393uA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,35 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBaDCCAQ2gAwIBAgIUCY+goBAOhowBs7BHs/qXdAX8XFgwCgYIKoZIzj0EAwIw
|
||||
ETEPMA0GA1UEAwwGUm9vdENBMB4XDTI0MTIxOTEzNTY1OFoXDTM0MTIxNzEzNTY1
|
||||
OFowETEPMA0GA1UEAwwGU2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||
/qief3Kjnz0FpkQVaKRqJq3kHmCqqs+y1EGYLEZZAqLFvxmv7xoL6muG4Mj8tzqk
|
||||
Ll94JJuz97hG1FiEZsq7O6NDMEEwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsG
|
||||
AQUFBwMBMB0GA1UdDgQWBBTK/UPsN0I2ErVPILWKMRV6TSeAmTAKBggqhkjOPQQD
|
||||
AgNJADBGAiEA8EhlOwvTbwGlxo55UIOJp9LBbCp0BEIWojlu8PzOVSsCIQDlV24S
|
||||
3BUJVCuMRujO5lTfJLxaSKkOEIgRANwIGi88WA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBGzCBwgIUP/PGQOKa5EyvsIXNgvv9PNietyEwCgYIKoZIzj0EAwMwEDEOMAwG
|
||||
A1UEAwwFVFJVU1QwHhcNMjQxMjE5MTM1NjU4WhcNMzQxMjE3MTM1NjU4WjARMQ8w
|
||||
DQYDVQQDDAZSb290Q0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT+qJ5/cqOf
|
||||
PQWmRBVopGomreQeYKqqz7LUQZgsRlkCosW/Ga/vGgvqa4bgyPy3OqQuX3gkm7P3
|
||||
uEbUWIRmyrs7MAoGCCqGSM49BAMDA0gAMEUCIQD2DY3UDXbzaIBKrsCtohKlEunH
|
||||
ip9LkSeYfSKCnfm23gIgA8AEJdunpRmPkilxgy6wZSLLROqDpGDnhnyv8dsR8cc=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBLTCB1AIUcsuauXAqvIS2RQcNPYysETJUAvwwCgYIKoZIzj0EAwMwIzEhMB8G
|
||||
A1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTI0MTIxOTEzNTY1OFoX
|
||||
DTM0MTIxNzEzNTY1OFowEDEOMAwGA1UEAwwFVFJVU1QwWTATBgcqhkjOPQIBBggq
|
||||
hkjOPQMBBwNCAAT+qJ5/cqOfPQWmRBVopGomreQeYKqqz7LUQZgsRlkCosW/Ga/v
|
||||
Ggvqa4bgyPy3OqQuX3gkm7P3uEbUWIRmyrs7MAoGCCqGSM49BAMDA0gAMEUCIQCM
|
||||
DV8sfoArfWiXAUF2LNS3kkHD7sgb91jr2+poEHgBBgIgXf9VeJp3K5jHX6lJwtE8
|
||||
nd+jW7T9nhTc/5njHg7xons=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BggqhkjOPQMBBw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIB+Z69so6HqCCWo5VOFxGsLXOlTWIYijOtzt+SeNGrgPoAoGCCqGSM49
|
||||
AwEHoUQDQgAE/qief3Kjnz0FpkQVaKRqJq3kHmCqqs+y1EGYLEZZAqLFvxmv7xoL
|
||||
6muG4Mj8tzqkLl94JJuz97hG1FiEZsq7Ow==
|
||||
-----END EC PRIVATE KEY-----
|
||||
@ -17,7 +17,6 @@ package org.thingsboard.server.coapserver;
|
||||
|
||||
import org.eclipse.californium.core.CoapServer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ -25,5 +24,5 @@ public interface CoapServerService {
|
||||
|
||||
CoapServer getCoapServer() throws UnknownHostException;
|
||||
|
||||
ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getDtlsSessionsMap();
|
||||
ConcurrentMap<TbCoapDtlsSessionKey, TbCoapDtlsSessionInfo> getDtlsSessionsMap();
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ public class DefaultCoapServerService implements CoapServerService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getDtlsSessionsMap() {
|
||||
public ConcurrentMap<TbCoapDtlsSessionKey, TbCoapDtlsSessionInfo> getDtlsSessionsMap() {
|
||||
return tbDtlsCertificateVerifier != null ? tbDtlsCertificateVerifier.getTbCoapDtlsSessionsMap() : null;
|
||||
}
|
||||
|
||||
|
||||
@ -109,7 +109,8 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
|
||||
if (msg != null && strCert.equals(msg.getCredentials())) {
|
||||
DeviceProfile deviceProfile = msg.getDeviceProfile();
|
||||
if (msg.hasDeviceInfo() && deviceProfile != null) {
|
||||
tbCoapDtlsSessionInMemoryStorage.put(remotePeer, new TbCoapDtlsSessionInfo(msg, deviceProfile));
|
||||
TbCoapDtlsSessionKey tbCoapDtlsSessionKey = new TbCoapDtlsSessionKey(remotePeer, msg.getCredentials());
|
||||
tbCoapDtlsSessionInMemoryStorage.put(tbCoapDtlsSessionKey, new TbCoapDtlsSessionInfo(msg, deviceProfile));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -138,7 +139,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
|
||||
public void setResultHandler(HandshakeResultHandler resultHandler) {
|
||||
}
|
||||
|
||||
public ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionsMap() {
|
||||
public ConcurrentMap<TbCoapDtlsSessionKey, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionsMap() {
|
||||
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionsMap();
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,6 @@ package org.thingsboard.server.coapserver;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ -26,7 +25,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
@Data
|
||||
public class TbCoapDtlsSessionInMemoryStorage {
|
||||
|
||||
private final ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> dtlsSessionsMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<TbCoapDtlsSessionKey, TbCoapDtlsSessionInfo> dtlsSessionsMap = new ConcurrentHashMap<>();
|
||||
private long dtlsSessionInactivityTimeout;
|
||||
private long dtlsSessionReportTimeout;
|
||||
|
||||
@ -36,9 +35,9 @@ public class TbCoapDtlsSessionInMemoryStorage {
|
||||
this.dtlsSessionReportTimeout = dtlsSessionReportTimeout;
|
||||
}
|
||||
|
||||
public void put(InetSocketAddress remotePeer, TbCoapDtlsSessionInfo dtlsSessionInfo) {
|
||||
log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", remotePeer, dtlsSessionInfo.getLastActivityTime());
|
||||
dtlsSessionsMap.putIfAbsent(remotePeer, dtlsSessionInfo);
|
||||
public void put(TbCoapDtlsSessionKey tbCoapDtlsSessionKey, TbCoapDtlsSessionInfo dtlsSessionInfo) {
|
||||
log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", tbCoapDtlsSessionKey, dtlsSessionInfo.getLastActivityTime());
|
||||
dtlsSessionsMap.putIfAbsent(tbCoapDtlsSessionKey, dtlsSessionInfo);
|
||||
}
|
||||
|
||||
public void evictTimeoutSessions() {
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.coapserver;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
|
||||
public record TbCoapDtlsSessionKey(InetSocketAddress peerAddress, String credentials) {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TbCoapDtlsSessionKey that = (TbCoapDtlsSessionKey) o;
|
||||
return Objects.equals(peerAddress, that.peerAddress) &&
|
||||
Objects.equals(credentials, that.credentials);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,10 @@ 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.eclipse.californium.elements.EndpointContext;
|
||||
import org.eclipse.californium.elements.auth.X509CertPath;
|
||||
import org.thingsboard.server.coapserver.CoapServerService;
|
||||
import org.thingsboard.server.coapserver.TbCoapDtlsSessionKey;
|
||||
import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
|
||||
import org.thingsboard.server.common.adaptor.AdaptorException;
|
||||
import org.thingsboard.server.common.adaptor.JsonConverter;
|
||||
@ -47,12 +50,14 @@ import org.thingsboard.server.transport.coap.client.CoapClientContext;
|
||||
import org.thingsboard.server.transport.coap.client.TbCoapClientState;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import static org.eclipse.californium.elements.DtlsEndpointContext.KEY_SESSION_ID;
|
||||
|
||||
@ -65,7 +70,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
|
||||
private static final int FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST = 3;
|
||||
private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4;
|
||||
|
||||
private final ConcurrentMap<InetSocketAddress, TbCoapDtlsSessionInfo> dtlsSessionsMap;
|
||||
private final ConcurrentMap<TbCoapDtlsSessionKey, TbCoapDtlsSessionInfo> dtlsSessionsMap;
|
||||
private final long timeout;
|
||||
private final long piggybackTimeout;
|
||||
private final CoapClientContext clients;
|
||||
@ -177,11 +182,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
|
||||
|
||||
var dtlsSessionId = request.getSourceContext().get(KEY_SESSION_ID);
|
||||
if (dtlsSessionsMap != null && dtlsSessionId != null && !dtlsSessionId.isEmpty()) {
|
||||
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionsMap
|
||||
.computeIfPresent(request.getSourceContext().getPeerAddress(), (dtlsSessionIdStr, dtlsSessionInfo) -> {
|
||||
dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis());
|
||||
return dtlsSessionInfo;
|
||||
});
|
||||
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = this.getCoapDtlsSessionInfo(request.getSourceContext());
|
||||
if (tbCoapDtlsSessionInfo != null) {
|
||||
processRequest(exchange, type, request, tbCoapDtlsSessionInfo.getMsg(), tbCoapDtlsSessionInfo.getDeviceProfile());
|
||||
} else {
|
||||
@ -458,5 +459,32 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
|
||||
}
|
||||
}
|
||||
|
||||
private TbCoapDtlsSessionInfo getCoapDtlsSessionInfo(EndpointContext endpointContext) {
|
||||
InetSocketAddress peerAddress = endpointContext.getPeerAddress();
|
||||
String certPemStr = getCertPem(endpointContext);
|
||||
TbCoapDtlsSessionKey tbCoapDtlsSessionKey = StringUtils.isNotBlank(certPemStr) ? new TbCoapDtlsSessionKey(peerAddress, certPemStr) : null;
|
||||
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo;
|
||||
if (tbCoapDtlsSessionKey != null) {
|
||||
tbCoapDtlsSessionInfo = dtlsSessionsMap
|
||||
.computeIfPresent(tbCoapDtlsSessionKey, (dtlsSessionIdStr, dtlsSessionInfo) -> {
|
||||
dtlsSessionInfo.setLastActivityTime(System.currentTimeMillis());
|
||||
return dtlsSessionInfo;
|
||||
});
|
||||
} else {
|
||||
tbCoapDtlsSessionInfo = null;
|
||||
}
|
||||
return tbCoapDtlsSessionInfo;
|
||||
}
|
||||
|
||||
private String getCertPem(EndpointContext endpointContext) {
|
||||
try {
|
||||
X509CertPath certPath = (X509CertPath) endpointContext.getPeerIdentity();
|
||||
X509Certificate x509Certificate = (X509Certificate) certPath.getPath().getCertificates().get(0);
|
||||
return Base64.getEncoder().encodeToString(x509Certificate.getEncoded());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to get cert PEM: [{}]", endpointContext.getPeerAddress(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user