Browse Source

Add unit tests and update regex pattern to convert chain to list

pull/7935/head
Andrii Landiak 4 years ago
parent
commit
e9586daf9d
  1. 85
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  2. 19
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  3. 174
      application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java
  4. 7
      application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
  5. 5
      application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestConfigProperties.java
  6. 1
      common/cluster-api/src/main/proto/queue.proto
  7. 6
      common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java
  8. 3
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
  9. 3
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/SslUtil.java
  10. 27
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

85
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -107,11 +107,9 @@ import org.thingsboard.server.service.resource.TbResourceService;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -243,41 +241,44 @@ public class DefaultTransportApiService implements TransportApiService {
} }
} }
private ListenableFuture<TransportApiResponseMsg> validateOrCreateDeviceX509Certificate(String certChain, DeviceCredentialsType credentialsType) { protected ListenableFuture<TransportApiResponseMsg> validateOrCreateDeviceX509Certificate(String certChain, DeviceCredentialsType credentialsType) {
List<String> chain = convertX509CertificateChainToList(certChain);
String updateDeviceCertificateValue = chain.get(0);
String updateDeviceCertificateHash = EncryptionUtil.getSha3Hash(updateDeviceCertificateValue);
String deviceCommonName = "";
try { try {
List<X509Certificate> chain = getX509CertificateChainFromString(certChain); deviceCommonName = SslUtil.parseCommonName(readCertFile(chain.get(0)));
String updateDeviceCertificateValue = SslUtil.getCertificateString(chain.get(0)); } catch (Exception ignored) {
String updateDeviceCertificateHash = EncryptionUtil.getSha3Hash(updateDeviceCertificateValue); }
for (String certificateValue : chain) {
for (X509Certificate cert: chain) { String certificateHash = EncryptionUtil.getSha3Hash(certificateValue);
String certificateValue = SslUtil.getCertificateString(cert); DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(certificateHash);
String certificateHash = EncryptionUtil.getSha3Hash(certificateValue); if (credentials != null && credentials.getCredentialsType() == credentialsType) {
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(certificateHash); return getDeviceInfo(credentials);
if (credentials != null && credentials.getCredentialsType() == credentialsType) { }
return getDeviceInfo(credentials); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash);
if (deviceProfile != null) {
String deviceName = extractDeviceNameFromCNByRegEx(deviceCommonName, deviceProfile.getCertificateRegexPattern());
if (deviceName == null) {
log.error("Device name cannot be unmatched from CN!");
return getEmptyTransportApiResponseFuture();
} }
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByCertificateHash(certificateHash); Device device = deviceService.findDeviceByTenantIdAndName(deviceProfile.getTenantId(), deviceName);
if (deviceProfile != null) { if (device != null) {
String deviceName = extractDeviceNameFromCNByRegEx(SslUtil.parseCommonName(chain.get(0)), deviceProfile.getCertificateRegexPattern()); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
Device device = deviceService.findDeviceByTenantIdAndName(deviceProfile.getTenantId(), deviceName); if (deviceCredentials != null && deviceCredentials.getCredentialsType() == credentialsType) {
if (device != null) { deviceCredentials = updateDeviceCredentials(device.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); } else if (deviceCredentials == null) {
if (deviceCredentials != null && deviceCredentials.getCredentialsType() == credentialsType) { deviceCredentials = createDeviceCredentials(device.getTenantId(), device.getId(), updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
deviceCredentials = updateDeviceCredentials(device.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
} else if (deviceCredentials == null) {
deviceCredentials = createDeviceCredentials(device.getTenantId(), device.getId(), updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
}
return getDeviceInfo(deviceCredentials);
} else if (deviceProfile.getProvisionType() == DeviceProfileProvisionType.ALLOW_CREATING_NEW_DEVICES_BY_X509_CERTIFICATE && deviceProfile.isAllowCreateNewDevicesByX509Strategy()) {
Device savedDevice = createDevice(deviceProfile.getTenantId(), deviceProfile.getId(), deviceName, deviceProfile.getName());
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getTenantId(), savedDevice.getId());
deviceCredentials = updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
return getDeviceInfo(deviceCredentials);
} }
return getDeviceInfo(deviceCredentials);
} else if (deviceProfile.getProvisionType() == DeviceProfileProvisionType.ALLOW_CREATING_NEW_DEVICES_BY_X509_CERTIFICATE && deviceProfile.isAllowCreateNewDevicesByX509Strategy()) {
Device savedDevice = createDevice(deviceProfile.getTenantId(), deviceProfile.getId(), deviceName, deviceProfile.getName());
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getTenantId(), savedDevice.getId());
deviceCredentials = updateDeviceCredentials(savedDevice.getTenantId(), deviceCredentials, updateDeviceCertificateValue, updateDeviceCertificateHash, credentialsType);
return getDeviceInfo(deviceCredentials);
} }
} }
} catch (CertificateEncodingException e) {
throw new RuntimeException(e);
} }
return getEmptyTransportApiResponseFuture(); return getEmptyTransportApiResponseFuture();
} }
@ -727,19 +728,17 @@ public class DefaultTransportApiService implements TransportApiService {
if (matcher.find()) { if (matcher.find()) {
return matcher.group(0); return matcher.group(0);
} }
return commonName; return null;
} }
private List<X509Certificate> getX509CertificateChainFromString(String certificateChain) { private List<String> convertX509CertificateChainToList(String certificateChain) {
List<X509Certificate> chain = new ArrayList<>(); List<String> chain = new ArrayList<>();
String[] test = Arrays.stream(certificateChain.split("-----BEGIN CERTIFICATE-----")).filter(e -> e.trim().length() > 0).map(EncryptionUtil::certTrimNewLines).toArray(String[]::new); String regex = "-----BEGIN CERTIFICATE-----\\s*.*?\\s*-----END CERTIFICATE-----";
Arrays.stream(test).forEach(s -> { Pattern pattern = Pattern.compile(regex);
try { Matcher matcher = pattern.matcher(certificateChain);
chain.add(readCertFile(s)); while (matcher.find()) {
} catch (Exception e) { chain.add(EncryptionUtil.certTrimNewLines(matcher.group()));
throw new RuntimeException(e); }
}
});
return chain; return chain;
} }

