Merge pull request #5897 from thingsboard/lwm2m_bootstrap_trust_cert

[3.3.3] lwm2m: add verify trust certificate to bootstrap
This commit is contained in:
Andrew Shvayka 2022-01-20 12:04:23 +02:00 committed by GitHub
commit a634b5669e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 307 additions and 152 deletions

View File

@ -26,6 +26,7 @@ import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.queue.util.TbLwM2mBootstrapTransportComponent;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.TbLwM2MDtlsBootstrapCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
@ -36,6 +37,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.security.cert.X509Certificate;
import static org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig;
@Slf4j
@ -50,6 +52,7 @@ public class LwM2MTransportBootstrapService {
private final LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore;
private final LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore;
private final TransportService transportService;
private final TbLwM2MDtlsBootstrapCertificateVerifier certificateVerifier;
private LeshanBootstrapServer server;
@PostConstruct
@ -75,8 +78,17 @@ public class LwM2MTransportBootstrapService {
/* Create CoAP Config */
builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
/* Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers());
dtlsConfig.setSupportedCipherSuites(this.pskMode ? DefaultLwM2mTransportService.PSK_CIPHER_SUITES : DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES);
/* Create credentials */
this.setServerWithCredentials(builder);
this.setServerWithCredentials(builder, dtlsConfig);
/* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
/* Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
@ -85,15 +97,6 @@ public class LwM2MTransportBootstrapService {
builder.setSecurityStore(lwM2MBootstrapSecurityStore);
/* Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers());
dtlsConfig.setSupportedCipherSuites(this.pskMode ? DefaultLwM2mTransportService.PSK_CIPHER_SUITES : DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES);
/* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore, lwM2MInMemoryBootstrapConfigStore, transportService);
builder.setSessionManager(sessionManager);
@ -101,18 +104,14 @@ public class LwM2MTransportBootstrapService {
return builder.build();
}
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) {
if (this.bootstrapConfig.getSslCredentials() != null) {
SslCredentials sslCredentials = this.bootstrapConfig.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain());
if (this.serverConfig.getTrustSslCredentials() != null) {
builder.setTrustedCertificates(this.serverConfig.getTrustSslCredentials().getTrustedCertificates());
} else {
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES);
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
} else {
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);

View File

@ -31,9 +31,7 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.Abstrac
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Data
@ -97,7 +95,6 @@ public class LwM2MBootstrapConfig implements Serializable {
byte[] secretKey = new byte[]{};
byte[] serverPublicKey = new byte[]{};
serverSecurity.serverId = serverCredential.getShortServerId();
log.info("serverId = [{}]", serverSecurity.serverId);
serverSecurity.securityMode = SecurityMode.valueOf(securityMode.name());
serverSecurity.bootstrapServer = serverCredential.isBootstrapServerIs();
if (!LwM2MSecurityMode.NO_SEC.equals(securityMode)) {
@ -112,22 +109,17 @@ public class LwM2MBootstrapConfig implements Serializable {
if (LwM2MSecurityMode.PSK.equals(securityMode)) {
publicKeyOrId = server.getClientPublicKeyOrId().getBytes();
secretKey = Hex.decodeHex(server.getClientSecretKey().toCharArray());
log.info("publicKeyOrId [{}]", new String(publicKeyOrId, StandardCharsets.UTF_8));
} else {
publicKeyOrId = server.getDecodedClientPublicKeyOrId();
secretKey = server.getDecodedClientSecretKey();
log.info("publicKeyOrId [{}]", Hex.encodeHexString(publicKeyOrId));
}
serverPublicKey = serverCredential.getDecodedCServerPublicKey();
}
log.info("secretKey [{}]", Hex.encodeHexString(secretKey));
serverUri += (((serverCredential.getHost().equals("0.0.0.0") ? "localhost" : serverCredential.getHost()) + ":" + serverCredential.getPort()));
log.info("serverSecurity.uri = [{}]", serverUri);
serverSecurity.uri = serverUri;
serverSecurity.publicKeyOrId = publicKeyOrId;
serverSecurity.secretKey = secretKey;
serverSecurity.serverPublicKey = serverPublicKey;
log.info("server [{}]", Hex.encodeHexString(serverSecurity.serverPublicKey));
return serverSecurity;
}

View File

@ -38,6 +38,7 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecu
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -83,15 +84,16 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
@Override
public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
boolean authorized = true;
Iterator<SecurityInfo> securityInfos;
Iterator<SecurityInfo> securityInfos = null;
try {
if (bsSecurityStore != null && securityChecker != null) {
if (clientIdentity.isSecure() && clientIdentity.isPSK()) {
securityInfos = bsSecurityStore.getAllByEndpoint(clientIdentity.getPskIdentity());
} else {
if (clientIdentity.isPSK()) {
SecurityInfo securityInfo = bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity());
securityInfos = Collections.singletonList(securityInfo).iterator();
} else if (!clientIdentity.isX509()) {
securityInfos = bsSecurityStore.getAllByEndpoint(request.getEndpointName());
}
authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
authorized = this.checkSecurityInfo(request.getEndpointName(), clientIdentity, securityInfos);
}
} catch (LwM2MAuthException e) {
authorized = false;
@ -187,7 +189,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
// store response
DefaultBootstrapSession session = (DefaultBootstrapSession) bsSession;
session.getResponses().add(response);
this.sendLogs (bsSession.getEndpoint(),
this.sendLogs(bsSession.getEndpoint(),
String.format("%s: %s %s receives error response %s ", LOG_LWM2M_INFO,
request.getClass().getSimpleName(),
request.getPath().toString(), response.toString()));
@ -195,7 +197,7 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
return BootstrapPolicy.continueWith(nextRequest(bsSession));
} else {
// on response error for bootstrap finish request we stop the session
this.sendLogs (bsSession.getEndpoint(),
this.sendLogs(bsSession.getEndpoint(),
String.format("%s: error response for request bootstrap finish. Stop the session: %s", LOG_LWM2M_ERROR, bsSession.toString()));
return BootstrapPolicy.failed();
}
@ -222,7 +224,16 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
}
private void sendLogs(String endpointName, String logMsg) {
log.info(logMsg);
log.info("Endpoint: [{}] [{}]", endpointName, logMsg);
transportService.log(((LwM2MBootstrapSecurityStore) bsSecurityStore).getSessionByEndpoint(endpointName), logMsg);
}
private boolean checkSecurityInfo(String endpoint, Identity clientIdentity, Iterator<SecurityInfo> securityInfos) {
if (clientIdentity.isX509()) {
return clientIdentity.getX509CommonName().equals(endpoint)
& ((LwM2MBootstrapSecurityStore) bsSecurityStore).getBootstrapConfigByEndpoint(endpoint) != null;
} else {
return securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos);
}
}
}

View File

@ -0,0 +1,167 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.util.CertPathUtil;
import org.eclipse.californium.scandium.dtls.AlertMessage;
import org.eclipse.californium.scandium.dtls.CertificateMessage;
import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
import org.eclipse.californium.scandium.dtls.ConnectionId;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.queue.util.TbLwM2mBootstrapTransportComponent;
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
import javax.annotation.PostConstruct;
import javax.security.auth.x500.X500Principal;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
@TbLwM2mBootstrapTransportComponent
@RequiredArgsConstructor
public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCertificateVerifier {
private final LwM2MTransportServerConfig config;
private final LwM2MBootstrapSecurityStore bsSecurityStore;
@SuppressWarnings("deprecation")
private StaticCertificateVerifier staticCertificateVerifier;
@Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert;
@Override
public List<CertificateType> getSupportedCertificateType() {
return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY);
}
@SuppressWarnings("deprecation")
@PostConstruct
public void init() {
try {
/* by default trust all */
if (config.getTrustSslCredentials() != null) {
X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates);
}
} catch (Exception e) {
log.info("Failed to initialize the certificate verifier", e);
}
}
@Override
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage,
boolean truncateCertificatePath, CertificateMessage message,
DTLSSession session) {
CertPath certChain = message.getCertificateChain();
if (certChain == null) {
//We trust all RPK on this layer, and use TbLwM2MAuthorizer
PublicKey publicKey = message.getPublicKey();
return new CertificateVerificationResult(cid, publicKey, null);
} else {
try {
boolean x509CredentialsFound = false;
X509Certificate[] chain = certChain.getCertificates().toArray(new X509Certificate[0]);
for (X509Certificate cert : chain) {
try {
if (!skipValidityCheckForClientCert) {
cert.checkValidity();
}
TbLwM2MSecurityInfo securityInfo = null;
// verify if trust
if (staticCertificateVerifier != null) {
try {
staticCertificateVerifier.verifyCertificate(message, session);
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN");
if (StringUtils.isNotEmpty(endpoint)) {
securityInfo = bsSecurityStore.getX509ByEndpoint(endpoint);
}
} catch (HandshakeException e) {
log.trace("Certificate validation failed.", e);
}
}
// if not trust or cert trust securityInfo == null
if (securityInfo == null || securityInfo.getMsg() == null) {
String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
try {
securityInfo = bsSecurityStore.getX509ByEndpoint(sha3Hash);
} catch (LwM2MAuthException e) {
log.trace("Failed to find security info: [{}]", sha3Hash, e);
}
}
ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
if (msg != null && StringUtils.isNotEmpty(msg.getCredentials())) {
x509CredentialsFound = true;
break;
}
} catch (CertificateEncodingException |
CertificateExpiredException |
CertificateNotYetValidException e) {
log.trace("Failed to find security info: [{}]", cert.getSubjectX500Principal().getName(), e);
}
}
if (!x509CredentialsFound) {
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR,
session.getPeer());
throw new HandshakeException("x509 verification not enabled!", alert);
}
return new CertificateVerificationResult(cid, certChain, null);
} catch (HandshakeException e) {
log.trace("Certificate validation failed!", e);
return new CertificateVerificationResult(cid, e, null);
}
}
}
@Override
public List<X500Principal> getAcceptedIssuers() {
return CertPathUtil.toSubjects(null);
}
@Override
public void setResultHandler(HandshakeResultHandler resultHandler) {
}
}

