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:
commit
a634b5669e
@ -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]);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user