19
application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java

@ -298,6 +298,25 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
} }
@Test
public void testSaveDeviceProfileWithSameCertificateHash() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setCertificateHash("Certificate Hash");
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
deviceProfile2.setCertificateHash("Certificate Hash");
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Device profile with such certificate hash already exists";
doPost("/api/deviceProfile", deviceProfile2)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test @Test
public void testChangeDeviceProfileTypeNull() throws Exception { public void testChangeDeviceProfileTypeNull() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");

174
application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java

@ -0,0 +1,174 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.transport;
import com.google.common.util.concurrent.Futures;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceProvisionService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.resource.TbResourceService;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Slf4j
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DefaultTransportApiService.class)
public class DefaultTransportApiServiceTest {
@MockBean
protected TbDeviceProfileCache deviceProfileCache;
@MockBean
protected TbTenantProfileCache tenantProfileCache;
@MockBean
protected TbApiUsageStateService apiUsageStateService;
@MockBean
protected DeviceService deviceService;
@MockBean
protected DeviceProfileService deviceProfileService;
@MockBean
protected RelationService relationService;
@MockBean
protected DeviceCredentialsService deviceCredentialsService;
@MockBean
protected DbCallbackExecutorService dbCallbackExecutorService;
@MockBean
protected TbClusterService tbClusterService;
@MockBean
protected DataDecodingEncodingService dataDecodingEncodingService;
@MockBean
protected DeviceProvisionService deviceProvisionService;
@MockBean
protected TbResourceService resourceService;
@MockBean
protected OtaPackageService otaPackageService;
@MockBean
protected OtaPackageDataCache otaPackageDataCache;
@MockBean
protected QueueService queueService;
@SpyBean
DefaultTransportApiService service;
private final String deviceCertificate = "-----BEGIN CERTIFICATE-----Device certificate value-----END CERTIFICATE-----";
private final String deviceProfileCertificate = "-----BEGIN CERTIFICATE-----Device profile certificate value-----END CERTIFICATE-----";
@Test
public void validateExistingDeviceX509Certificate() {
var device = createDevice();
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
var deviceCredentials = createDeviceCredentials(deviceCertificate, device.getId());
when(deviceCredentialsService.findDeviceCredentialsByCredentialsId(any())).thenReturn(deviceCredentials);
service.validateOrCreateDeviceX509Certificate(deviceCertificate, DeviceCredentialsType.X509_CERTIFICATE);
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any());
}
@Test
public void updateExistingDeviceX509Certificate() {
var deviceProfile = createDeviceProfile(deviceProfileCertificate);
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
var device = createDevice();
when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device);
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
var deviceCredentials = createDeviceCredentials(deviceCertificate, device.getId());
when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials);
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
service.validateOrCreateDeviceX509Certificate(deviceProfileCertificate, DeviceCredentialsType.X509_CERTIFICATE);
verify(deviceProfileService, times(1)).findDeviceProfileByCertificateHash(any());
verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any());
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any());
verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any());
}
@Test
public void createDeviceByX509Provision() {
var deviceProfile = createDeviceProfile(deviceProfileCertificate);
when(deviceProfileService.findDeviceProfileByCertificateHash(any())).thenReturn(deviceProfile);
var device = createDevice();
when(deviceService.saveDevice(any())).thenReturn(device);
when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device));
var deviceCredentials = createDeviceCredentials(deviceCertificate, device.getId());
when(deviceCredentialsService.findDeviceCredentialsByDeviceId(any(), any())).thenReturn(deviceCredentials);
when(deviceCredentialsService.updateDeviceCredentials(any(), any())).thenReturn(deviceCredentials);
service.validateOrCreateDeviceX509Certificate(deviceProfileCertificate, DeviceCredentialsType.X509_CERTIFICATE);
verify(deviceProfileService, times(1)).findDeviceProfileByCertificateHash(any());
verify(deviceService, times(1)).findDeviceByTenantIdAndName(any(), any());
verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any());
verify(deviceCredentialsService, times(1)).updateDeviceCredentials(any(), any());
}
private DeviceCredentials createDeviceCredentials(String certificateValue, DeviceId deviceId) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(deviceId);
deviceCredentials.setCredentialsValue(certificateValue);
deviceCredentials.setCredentialsId(EncryptionUtil.getSha3Hash(certificateValue));
deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
return deviceCredentials;
}
private DeviceProfile createDeviceProfile(String certificateValue) {
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setCertificateValue(certificateValue);
deviceProfile.setCertificateHash(EncryptionUtil.getSha3Hash(certificateValue));
deviceProfile.setCertificateRegexPattern("^$");
deviceProfile.setAllowCreateNewDevicesByX509Strategy(true);
deviceProfile.setProvisionType(DeviceProfileProvisionType.ALLOW_CREATING_NEW_DEVICES_BY_X509_CERTIFICATE);
return deviceProfile;
}
private Device createDevice() {
Device device = new Device();
device.setId(new DeviceId(UUID.randomUUID()));
return device;
}
}

