lwm2m - update lwm2mController by base64 and createte BootstrpServerCredentials by Profile (Base64 validate)

This commit is contained in:
nickAS21 2021-11-03 23:28:28 +02:00
parent 1dada1526f
commit bad6653a50
11 changed files with 272 additions and 22 deletions

View File

@ -17,6 +17,7 @@ package org.thingsboard.server.service.lwm2m;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.eclipse.leshan.core.util.Hex;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
@ -50,22 +51,26 @@ public class LwM2MServiceImpl implements LwM2MService {
bsServ.setPort(serverConfig.getPort());
bsServ.setSecurityHost(serverConfig.getSecureHost());
bsServ.setSecurityPort(serverConfig.getSecurePort());
bsServ.setServerPublicKey(getPublicKey(serverConfig));
byte[] publicKeyBase64 = getPublicKey(serverConfig);
if (publicKeyBase64 == null) {
bsServ.setServerPublicKey("");
} else {
bsServ.setServerPublicKey(Base64.encodeBase64String(getPublicKey(serverConfig)));
}
return bsServ;
}
private String getPublicKey(LwM2MSecureServerConfig config) {
private byte[] getPublicKey(LwM2MSecureServerConfig config) {
try {
KeyStore keyStore = serverConfig.getKeyStoreValue();
if (keyStore != null) {
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(config.getCertificateAlias());
return Hex.encodeHexString(serverCertificate.getPublicKey().getEncoded());
return serverCertificate.getPublicKey().getEncoded();
}
} catch (Exception e) {
log.trace("Failed to fetch public key from key store!", e);
}
return "";
return null;
}
}

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.device.data.lwm2m;
import lombok.Data;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.ServerCredentials;
import java.util.Map;
@ -24,7 +25,7 @@ public class BootstrapConfiguration {
//TODO: define the objects;
private Map<String, Object> servers;
private Map<String, Object> lwm2mServer;
private Map<String, Object> bootstrapServer;
private ServerCredentials lwm2mServer;
private ServerCredentials bootstrapServer;
}

View File

@ -0,0 +1,38 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
@Getter
@Setter
public abstract class AbstractServerCredentials extends ServerSecurityConfig implements ServerCredentials {
@JsonIgnore
public byte[] getDecodedCServerPublicKey() {
return getDecoded(serverPublicKey);
}
@SneakyThrows
private static byte[] getDecoded(String key) {
return Base64.decodeBase64(key.getBytes());
}
}

View File

@ -0,0 +1,25 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
public class NoSecServerCredentials extends AbstractServerCredentials{
@Override
public LwM2MSecurityMode getSecurityMode() {
return LwM2MSecurityMode.NO_SEC;
}
}

View File

@ -0,0 +1,25 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
public class PSKServerCredentials extends AbstractServerCredentials{
@Override
public LwM2MSecurityMode getSecurityMode() {
return LwM2MSecurityMode.PSK;
}
}

View File

@ -0,0 +1,25 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
public class RPKServerCredentials extends AbstractServerCredentials{
@Override
public LwM2MSecurityMode getSecurityMode() {
return LwM2MSecurityMode.RPK;
}
}

View File

@ -0,0 +1,27 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import lombok.Data;
@Data
public class ServerConfig {
private Integer shortId = 123;
private Integer lifetime = 300;
private Integer defaultMinPeriod = 1;
private boolean notifIfDisabled = true;
private String binding = "U";
}

View File

@ -0,0 +1,37 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "securityMode")
@JsonSubTypes({
@JsonSubTypes.Type(value = NoSecServerCredentials.class, name = "NO_SEC"),
@JsonSubTypes.Type(value = PSKServerCredentials.class, name = "PSK"),
@JsonSubTypes.Type(value = RPKServerCredentials.class, name = "RPK"),
@JsonSubTypes.Type(value = X509ServerCredentials.class, name = "X509")
})
@JsonIgnoreProperties(ignoreUnknown = true)
public interface ServerCredentials {
@JsonIgnore
LwM2MSecurityMode getSecurityMode();
}

View File

