Browse Source

Lwm2m: backEnd: refactoring security

pull/3980/head
nickAS21 6 years ago
parent
commit
bc4684c907
  1. 176
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java
  2. 74
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  3. 205
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java
  4. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java
  5. 45
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java
  6. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java
  7. 66
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java
  8. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java
  9. 221
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java
  10. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java
  11. 1098
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java
  12. 1132
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java
  13. 17
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  14. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClient.java
  15. 241
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java
  16. 100
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java
  17. 1
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java
  18. 7
      netty-mqtt/pom.xml
  19. 2
      pom.xml

176
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java

@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.core.model.StaticModel;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.bootstrap.BootstrapSessionManager;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
@ -28,18 +29,40 @@ import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MSetSecurityStoreBootstrap;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.util.Arrays;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true'&& '${transport.lwm2m.bootstrap.enable}'=='true')")
public class LwM2MTransportBootstrapServerConfiguration {
private PublicKey publicKey;
private PrivateKey privateKey;
@Autowired
private LwM2MTransportContextBootstrap contextBs;
@ -90,7 +113,7 @@ public class LwM2MTransportBootstrapServerConfiguration {
builder.setDtlsConfig(dtlsConfig);
/** Create credentials */
new LwM2MSetSecurityStoreBootstrap(builder, contextBs, contextS, dtlsMode);
LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
builder.setSessionManager(sessionManager);
@ -98,4 +121,151 @@ public class LwM2MTransportBootstrapServerConfiguration {
/** Create BootstrapServer */
return builder.build();
}
public void LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MSecurityMode dtlsMode) {
/** Set securityStore with new registrationStore */
switch (dtlsMode) {
/** Use No_Sec only */
case NO_SEC:
setServerWithX509Cert(builder, NO_SEC.code);
break;
/** Use PSK/RPK */
case PSK:
case RPK:
setRPK(builder);
break;
case X509:
setServerWithX509Cert(builder, X509.code);
break;
/** Use X509_EST only */
case X509_EST:
// TODO support sentinel pool and make pool configurable
break;
/** Use ather X509, PSK, No_Sec ?? */
default:
break;
}
}
private void setRPK(LeshanBootstrapServerBuilder builder) {
try {
/** Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
/** Get point values */
byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
/** Create key specs */
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
/** Get keys */
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
}
if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPublicKey(this.publicKey);
builder.setPrivateKey(this.privateKey);
this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
getParamsRPK();
}
} catch (GeneralSecurityException | IllegalArgumentException e) {
log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
throw new RuntimeException(e);
}
}
private void setServerWithX509Cert(LeshanBootstrapServerBuilder builder, int securityModeCode) {
try {
if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
setBuilderX509(builder);
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
if (rootCAX509Cert != null && securityModeCode == X509.code) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
}
else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.error("Unable to load X509 files for BootStrapServer");
}
} catch (KeyStoreException ex) {
log.error("[{}] Unable to load X509 files server", ex.getMessage());
}
}
private void setBuilderX509(LeshanBootstrapServerBuilder builder) {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPrivateKey(this.privateKey);
}
if (serverCertificate != null) {
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);
infoParamsX509(serverCertificate);
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
}
}
private void getParamsRPK() {
if (this.publicKey instanceof ECPublicKey) {
/** Get x coordinate */
byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */
byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */
String params = ((ECPublicKey) this.publicKey).getParams().toString();
log.info(
" \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
params, Hex.encodeHexString(x), Hex.encodeHexString(y),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
} else {
throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
}
}
private void infoParamsX509(X509Certificate certificate) {
try {
log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
Hex.encodeHexString(certificate.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
} catch (CertificateEncodingException e) {
log.error("", e);
}
}
}

74
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java

@ -29,10 +29,10 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2MSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
@ -53,14 +53,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getBootstrapParametersFromThingsboard;
@Slf4j
@Component("LwM2MBootstrapSecurityStore")
@Service("LwM2MBootstrapSecurityStore")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true' && '${transport.lwm2m.bootstrap.enable}'=='true')")
public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
private final EditableBootstrapConfigStore bootstrapConfigStore;
@Autowired
LwM2MGetSecurityInfo lwM2MGetSecurityInfo;
LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
@Autowired
public LwM2MTransportContextServer context;
@ -72,8 +72,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) {
String endPointKey = endPoint;
ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null) {
ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(endPointKey, TypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
BootstrapConfig bsConfigNew = store.getBootstrapConfig();
@ -86,7 +86,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
}
bootstrapConfigStore.add(endPoint, bsConfigNew);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
log.error("", e);
}
return store.getSecurityInfo() == null ? null : Arrays.asList(store.getSecurityInfo());
}
@ -96,17 +96,16 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override
public SecurityInfo getByIdentity(String identity) {
ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.BOOTSTRAP);
/** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store);
if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** 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) {
e.printStackTrace();
log.error("", e);
}
return store.getSecurityInfo();
}
@ -154,33 +153,36 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
private LwM2MBootstrapConfig getParametersBootstrap(ReadResultSecurityStore store) {
try {
JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential();
ObjectMapper mapper = new ObjectMapper();
LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
UUID sessionUUiD = UUID.randomUUID();
TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
return lwM2MBootstrapConfig;
}
else {
log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
return null;
if (bootstrapJsonCredential != null) {
ObjectMapper mapper = new ObjectMapper();
LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
UUID sessionUUiD = UUID.randomUUID();
TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2MSessionMsgListener(null, sessionInfo));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
String logMsg = String.format(LOG_LW2M_INFO + ": getParametersBootstrap: %s Access connect client with bootstrap server.", store.getEndPoint());
context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
return lwM2MBootstrapConfig;
} else {
log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
log.error(LOG_LW2M_ERROR + " getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
String logMsg = String.format(LOG_LW2M_ERROR + ": getParametersBootstrap: %s Different values SecurityMode between of client and profile.", store.getEndPoint());
context.sentParametersOnThingsboard(context.getTelemetryMsgObject(logMsg), LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, sessionInfo);
return null;
}
}
} catch (JsonProcessingException e) {
log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
return null;
}
log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint());
return null;
}
/**
@ -193,7 +195,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
* @return false if not sync between SecurityMode of Bootstrap credential and profile
*/
private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
}
}