7
application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration;
import org.thingsboard.server.common.data.device.profile.AllowCreatingNewDevicesByX509CertificateProvisionConfiguration;
import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
@ -94,6 +95,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
deviceProfile.setProvisionType(provisionType); deviceProfile.setProvisionType(provisionType);
deviceProfile.setProvisionDeviceKey(config.getProvisionKey()); deviceProfile.setProvisionDeviceKey(config.getProvisionKey());
deviceProfile.setDescription(transportPayloadType.name() + " Test"); deviceProfile.setDescription(transportPayloadType.name() + " Test");
deviceProfile.setAllowCreateNewDevicesByX509Strategy(config.allowCreatingNewDeviceByX509Strategy);
deviceProfile.setCertificateValue("Device Profile certificate value");
deviceProfile.setCertificateRegexPattern(config.getRegEx());
DeviceProfileData deviceProfileData = new DeviceProfileData(); DeviceProfileData deviceProfileData = new DeviceProfileData();
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
@ -144,6 +148,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
case CHECK_PRE_PROVISIONED_DEVICES: case CHECK_PRE_PROVISIONED_DEVICES:
provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(config.getProvisionSecret()); provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(config.getProvisionSecret());
break; break;
case ALLOW_CREATING_NEW_DEVICES_BY_X509_CERTIFICATE:
provisionConfiguration = new AllowCreatingNewDevicesByX509CertificateProvisionConfiguration(config.getProvisionSecret());
break;
case DISABLED: case DISABLED:
default: default:
provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(config.getProvisionSecret()); provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(config.getProvisionSecret());

5
application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestConfigProperties.java

@ -40,9 +40,14 @@ public class MqttTestConfigProperties {
boolean enableCompatibilityWithJsonPayloadFormat; boolean enableCompatibilityWithJsonPayloadFormat;
boolean useJsonPayloadFormatForDefaultDownlinkTopics; boolean useJsonPayloadFormatForDefaultDownlinkTopics;
boolean sendAckOnValidationException; boolean sendAckOnValidationException;
boolean allowCreatingNewDeviceByX509Strategy;
DeviceProfileProvisionType provisionType; DeviceProfileProvisionType provisionType;
String provisionKey; String provisionKey;
String provisionSecret; String provisionSecret;
String x509DeviceCertificate;
String x509DeviceProfileCertificate;
String commonName;
String regEx;
} }

1
common/cluster-api/src/main/proto/queue.proto