@ -0,0 +1,25 @@
/**
* 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.common.data.device.profile.lwm2m.bootstrap;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
public class X509ServerCredentials extends AbstractServerCredentials{
@Override
public LwM2MSecurityMode getSecurityMode() {
return LwM2MSecurityMode.X509;
}
}

View File

@ -22,23 +22,23 @@ import lombok.Data;
@ApiModel
@Data
public class ServerSecurityConfig {
@ApiModelProperty(position = 1, value = "Is Bootstrap Server", example = "true", readOnly = true)
boolean bootstrapServerIs = true;
@ApiModelProperty(position = 1, value = "Is Bootstrap Server or Lwm2m Server", example = "true or false", readOnly = true)
protected boolean bootstrapServerIs = true;
@ApiModelProperty(position = 2, value = "Host for 'No Security' mode", example = "0.0.0.0", readOnly = true)
String host;
@ApiModelProperty(position = 3, value = "Port for 'No Security' mode", example = "5687", readOnly = true)
Integer port;
protected String host;
@ApiModelProperty(position = 3, value = "Port for Lwm2m Server: 'No Security' mode: Lwm2m Server or Bootstrap Server", example = "'5685' or '5687'", readOnly = true)
protected Integer port;
@ApiModelProperty(position = 4, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true)
String securityHost;
@ApiModelProperty(position = 5, value = "Port for 'Security' mode (DTLS)", example = "5688", readOnly = true)
Integer securityPort;
@ApiModelProperty(position = 5, value = "Server short Id", example = "111", readOnly = true)
Integer serverId = 111;
@ApiModelProperty(position = 7, value = "Client Hold Off Time", example = "1", readOnly = true)
Integer clientHoldOffTime = 1;
@ApiModelProperty(position = 8, value = "Server Public Key (base64 encoded)", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" +
protected String securityHost;
@ApiModelProperty(position = 5, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true)
protected Integer securityPort;
@ApiModelProperty(position = 6, value = "Server short Id", example = "111", readOnly = true)
protected Integer serverId = 111;
@ApiModelProperty(position = 7, value = "Client Hold Off Time. The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode. (This information is relevant for use with a Bootstrap-Server only.)", example = "1", readOnly = true)
protected Integer clientHoldOffTime = 1;
@ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" +
"+44sgk3c8g0LotfMpLlZJPhPwJ6ipXV+O1r7IZUjBs3LNA==", readOnly = true)
String serverPublicKey;
@ApiModelProperty(position = 9, value = "Bootstrap Server Account Timeout", example = "0", readOnly = true)
protected String serverPublicKey;
@ApiModelProperty(position = 9, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true)
Integer bootstrapServerAccountTimeout = 0;
}

View File

@ -28,6 +28,7 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import com.squareup.wire.schema.internal.parser.ProtoParser;
import com.squareup.wire.schema.internal.parser.TypeElement;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
@ -58,16 +59,21 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.RPKServerCredentials;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.ServerCredentials;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.X509ServerCredentials;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
@ -695,6 +701,42 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
}
private void validateLwm2mServersCredentialOfBootstrapForClient(ServerCredentials bootstrapServerConfig, String server) {
switch (bootstrapServerConfig.getSecurityMode()) {
case NO_SEC:
case PSK:
break;
case RPK:
RPKServerCredentials rpkServerCredentials = (RPKServerCredentials) bootstrapServerConfig;
if (StringUtils.isEmpty(rpkServerCredentials.getServerPublicKey())) {
throw new DeviceCredentialsValidationException(server + " RPK public key must be specified!");
}
try {
String pubkRpkSever = EncryptionUtil.pubkTrimNewLines(rpkServerCredentials.getServerPublicKey());
rpkServerCredentials.setServerPublicKey(pubkRpkSever);
SecurityUtil.publicKey.decode(rpkServerCredentials.getDecodedCServerPublicKey());
} catch (Exception e) {
throw new DeviceCredentialsValidationException(server + " RPK public key must be in standard [RFC7250] and then encoded to Base64 format!");
}
break;
case X509:
X509ServerCredentials x509ServerCredentials = (X509ServerCredentials) bootstrapServerConfig;
// X509BootstrapServerCredentials x509ServerCredentials = (X509BootstrapServerCredentials) bootstrapServerConfig;
if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) {
throw new DeviceCredentialsValidationException(server + " X509 public key must be specified!");
}
try {
String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey());
x509ServerCredentials.setServerPublicKey(certServer);
SecurityUtil.publicKey.decode(x509ServerCredentials.getDecodedCServerPublicKey());
} catch (Exception e) {
throw new DeviceCredentialsValidationException(server + " X509 public key must be in standard [RFC7250] and then encoded to Base64 format!");
}
break;
}
}
private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
new PaginatedRemover<TenantId, DeviceProfile>() {