View File

@ -33,6 +33,7 @@ import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.util.Collections;
import java.util.Iterator;
@ -67,66 +68,48 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
}
@Override
public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
endPoint = store.getEndpoint();
BootstrapConfig bsConfigNew = store.getBootstrapConfig();
if (bsConfigNew != null) {
try {
boolean bootstrapServerUpdateEnable = ((Lwm2mDeviceProfileTransportConfiguration)store.getDeviceProfile().getProfileData().getTransportConfiguration()).isBootstrapServerUpdateEnable();
if (!bootstrapServerUpdateEnable) {
Optional<Map.Entry<Integer, BootstrapConfig.ServerSecurity>> securities = bsConfigNew.security.entrySet().stream().filter(sec -> sec.getValue().bootstrapServer).findAny();
if (securities.isPresent()) {
bsConfigNew.security.entrySet().remove(securities.get());
int serverSortId = securities.get().getValue().serverId;
Optional<Map.Entry<Integer, BootstrapConfig.ServerConfig>> serverConfigs = bsConfigNew.servers.entrySet().stream().filter(serv -> ((BootstrapConfig.ServerConfig)serv.getValue()).shortId==serverSortId).findAny();
if (serverConfigs.isPresent()) {
bsConfigNew.servers.entrySet().remove(serverConfigs.get());
}
}
}
for (String config : bootstrapConfigStore.getAll().keySet()) {
if (config.equals(endPoint)) {
bootstrapConfigStore.remove(config);
}
}
bootstrapConfigStore.add(endPoint, bsConfigNew);
} catch (InvalidConfigurationException e) {
if (e.getMessage().contains("Psk identity") && e.getMessage().contains("already used for this bootstrap server")) {
log.trace("Invalid Bootstrap Configuration", e);
}
else {
log.error("Invalid Bootstrap Configuration", e);
}
}
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
}
}
return null;
public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, BOOTSTRAP);
SecurityInfo securityInfo = this.addValueToStore(store, endpoint);
return securityInfo == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
}
@Override
public SecurityInfo getByIdentity(String identity) {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
BootstrapConfig bsConfig = store.getBootstrapConfig();
if (bsConfig.security != null) {
try {
bootstrapConfigStore.add(store.getEndpoint(), bsConfig);
} catch (InvalidConfigurationException e) {
log.error("Invalid Bootstrap Configuration", e);
try {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
BootstrapConfig bsConfig = store.getBootstrapConfig();
if (bsConfig.security != null) {
try {
bootstrapConfigStore.add(store.getEndpoint(), bsConfig);
} catch (InvalidConfigurationException e) {
log.trace("Invalid Bootstrap Configuration", e);
return null;
}
}
return store.getSecurityInfo();
}
return store.getSecurityInfo();
} catch (LwM2MAuthException e) {
log.trace("Bootstrap Registration failed: No pre-shared key found for [identity: {}]", identity);
return null;
}
}
public TbLwM2MSecurityInfo getX509ByEndpoint(String endPoint) {
try {
TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
this.addValueToStore(store, store.getEndpoint());
return store;
} catch (LwM2MAuthException e) {
log.trace("Failed find security info: {}", endPoint, e);
}
return null;
}
private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) {
/* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
@ -163,25 +146,23 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
*
* @return false if not sync between SecurityMode of Bootstrap credential and profile
*/
// private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
private boolean getValidatedSecurityMode(LwM2MBootstrapConfig lwM2MBootstrapConfig) {
LwM2MSecurityMode bootstrapServerSecurityMode = lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode();
LwM2MSecurityMode lwm2mServerSecurityMode = lwM2MBootstrapConfig.getLwm2mServer().getSecurityMode();
AtomicBoolean validBs = new AtomicBoolean(true);
AtomicBoolean validLw = new AtomicBoolean(true);
AtomicBoolean validLw = new AtomicBoolean(true);
lwM2MBootstrapConfig.getServerConfiguration().forEach(serverCredential -> {
if (((AbstractLwM2MBootstrapServerCredential)serverCredential).isBootstrapServerIs()) {
if (((AbstractLwM2MBootstrapServerCredential) serverCredential).isBootstrapServerIs()) {
if (!bootstrapServerSecurityMode.equals(serverCredential.getSecurityMode())) {
validBs.set(false);
}
}
else {
} else {
if (!lwm2mServerSecurityMode.equals(serverCredential.getSecurityMode())) {
validLw.set(false);
}
}
});
return validBs.get()&validLw.get();
return validBs.get() && validLw.get();
}
public TransportProtos.SessionInfoProto getSessionByEndpoint(String endpoint) {
@ -191,4 +172,47 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
public TransportProtos.SessionInfoProto removeSessionByEndpoint(String endpoint) {
return bsSessions.remove(endpoint);
}
}
public BootstrapConfig getBootstrapConfigByEndpoint(String endpoint) {
return bootstrapConfigStore.getAll().get(endpoint);
}
public SecurityInfo addValueToStore(TbLwM2MSecurityInfo store, String endpoint) {
/* add value to store from BootstrapJson */
SecurityInfo securityInfo = null;
if (store != null && store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
securityInfo = store.getSecurityInfo();
this.setBootstrapConfigScurityInfo(store);
BootstrapConfig bsConfigNew = store.getBootstrapConfig();
if (bsConfigNew != null) {
try {
boolean bootstrapServerUpdateEnable = ((Lwm2mDeviceProfileTransportConfiguration) store.getDeviceProfile().getProfileData().getTransportConfiguration()).isBootstrapServerUpdateEnable();
if (!bootstrapServerUpdateEnable) {
Optional<Map.Entry<Integer, BootstrapConfig.ServerSecurity>> securities = bsConfigNew.security.entrySet().stream().filter(sec -> sec.getValue().bootstrapServer).findAny();
if (securities.isPresent()) {
bsConfigNew.security.entrySet().remove(securities.get());
int serverSortId = securities.get().getValue().serverId;
Optional<Map.Entry<Integer, BootstrapConfig.ServerConfig>> serverConfigs = bsConfigNew.servers.entrySet().stream().filter(serv -> (serv.getValue()).shortId == serverSortId).findAny();
if (serverConfigs.isPresent()) {
bsConfigNew.servers.entrySet().remove(serverConfigs.get());
}
}
}
for (String config : bootstrapConfigStore.getAll().keySet()) {
if (config.equals(endpoint)) {
bootstrapConfigStore.remove(config);
}
}
bootstrapConfigStore.add(endpoint, bsConfigNew);
} catch (InvalidConfigurationException e) {
if (e.getMessage().contains("Psk identity") && e.getMessage().contains("already used for this bootstrap server")) {
log.trace("Invalid Bootstrap Configuration", e);
} else {
log.error("Invalid Bootstrap Configuration", e);
}
}
}
}
return securityInfo;
}
}

View File

@ -49,7 +49,6 @@ import static org.eclipse.leshan.core.SecurityMode.PSK;
import static org.eclipse.leshan.core.SecurityMode.RPK;
import static org.eclipse.leshan.core.SecurityMode.X509;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
@Slf4j
@Component
@ -89,7 +88,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0];
if ((CLIENT.equals(keyValue) && securityInfo.getSecurityMode() == null)) {
if (securityInfo.getSecurityMode() == null) {
throw new LwM2MAuthException();
}
return securityInfo;
@ -119,12 +118,11 @@ public class LwM2mCredentialsSecurityInfoValidator {
createClientSecurityInfoRPK(result, endpoint, credentials.getClient());
break;
case X509:
createClientSecurityInfoX509(result, endpoint, credentials.getClient());
createClientSecurityInfoX509(result, endpoint);
break;
default:
break;
}
// } else
if (keyValue.equals(BOOTSTRAP)) {
LwM2MBootstrapConfig bootstrapCredentialConfig = new LwM2MBootstrapConfig(((Lwm2mDeviceProfileTransportConfiguration) msg.getDeviceProfile().getProfileData().getTransportConfiguration()).getBootstrap(),
credentials.getBootstrap().getBootstrapServer(), credentials.getBootstrap().getLwm2mServer());
@ -173,7 +171,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
}
}
private void createClientSecurityInfoX509(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredential clientCredentialsConfig) {
private void createClientSecurityInfoX509(TbLwM2MSecurityInfo result, String endpoint) {
result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
result.setSecurityMode(X509);
}

View File

@ -29,7 +29,6 @@ import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -50,22 +49,13 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
import javax.annotation.PostConstruct;
import javax.security.auth.x500.X500Principal;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
@ -97,11 +87,11 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
public void init() {
try {
/* by default trust all */
X509Certificate[] trustedCertificates = new X509Certificate[0];
if (config.getTrustSslCredentials() != null) {
trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates);
}
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates);
} catch (Exception e) {
log.info("Failed to initialize the ");
}
@ -117,27 +107,29 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
} else {
try {
boolean x509CredentialsFound = false;
CertPath certpath = message.getCertificateChain();
X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]);
X509Certificate[] chain = certChain.getCertificates().toArray(new X509Certificate[0]);
for (X509Certificate cert : chain) {
try {
if (!skipValidityCheckForClientCert) {
cert.checkValidity();
}
TbLwM2MSecurityInfo securityInfo = null;
// verify if trust
if (config.getTrustSslCredentials() != null && config.getTrustSslCredentials().getTrustedCertificates().length > 0) {
if (verifyTrust(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) {
if (staticCertificateVerifier != null) {
try {
staticCertificateVerifier.verifyCertificate(message, session);
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN");
securityInfo = StringUtils.isNotEmpty(endpoint) ? securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT) : null;
if (StringUtils.isNotEmpty(endpoint)) {
securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT);
}
} catch (HandshakeException | LwM2MAuthException e) {
log.trace("Certificate trust validation failed.", e);
}
}
// if not trust or cert trust securityInfo == null
String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
if (securityInfo == null) {
if (securityInfo == null || securityInfo.getMsg() == null) {
try {
securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
} catch (LwM2MAuthException e) {
@ -145,7 +137,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
}
}
ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
if (msg != null && StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MClientCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MClientCredentials.class);
if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) {
continue;
@ -176,15 +168,11 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
}
}
if (!x509CredentialsFound) {
if (staticCertificateVerifier != null) {
staticCertificateVerifier.verifyCertificate(message, session);
} else {
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR,
session.getPeer());
throw new HandshakeException("x509 verification not enabled!", alert);
}
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR,
session.getPeer());
throw new HandshakeException("x509 verification not enabled!", alert);
}
return new CertificateVerificationResult(cid, certpath, null);
return new CertificateVerificationResult(cid, certChain, null);
} catch (HandshakeException e) {
log.trace("Certificate validation failed!", e);
return new CertificateVerificationResult(cid, e, null);
@ -201,27 +189,4 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
public void setResultHandler(HandshakeResultHandler resultHandler) {
}
private X509Certificate verifyTrust(X509Certificate certificate, X509Certificate[] certificates) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[]{certificate}));
for (int index = 0; index < certificates.length; ++index) {
X509Certificate caCert = certificates[index];
try {
TrustAnchor trustAnchor = new TrustAnchor(caCert, null);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParams = new PKIXParameters(
Collections.singleton(trustAnchor));
pkixParams.setRevocationEnabled(false);
if (cpv.validate(cp, pkixParams) != null) return certificate;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertPathValidatorException e) {
log.trace("[{}]. [{}]", certificate.getSubjectDN(), e.getMessage());
}
}
} catch (CertificateException e) {
log.trace("[{}] certPath not valid. [{}]", certificate.getSubjectDN(), e.getMessage());
}
return null;
}
}

View File

@ -23,7 +23,6 @@ import org.jetbrains.annotations.Nullable;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@ -75,8 +74,8 @@ public class TbLwM2mSecurityStore implements TbMainSecurityStore {
try {
securityInfo = fetchAndPutSecurityInfo(pskIdentity);
} catch (LwM2MAuthException e) {
log.info("Registration failed: FORBIDDEN, endpointId: [{}]", pskIdentity);
securityInfo = SecurityInfo.newPreSharedKeyInfo(pskIdentity, pskIdentity, new byte[]{0x00});
log.trace("Registration failed: No pre-shared key found for [identity: {}]", pskIdentity);
return null;
}
}
return securityInfo;