@ -422,7 +422,6 @@ message CredentialsDataProto {
ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1; ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1;
ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2; ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2;
ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3; ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3;
ValidateOrCreateDeviceX509CertRequestMsg validateOrCreateDeviceX509CertRequestMsg = 4;
} }
message ProvisionDeviceRequestMsg { message ProvisionDeviceRequestMsg {

6
common/message/src/main/java/org/thingsboard/server/common/msg/EncryptionUtil.java

@ -35,9 +35,11 @@ public class EncryptionUtil {
.replaceAll("-----END CERTIFICATE-----", ""); .replaceAll("-----END CERTIFICATE-----", "");
} }
public static String certTrimNewLinesWithoutBeginEnd(String input) { public static String certTrimNewLinesForChainInDeviceProfile(String input) {
return input.replaceAll("\n", "") return input.replaceAll("\n", "")
.replaceAll("\r", ""); .replaceAll("\r", "")
.replaceAll("-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE-----\n")
.replaceAll("-----END CERTIFICATE-----", "\n-----END CERTIFICATE-----\n");
} }
public static String pubkTrimNewLines(String input) { public static String pubkTrimNewLines(String input) {

3
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java

@ -41,7 +41,6 @@ import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -168,7 +167,7 @@ public class MqttSslHandlerProvider {
if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) { if (!clientDeviceCertValue.equals(credentialsBodyHolder[0])) {
throw new CertificateException("Invalid Certificate's chain. Cannot find such device credentials."); throw new CertificateException("Invalid Certificate's chain. Cannot find such device credentials.");
} }
} catch (CertificateEncodingException | InterruptedException e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
} }
} }

3
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/SslUtil.java

@ -27,7 +27,6 @@ import org.thingsboard.server.common.msg.EncryptionUtil;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
/** /**
* @author Valerii Sosliuk * @author Valerii Sosliuk
@ -49,7 +48,7 @@ public class SslUtil {
String end = "-----END CERTIFICATE-----"; String end = "-----END CERTIFICATE-----";
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
for (Certificate cert: chain) { for (Certificate cert: chain) {
stringBuilder.append(begin).append(EncryptionUtil.certTrimNewLinesWithoutBeginEnd(Base64Utils.encodeToString(cert.getEncoded()))).append(end).append("\n"); stringBuilder.append(begin).append(EncryptionUtil.certTrimNewLines(Base64Utils.encodeToString(cert.getEncoded()))).append(end).append("\n");
} }
return stringBuilder.toString(); return stringBuilder.toString();
} }

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

@ -48,6 +48,10 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.Validator;
import java.io.ByteArrayInputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -342,17 +346,15 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
} }
private void formatDeviceProfileCertificate(DeviceProfile deviceProfile) { private void formatDeviceProfileCertificate(DeviceProfile deviceProfile) {
String certificateValue = deviceProfile.getCertificateValue(); String certificateValue = formatCertificateValue(deviceProfile.getCertificateValue());
String cert = regexCertificateChain(certificateValue); String cert = regexCertificateChain(certificateValue);
String sha3Hash = EncryptionUtil.getSha3Hash(cert); String sha3Hash = EncryptionUtil.getSha3Hash(cert);
deviceProfile.setCertificateValue(certificateValue);
deviceProfile.setCertificateHash(sha3Hash); deviceProfile.setCertificateHash(sha3Hash);
if (!isCertificateChain(certificateValue)) {
deviceProfile.setCertificateValue(EncryptionUtil.certTrimNewLines(certificateValue));
}
} }
private String regexCertificateChain(String chain) { private String regexCertificateChain(String chain) {
String regex = "-----BEGIN CERTIFICATE-----\\s*((.+\\s+)*?)-----END CERTIFICATE-----"; String regex = "-----BEGIN CERTIFICATE-----\\s*.*?\\s*-----END CERTIFICATE-----";
Pattern pattern = Pattern.compile(regex); Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(chain); Matcher matcher = pattern.matcher(chain);
if (matcher.find()) { if (matcher.find()) {
@ -361,9 +363,18 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
return chain; return chain;
} }
private boolean isCertificateChain(String certificateValue) { private String formatCertificateValue(String certificateValue) {
int count = certificateValue.split("-----BEGIN CERTIFICATE", -1).length - 1; try {
return count > 1; CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream inputStream = new ByteArrayInputStream(certificateValue.getBytes());
Certificate[] certificates = cf.generateCertificates(inputStream).toArray(new Certificate[0]);
if (certificates.length > 1) {
return EncryptionUtil.certTrimNewLinesForChainInDeviceProfile(certificateValue);
}
return EncryptionUtil.certTrimNewLines(certificateValue);
} catch (CertificateException e) {
throw new RuntimeException(e);
}
} }
} }

Loading…
Cancel
Save