205
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MSetSecurityStoreBootstrap.java

@ -1,205 +0,0 @@
/**
* Copyright © 2016-2020 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.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.LwM2MTransportContextBootstrap;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.ECPoint;
import java.security.spec.KeySpec;
import java.security.spec.ECPrivateKeySpec;
import java.util.Arrays;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
@Slf4j
@Data
public class LwM2MSetSecurityStoreBootstrap {
private KeyStore keyStore;
private PublicKey publicKey;
private PrivateKey privateKey;
private LwM2MTransportContextBootstrap contextBs;
private LwM2MTransportContextServer contextS;
private LeshanBootstrapServerBuilder builder;
EditableSecurityStore securityStore;
public LwM2MSetSecurityStoreBootstrap(LeshanBootstrapServerBuilder builder, LwM2MTransportContextBootstrap contextBs, LwM2MTransportContextServer contextS, LwM2MSecurityMode dtlsMode) {
this.builder = builder;
this.contextBs = contextBs;
this.contextS = contextS;
/** Set securityStore with new registrationStore */
switch (dtlsMode) {
/** Use No_Sec only */
case NO_SEC:
setServerWithX509Cert(NO_SEC.code);
break;
/** Use PSK/RPK */
case PSK:
case RPK:
setRPK();
break;
case X509:
setServerWithX509Cert(X509.code);
break;
/** Use X509_EST only */
case X509_EST:
// TODO support sentinel pool and make pool configurable
break;
/** Use ather X509, PSK, No_Sec ?? */
default:
break;
}
}
private void setRPK() {
try {
/** Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.contextBs.getCtxBootStrap().getBootstrapPrivateS() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateS().isEmpty()) {
/** Get point values */
byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateS().toCharArray());
/** Create key specs */
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
/** Get keys */
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
}
if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
this.privateKey != null && this.privateKey.getEncoded().length > 0) {
this.builder.setPublicKey(this.publicKey);
this.builder.setPrivateKey(this.privateKey);
this.contextBs.getCtxBootStrap().setBootstrapPublicKey(this.publicKey);
getParamsRPK();
}
} catch (GeneralSecurityException | IllegalArgumentException e) {
log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
throw new RuntimeException(e);
}
}
private void setServerWithX509Cert(int securityModeCode) {
try {
if (this.contextS.getCtxServer().getKeyStoreValue() != null) {
KeyStore keyStoreServer = this.contextS.getCtxServer().getKeyStoreValue();
setBuilderX509();
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getCtxServer().getRootAlias());
if (rootCAX509Cert != null && securityModeCode == X509.code) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
this.builder.setTrustedCertificates(trustedCertificates);
} else {
/** by default trust all */
this.builder.setTrustedCertificates(new X509Certificate[0]);
}
}
else {
/** by default trust all */
this.builder.setTrustedCertificates(new X509Certificate[0]);
log.error("Unable to load X509 files for BootStrapServer");
}
} catch (KeyStoreException ex) {
log.error("[{}] Unable to load X509 files server", ex.getMessage());
}
}
private void setBuilderX509() {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.contextS.getCtxServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias());
this.privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
this.builder.setPrivateKey(this.privateKey);
}
if (serverCertificate != null) {
this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});
this.contextBs.getCtxBootStrap().setBootstrapCertificate(serverCertificate);
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
}
}
private void getParamsRPK() {
if (this.publicKey instanceof ECPublicKey) {
/** Get x coordinate */
byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */
byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */
String params = ((ECPublicKey) this.publicKey).getParams().toString();
log.info(
" \nBootstrap uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
params, Hex.encodeHexString(x), Hex.encodeHexString(y),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
} else {
throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
}
}
// private void getParamsX509() {
// try {
// log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
// Hex.encodeHexString(this.certificate.getEncoded()),
// Hex.encodeHexString(this.privateKey.getEncoded()));
// } catch (CertificateEncodingException e) {
// e.printStackTrace();
// }
// }
}

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LWM2MGenerationPSkRPkECC.java

@ -73,15 +73,15 @@ public class LWM2MGenerationPSkRPkECC {
try {
kpg = KeyPairGenerator.getInstance(algorithm, provider);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
log.error("", e);
} catch (NoSuchProviderException e) {
e.printStackTrace();
log.error("", e);
}
ECGenParameterSpec ecsp = new ECGenParameterSpec(nameParameterSpec);
try {
kpg.initialize(ecsp);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
log.error("", e);
}
KeyPair kp = kpg.genKeyPair();

45
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2MGetSecurityInfo.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mValidateCredentialsSecurityInfo.java

@ -44,11 +44,10 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PS
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
@Slf4j
@Component("LwM2MGetSecurityInfo")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2MGetSecurityInfo {
public class LwM2mValidateCredentialsSecurityInfo {
@Autowired
public LwM2MTransportContextServer contextS;
@ -57,7 +56,13 @@ public class LwM2MGetSecurityInfo {
public LwM2MTransportContextBootstrap contextBS;
public ReadResultSecurityStore getSecurityInfo(String endPoint, TypeServer keyValue) {
/**
* Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials
* @param endPoint -
* @param keyValue -
* @return ValidateDeviceCredentialsResponseMsg and SecurityInfo
*/
public ReadResultSecurityStore validateCredentialsSecurityInfo(String endPoint, TypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1);
final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endPoint).build(),
@ -65,7 +70,7 @@ public class LwM2MGetSecurityInfo {
@Override
public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
String credentialsBody = msg.getCredentialsBody();
resultSecurityStore[0] = putSecurityInfo(endPoint, msg.getDeviceInfo().getDeviceName(), credentialsBody, keyValue);
resultSecurityStore[0] = createSecurityInfo(endPoint, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg);
Optional<DeviceProfile> deviceProfileOpt = LwM2MTransportHandler.decode(msg.getProfileBody().toByteArray());
deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
@ -74,20 +79,27 @@ public class LwM2MGetSecurityInfo {
@Override
public void onError(Throwable e) {
log.trace("[{}] Failed to process credentials PSK: {}", endPoint, e);
resultSecurityStore[0] = putSecurityInfo(endPoint, null, null, null);
log.trace("[{}] [{}] Failed to process credentials PSK ", endPoint, e.toString());
resultSecurityStore[0] = createSecurityInfo(endPoint, null, null);
latch.countDown();
}
});
try {
latch.await(contextS.getCtxServer().getTimeout(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
log.error("", e);
}
return resultSecurityStore[0];
}
private ReadResultSecurityStore putSecurityInfo(String endPoint, String deviceName, String jsonStr, TypeServer keyValue) {
/**
* Create new SecurityInfo
* @param endPoint -
* @param jsonStr -
* @param keyValue -
* @return SecurityInfo
*/
private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, TypeServer keyValue) {
ReadResultSecurityStore result = new ReadResultSecurityStore();
JsonObject objectMsg = LwM2MTransportHandler.validateJson(jsonStr);
if (objectMsg != null && !objectMsg.isJsonNull()) {
@ -103,20 +115,21 @@ public class LwM2MGetSecurityInfo {
if (keyValue.equals(TypeServer.BOOTSTRAP)) {
result.setBootstrapJsonCredential(object);
result.setEndPoint(endPoint);
result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
} else {
LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
switch (lwM2MSecurityMode) {
case NO_SEC:
getClientSecurityInfoNoSec(result);
createClientSecurityInfoNoSec(result);
break;
case PSK:
getClientSecurityInfoPSK(result, endPoint, object);
createClientSecurityInfoPSK(result, endPoint, object);
break;
case RPK:
getClientSecurityInfoRPK(result, endPoint, object);
createClientSecurityInfoRPK(result, endPoint, object);
break;
case X509:
getClientSecurityInfoX509(result, endPoint);
createClientSecurityInfoX509(result, endPoint);
break;
default:
break;
@ -127,12 +140,12 @@ public class LwM2MGetSecurityInfo {
return result;
}
private void getClientSecurityInfoNoSec(ReadResultSecurityStore result) {
private void createClientSecurityInfoNoSec(ReadResultSecurityStore result) {
result.setSecurityInfo(null);
result.setSecurityMode(NO_SEC.code);
}
private void getClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
private void createClientSecurityInfoPSK(ReadResultSecurityStore result, String endPoint, JsonObject object) {
/** PSK Deserialization */
String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
if (identity != null && !identity.isEmpty()) {
@ -152,7 +165,7 @@ public class LwM2MGetSecurityInfo {
}
}
private void getClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
private void createClientSecurityInfoRPK(ReadResultSecurityStore result, String endpoint, JsonObject object) {
try {
if (object.has("key") && object.get("key").isJsonPrimitive()) {
byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
@ -167,7 +180,7 @@ public class LwM2MGetSecurityInfo {
}
}
private void getClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
private void createClientSecurityInfoX509(ReadResultSecurityStore result, String endpoint) {
result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
result.setSecurityMode(X509.code);
}

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSessionMsgListener.java

@ -33,10 +33,10 @@ import java.util.Optional;
@Slf4j
public class LwM2MSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
private LwM2MTransportService service;
private LwM2MTransportServiceImpl service;
private TransportProtos.SessionInfoProto sessionInfo;
public LwM2MSessionMsgListener(LwM2MTransportService service, TransportProtos.SessionInfoProto sessionInfo) {
public LwM2MSessionMsgListener(LwM2MTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
this.service = service;
this.sessionInfo = sessionInfo;
}

66
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportHandler.java

@ -31,20 +31,14 @@ import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@ -53,8 +47,8 @@ import java.util.LinkedList;
import java.util.Optional;
@Slf4j
@Component("LwM2MTransportHandler")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
//@Component("LwM2MTransportHandler")
//@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2MTransportHandler {
// We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
@ -114,32 +108,38 @@ public class LwM2MTransportHandler {
public static final String SERVICE_CHANNEL = "SERVICE";
public static final String RESPONSE_CHANNEL = "RESP";
@Autowired
@Qualifier("LeshanServerCert")
private LeshanServer lhServerCert;
// @Autowired
// @Qualifier("LeshanServerCert")
// private LeshanServer lhServerCert;
//
// @Autowired
// @Qualifier("LeshanServerNoSecPskRpk")
// private LeshanServer lhServerNoSecPskRpk;
@Autowired
@Qualifier("leshanServerNoSecPskRpk")
private LeshanServer lhServerNoSecPskRpk;
// @Autowired
// @Qualifier("ServerListenerCert")
// private LwM2mServerListener serverListenerCert;
//
// @Autowired
// @Qualifier("ServerListenerNoSecPskRpk")
// private LwM2mServerListener serverListenerNoSecPskRpk;
@Autowired
private LwM2MTransportService service;
@PostConstruct
public void init() {
try {
LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);
this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);
lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);
this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);
} catch (java.lang.NullPointerException e) {
log.error("init [{}]", e.toString());
}
}
// @PostConstruct
// public void init() {
// try {
// serverListenerCert.init(lhServerCert);
// this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
// this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
// this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
// serverListenerNoSecPskRpk.init(lhServerNoSecPskRpk);
// this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
// this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
// this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
// } catch (Exception e) {
// log.error("init [{}]", e.toString());
// }
// }
public static NetworkConfig getCoapConfig() {
NetworkConfig coapConfig;
@ -217,7 +217,7 @@ public class LwM2MTransportHandler {
JsonObject objectMsg = (observeAttrStr != null) ? validateJson(observeAttrStr) : null;
return (getValidateCredentialsBodyFromThingsboard(objectMsg)) ? objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject() : null;
} catch (IOException e) {
e.printStackTrace();
log.error("", e);
}
}
return null;
@ -232,7 +232,7 @@ public class LwM2MTransportHandler {
JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null;
return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null;
} catch (IOException e) {
e.printStackTrace();
log.error("", e);
}
}
return null;

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportRequest.java

@ -86,7 +86,7 @@ public class LwM2MTransportRequest {
private LwM2mValueConverterImpl converter;
@Autowired
LwM2MTransportService service;
LwM2MTransportServiceImpl service;
@PostConstruct

221
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerConfiguration.java

@ -20,10 +20,16 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
import org.eclipse.leshan.core.node.codec.LwM2mNodeDecoder;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.model.VersionedModelProvider;
import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.RedisSecurityStore;
import org.eclipse.leshan.server.security.DefaultAuthorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
@ -31,11 +37,33 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.server.secure.LwM2MSetSecurityStoreServer;
import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.util.Pool;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.util.Arrays;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler.getCoapConfig;
@ -45,6 +73,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
@Configuration("LwM2MTransportServerConfiguration")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2MTransportServerConfiguration {
private PublicKey publicKey;
private PrivateKey privateKey;
@Autowired
private LwM2MTransportContextServer context;
@ -52,14 +82,26 @@ public class LwM2MTransportServerConfiguration {
@Autowired
private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
@Bean
public LwM2mServerListener lwM2mServerListenerCert() {
return new LwM2mServerListener();
}
@Bean
public LwM2mServerListener lwM2mServerListenerNoSecPskRpk() {
return new LwM2mServerListener();
}
@Primary
@Bean(name = "LeshanServerCert")
public LeshanServer getLeshanServerCert() {
log.info("Starting LwM2M transport ServerCert... PostConstruct");
return getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
LeshanServer leshanServerCert = getLeshanServer(this.context.getCtxServer().getServerPortCert(), this.context.getCtxServer().getServerSecurePortCert(), X509);
return leshanServerCert;
}
@Bean(name = "leshanServerNoSecPskRpk")
@Bean(name = "LeshanServerNoSecPskRpk")
public LeshanServer getLeshanServerNoSecPskRpk() {
log.info("Starting LwM2M transport ServerNoSecPskRpk... PostConstruct");
return getLeshanServer(this.context.getCtxServer().getServerPort(), this.context.getCtxServer().getServerSecurePort(), RPK);
@ -94,9 +136,178 @@ public class LwM2MTransportServerConfiguration {
/** Create DTLS security mode
* There can be only one DTLS security mode
*/
new LwM2MSetSecurityStoreServer(builder, context, lwM2mInMemorySecurityStore, dtlsMode);
this.LwM2MSetSecurityStoreServer(builder, dtlsMode);
/** Create LWM2M server */
return builder.build();
}
private void LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MSecurityMode dtlsMode) {
/** Set securityStore with new registrationStore */
EditableSecurityStore securityStore = lwM2mInMemorySecurityStore;
switch (dtlsMode) {
/** Use PSK only */
case PSK:
generatePSK_RPK();
if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPrivateKey(this.privateKey);
builder.setPublicKey(null);
infoParamsPSK();
}
break;
/** Use RPK only */
case RPK:
generatePSK_RPK();
if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPublicKey(this.publicKey);
builder.setPrivateKey(this.privateKey);
infoParamsRPK();
}
break;
/** Use x509 only */
case X509:
setServerWithX509Cert(builder);
break;
/** No security */
case NO_SEC:
builder.setTrustedCertificates(new X509Certificate[0]);
break;
/** Use x509 with EST */
case X509_EST:
// TODO support sentinel pool and make pool configurable
break;
case REDIS:
/**
* Set securityStore with new registrationStore (if use redis store)
* Connect to redis
*/
Pool<Jedis> jedis = null;
try {
jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
securityStore = new RedisSecurityStore(jedis);
builder.setRegistrationStore(new RedisRegistrationStore(jedis));
} catch (URISyntaxException e) {
log.error("", e);
}
break;
default:
}
/** Set securityStore with registrationStore (if x509)*/
if (dtlsMode == X509) {
builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
@Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
String expectedX509CommonName) {
return endpoint.startsWith(expectedX509CommonName);
}
}));
}
/** Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore);
}
private void generatePSK_RPK() {
try {
/** Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
/** Get point values */
byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
/** Create key specs */
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
/** Get keys */
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
}
} catch (GeneralSecurityException | IllegalArgumentException e) {
log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
throw new RuntimeException(e);
}
}
private void infoParamsPSK() {
log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
Hex.encodeHexString(this.privateKey.getEncoded()),
this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));
}
private void infoParamsRPK() {
if (this.publicKey instanceof ECPublicKey) {
/** Get x coordinate */
byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */
byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */
String params = ((ECPublicKey) this.publicKey).getParams().toString();
log.info(
" \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
params, Hex.encodeHexString(x), Hex.encodeHexString(y),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
} else {
throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
}
}
private void setServerWithX509Cert(LeshanServerBuilder builder) {
try {
if (this.context.getCtxServer().getKeyStoreValue() != null) {
setBuilderX509(builder);
X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.error("Unable to load X509 files for LWM2MServer");
}
} catch (KeyStoreException ex) {
log.error("[{}] Unable to load X509 files server", ex.getMessage());
}
}
private void setBuilderX509(LeshanServerBuilder builder) {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
}
}
}

47
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServerInitializer.java

@ -20,14 +20,15 @@ import org.eclipse.leshan.server.californium.LeshanServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Component;
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Slf4j
@Service("LwM2MTransportServerInitializer")
@Component("LwM2MTransportServerInitializer")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2MTransportServerInitializer {
@ -36,8 +37,18 @@ public class LwM2MTransportServerInitializer {
private LeshanServer lhServerCert;
@Autowired
@Qualifier("leshanServerNoSecPskRpk")
@Qualifier("LeshanServerNoSecPskRpk")
private LeshanServer lhServerNoSecPskRpk;
//
// @Autowired
// @Qualifier("LeshanServerListener")
// private LwM2mServerListener lwM2mServerListener;
@Autowired
private LwM2mServerListener lwM2mServerListenerNoSecPskRpk;
@Autowired
private LwM2mServerListener lwM2mServerListenerCert;
@Autowired
private LwM2MTransportContextServer context;
@ -46,19 +57,33 @@ public class LwM2MTransportServerInitializer {
public void init() {
if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC();
if (this.context.getCtxServer().isServerStartAll()) {
this.lhServerCert.start();
this.lhServerNoSecPskRpk.start();
}
else {
this.startLhServerCert();
this.startLhServerNoSecPskRpk();
} else {
if (this.context.getCtxServer().getServerDtlsMode() == LwM2MSecurityMode.X509.code) {
this.lhServerCert.start();
}
else {
this.lhServerNoSecPskRpk.start();
this.startLhServerCert();
} else {
this.startLhServerNoSecPskRpk();
}
}
}
private void startLhServerCert() {
this.lhServerCert.start();
LwM2mServerListener serverListenerCert = this.lwM2mServerListenerCert.init(this.lhServerCert);
this.lhServerCert.getRegistrationService().addListener(serverListenerCert.registrationListener);
this.lhServerCert.getPresenceService().addListener(serverListenerCert.presenceListener);
this.lhServerCert.getObservationService().addListener(serverListenerCert.observationListener);
}
private void startLhServerNoSecPskRpk() {
this.lhServerNoSecPskRpk.start();
LwM2mServerListener serverListenerNoSecPskRpk = this.lwM2mServerListenerNoSecPskRpk.init(this.lhServerNoSecPskRpk);
this.lhServerNoSecPskRpk.getRegistrationService().addListener(serverListenerNoSecPskRpk.registrationListener);
this.lhServerNoSecPskRpk.getPresenceService().addListener(serverListenerNoSecPskRpk.presenceListener);
this.lhServerNoSecPskRpk.getObservationService().addListener(serverListenerNoSecPskRpk.observationListener);
}
@PreDestroy
public void shutdown() {
log.info("Stopping LwM2M transport Server!");

1098
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java

File diff suppressed because it is too large

1132
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportServiceImpl.java

File diff suppressed because it is too large

17
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -24,17 +24,25 @@ import org.eclipse.leshan.server.queue.PresenceListener;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.registration.RegistrationListener;
import org.eclipse.leshan.server.registration.RegistrationUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Slf4j
@Component()
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2mServerListener {
private LeshanServer lhServer;
private LwM2MTransportService service;
public LwM2mServerListener(LeshanServer lhServer, LwM2MTransportService service) {
@Autowired
private LwM2MTransportServiceImpl service;
public LwM2mServerListener init(LeshanServer lhServer) {
this.lhServer = lhServer;
this.service = service;
return this;
}
public final RegistrationListener registrationListener = new RegistrationListener() {
@ -96,8 +104,7 @@ public class LwM2mServerListener {
try {
service.onObservationResponse(registration, observation.getPath().toString(), response);
} catch (Exception e) {
e.printStackTrace();
log.error("onResponse");
log.error("[{}] onResponse", e.toString());
}
}

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClient.java

@ -27,7 +27,7 @@ import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.util.Map;
@ -48,7 +48,7 @@ public class LwM2MClient implements Cloneable {
private UUID sessionUuid;
private UUID profileUuid;
private LeshanServer lwServer;
private LwM2MTransportService lwM2MTransportService;
private LwM2MTransportServiceImpl lwM2MTransportServiceImpl;
private Registration registration;
private ValidateDeviceCredentialsResponseMsg credentialsResponse;
private Map<String, String> attributes;
@ -92,7 +92,7 @@ public class LwM2MClient implements Cloneable {
this.pendingRequests.remove(path);
if (this.pendingRequests.size() == 0) {
this.initValue();
this.lwM2MTransportService.putDelayedUpdateResourcesThingsboard(this);
this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
}
}
@ -123,7 +123,7 @@ public class LwM2MClient implements Cloneable {
public void onSuccessOrErrorDelayedRequests(String path) {
if (path != null) this.delayedRequests.remove(path);
if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
this.lwM2MTransportService.updatesAndSentModelParameter(this);
this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
}
}

241
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2MSetSecurityStoreServer.java

@ -1,241 +0,0 @@
/**
* Copyright © 2016-2020 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.server.secure;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.RedisSecurityStore;
import org.eclipse.leshan.server.security.DefaultAuthorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportContextServer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.util.Pool;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.util.Arrays;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.REDIS;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
@Slf4j
@Data
public class LwM2MSetSecurityStoreServer {
private KeyStore keyStore;
private X509Certificate certificate;
private PublicKey publicKey;
private PrivateKey privateKey;
private LwM2MTransportContextServer context;
private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
private LeshanServerBuilder builder;
EditableSecurityStore securityStore;
public LwM2MSetSecurityStoreServer(LeshanServerBuilder builder, LwM2MTransportContextServer context, LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore, LwM2MSecurityMode dtlsMode) {
this.builder = builder;
this.context = context;
this.lwM2mInMemorySecurityStore = lwM2mInMemorySecurityStore;
/** Set securityStore with new registrationStore */
switch (dtlsMode) {
/** Use PSK only */
case PSK:
generatePSK_RPK();
if (this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPrivateKey(this.privateKey);
builder.setPublicKey(null);
getParamsPSK();
}
break;
/** Use RPK only */
case RPK:
generatePSK_RPK();
if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
this.privateKey != null && this.privateKey.getEncoded().length > 0) {
builder.setPublicKey(this.publicKey);
builder.setPrivateKey(this.privateKey);
getParamsRPK();
}
break;
/** Use x509 only */
case X509:
setServerWithX509Cert();
break;
/** No security */
case NO_SEC:
builder.setTrustedCertificates(new X509Certificate[0]);
break;
/** Use x509 with EST */
case X509_EST:
// TODO support sentinel pool and make pool configurable
break;
case REDIS:
/**
* Set securityStore with new registrationStore (if use redis store)
* Connect to redis
*/
Pool<Jedis> jedis = null;
try {
jedis = new JedisPool(new URI(this.context.getCtxServer().getRedisUrl()));
securityStore = new RedisSecurityStore(jedis);
builder.setRegistrationStore(new RedisRegistrationStore(jedis));
} catch (URISyntaxException e) {
e.printStackTrace();
}
break;
default:
}
/** Set securityStore with new registrationStore (if not redis)*/
if (dtlsMode.code < REDIS.code) {
securityStore = lwM2mInMemorySecurityStore;
if (dtlsMode == X509) {
builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
@Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
String expectedX509CommonName) {
return endpoint.startsWith(expectedX509CommonName);
}
}));
}
}
/** Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore);
}
private void generatePSK_RPK() {
try {
/** Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.context.getCtxServer().getServerPublicX() != null && !this.context.getCtxServer().getServerPublicX().isEmpty() && this.context.getCtxServer().getServerPublicY() != null && !this.context.getCtxServer().getServerPublicY().isEmpty()) {
/** Get point values */
byte[] publicX = Hex.decodeHex(this.context.getCtxServer().getServerPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.context.getCtxServer().getServerPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
if (this.context.getCtxServer().getServerPrivateS() != null && !this.context.getCtxServer().getServerPrivateS().isEmpty()) {
/** Get point values */
byte[] privateS = Hex.decodeHex(this.context.getCtxServer().getServerPrivateS().toCharArray());
/** Create key specs */
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
/** Get keys */
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
}
} catch (GeneralSecurityException | IllegalArgumentException e) {
log.error("[{}] Failed generate Server PSK/RPK", e.getMessage());
throw new RuntimeException(e);
}
}
private void setServerWithX509Cert() {
try {
if (this.context.getCtxServer().getKeyStoreValue() != null) {
setBuilderX509();
X509Certificate rootCAX509Cert = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getRootAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
}
else {
/** by default trust all */
this.builder.setTrustedCertificates(new X509Certificate[0]);
log.error("Unable to load X509 files for LWM2MServer");
}
} catch (KeyStoreException ex) {
log.error("[{}] Unable to load X509 files server", ex.getMessage());
}
}
private void setBuilderX509() {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try {
X509Certificate serverCertificate = (X509Certificate) this.context.getCtxServer().getKeyStoreValue().getCertificate(this.context.getCtxServer().getServerAlias());
PrivateKey privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.getCtxServer().getKeyStorePasswordServer().toCharArray());
this.builder.setPrivateKey(privateKey);
this.builder.setCertificateChain(new X509Certificate[]{serverCertificate});
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
}
}
private void getParamsPSK() {
log.info("\nServer uses PSK -> private key : \n security key : [{}] \n serverSecureURI : [{}]",
Hex.encodeHexString(this.privateKey.getEncoded()),
this.context.getCtxServer().getServerSecureHost() + ":" + Integer.toString(this.context.getCtxServer().getServerSecurePort()));
}
private void getParamsRPK() {
if (this.publicKey instanceof ECPublicKey) {
/** Get x coordinate */
byte[] x = ((ECPublicKey) this.publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */
byte[] y = ((ECPublicKey) this.publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */
String params = ((ECPublicKey) this.publicKey).getParams().toString();
log.info(
" \nServer uses RPK : \n Elliptic Curve parameters : [{}] \n Public x coord : [{}] \n Public y coord : [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
params, Hex.encodeHexString(x), Hex.encodeHexString(y),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
} else {
throw new IllegalStateException("Unsupported Public Key Format (only ECPublicKey supported).");
}
}
}

100
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/secure/LwM2mInMemorySecurityStore.java

@ -25,32 +25,32 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MGetSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mValidateCredentialsSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandler;
import org.thingsboard.server.transport.lwm2m.server.client.AttrTelemetryObserveValue;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
import java.util.Map;
import java.util.UUID;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
@Slf4j
@Component("LwM2mInMemorySecurityStore")
@Service("LwM2mInMemorySecurityStore")
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' )|| ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
// lock for the two maps
@ -63,26 +63,36 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
private SecurityStoreListener listener;
@Autowired
LwM2MGetSecurityInfo lwM2MGetSecurityInfo;
LwM2mValidateCredentialsSecurityInfo lwM2MValidateCredentialsSecurityInfo;
/**
* Start after DefaultAuthorizer or LwM2mPskStore
* @param endPoint -
* @return SecurityInfo
*/
@Override
public SecurityInfo getByEndpoint(String endPoint) {
readLock.lock();
try {
String registrationId = this.getByRegistrationId(endPoint, null);
SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.add(endPoint);
SecurityInfo info = (registrationId != null && sessions.size() > 0 && sessions.get(registrationId) != null) ? sessions.get(registrationId).getInfo() : this.addLwM2MClientToSession(endPoint);
return info;
} finally {
readLock.unlock();
}
}
/**
* Start after LwM2mPskStore
* @param identity -
* @return SecurityInfo
*/
@Override
public SecurityInfo getByIdentity(String identity) {
readLock.lock();
try {
String integrationId = this.getByRegistrationId(null, identity);
return (integrationId != null) ? sessions.get(integrationId).getInfo() : add(identity);
return (integrationId != null) ? sessions.get(integrationId).getInfo() : this.addLwM2MClientToSession(identity);
} finally {
readLock.unlock();
}
@ -141,11 +151,19 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
return this.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
}
/**
* Update in sessions (LwM2MClient for key registration_Id) after starting registration LwM2MClient in LwM2MTransportServiceImpl
* Remove from sessions LwM2MClient with key registration_Endpoint
* @param lwServer -
* @param registration -
* @return LwM2MClient after adding it to session
*/
public LwM2MClient updateInSessionsLwM2MClient(LeshanServer lwServer, Registration registration) {
writeLock.lock();
try {
if (this.sessions.get(registration.getEndpoint()) == null) {
this.add(registration.getEndpoint());
this.addLwM2MClientToSession(registration.getEndpoint());
}
LwM2MClient lwM2MClient = this.sessions.get(registration.getEndpoint());
lwM2MClient.setLwServer(lwServer);
@ -166,45 +184,43 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;
}
public String getByRegistrationId(String credentialsId) {
List<String> registrationIds = (this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()).size() > 0) ?
this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getEndPoint())).map(model -> model.getKey()).collect(Collectors.toList()) :
this.sessions.entrySet().stream().filter(model -> credentialsId.equals(model.getValue().getIdentity())).map(model -> model.getKey()).collect(Collectors.toList());
return (registrationIds != null && registrationIds.size() > 0) ? registrationIds.get(0) : null;
}
public Registration getByRegistration(String registrationId) {
return this.sessions.get(registrationId).getRegistration();
}
private SecurityInfo add(String identity) {
ReadResultSecurityStore store = lwM2MGetSecurityInfo.getSecurityInfo(identity, TypeServer.CLIENT);
UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
if (store.getSecurityInfo() != null) {
if (store.getSecurityMode() < DEFAULT_MODE.code) {
/**
* Add new LwM2MClient to session
* @param identity-
* @return SecurityInfo. If error - SecurityInfoError
* and log:
* - FORBIDDEN - if there is no authorization
* - profileUuid - if the device does not have a profile
* - device - if the thingsboard does not have a device with a name equal to the identity
*/
private SecurityInfo addLwM2MClientToSession(String identity) {
ReadResultSecurityStore store = lwM2MValidateCredentialsSecurityInfo.validateCredentialsSecurityInfo(identity, TypeServer.CLIENT);
if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
if (store.getSecurityInfo() != null && profileUuid != null) {
String endpoint = store.getSecurityInfo().getEndpoint();
// sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, null, profileUuid));
sessions.put(endpoint, new LwM2MClient(endpoint, store.getSecurityInfo().getIdentity(), store.getSecurityInfo(), store.getMsg(), null, profileUuid));
}
} else {
if (store.getSecurityMode() == NO_SEC.code && profileUuid != null)
// sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, null, profileUuid));
} else if (store.getSecurityMode() == NO_SEC.code && profileUuid != null) {
sessions.put(identity, new LwM2MClient(identity, null, null, store.getMsg(), null, profileUuid));
else {
log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);
/**
* Return Error securityInfo
*/
byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());
SecurityInfo info = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);
return info;
} else {
log.error("Registration failed: FORBIDDEN/profileUuid/device [{}] , endpointId: [{}]", profileUuid, identity);
/**
* Return Error securityInfo
*/
byte[] preSharedKey = Hex.decodeHex("0A0B".toCharArray());
SecurityInfo infoError = SecurityInfo.newPreSharedKeyInfo("error", "error_identity", preSharedKey);
return infoError;
}
}
}
return store.getSecurityInfo();
return store.getSecurityInfo();
}
public Map<String, LwM2MClient> getSession (UUID sessionUuId){
return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
public Map<String, LwM2MClient> getSession(UUID sessionUuId) {
return this.sessions.entrySet().stream().filter(e -> e.getValue().getSessionUuid().equals(sessionUuId)).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
}
public Map<String, LwM2MClient> getSessions() {
@ -219,13 +235,10 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
return this.profiles.get(profileUuId);
}
public Map<UUID, AttrTelemetryObserveValue>setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) {
public Map<UUID, AttrTelemetryObserveValue> setProfiles(Map<UUID, AttrTelemetryObserveValue> profiles) {
return this.profiles = profiles;
}
/**
* @param deviceProfile
*/
public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) {
JsonObject profilesConfigData = LwM2MTransportHandler.getObserveAttrTelemetryFromThingsboard(deviceProfile);
if (profilesConfigData != null) {
@ -233,5 +246,4 @@ public class LwM2mInMemorySecurityStore extends InMemorySecurityStore {
}
return (profilesConfigData != null);
}
}

1
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java

@ -129,7 +129,6 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
case FLOAT:
return String.valueOf(value);
case TIME:
// return Long.toString(((Date) value).getTime());
String DATE_FORMAT = "MMM d, yyyy HH:mm a";
Long timeValue = ((Date) value).getTime();
DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);

7
netty-mqtt/pom.xml

@ -67,11 +67,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -87,4 +82,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

2
pom.xml

@ -579,7 +579,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>

Loading…
Cancel
Save