Lwm2m: back: start DTLS -one server 4 security

This commit is contained in:
nickAS21 2021-02-11 23:39:38 +02:00 committed by Andrew Shvayka
parent 4c12985a96
commit b864680b81
10 changed files with 360 additions and 585 deletions

View File

@ -217,23 +217,19 @@ public class LwM2MModelsRepository {
switch (mode) {
case NO_SEC:
bsServ.setHost(contextBootStrap.getBootstrapHost());
bsServ.setPort(contextBootStrap.getBootstrapPortNoSecPsk());
bsServ.setPort(contextBootStrap.getBootstrapPortNoSec());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortPsk());
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey("");
break;
case RPK:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortRpk());
bsServ.setServerPublicKey(getRPKPublicKey(this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
break;
case X509:
bsServ.setHost(contextBootStrap.getBootstrapSecureHost());
bsServ.setPort(contextBootStrap.getBootstrapSecurePortX509());
bsServ.setServerPublicKey(getServerPublicKeyX509(contextBootStrap.getBootstrapAlias()));
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity());
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity());
bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY()));
break;
default:
break;
@ -243,23 +239,19 @@ public class LwM2MModelsRepository {
switch (mode) {
case NO_SEC:
bsServ.setHost(contextServer.getServerHost());
bsServ.setPort(contextServer.getServerPortNoSecPsk());
bsServ.setPort(contextServer.getServerPortNoSec());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(contextServer.getServerSecureHost());
bsServ.setPort(contextServer.getServerPortPsk());
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey("");
break;
case RPK:
bsServ.setHost(contextServer.getServerSecureHost());
bsServ.setPort(contextServer.getServerPortRpk());
bsServ.setServerPublicKey(getRPKPublicKey(this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break;
case X509:
bsServ.setHost(contextServer.getServerSecureHost());
bsServ.setPort(contextServer.getServerPortX509());
bsServ.setServerPublicKey(getServerPublicKeyX509(contextServer.getServerAlias()));
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break;
default:
break;
@ -268,6 +260,11 @@ public class LwM2MModelsRepository {
return bsServ;
}
private String getPublicKey (String alias, String publicServerX, String publicServerY) {
String publicKey = getServerPublicKeyX509(alias);
return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
}
/**
* @param alias
* @return PublicKey format HexString or null

View File

@ -584,7 +584,7 @@ transport:
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
secure:
# Only Certificate_x509:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
@ -597,18 +597,11 @@ transport:
server:
id: "${LWM2M_SERVER_ID:123}"
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}"
secure:
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
start_psk: "${START_SERVER_PSK:true}"
start_rpk: "${START_SERVER_RPK:true}"
start_x509: "${START_SERVER_X509:true}"
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}"
bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}"
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}"
# Only RPK: Public & Private Key
bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
# create_rpk: "${CREATE_RPK:}"
public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
@ -619,18 +612,11 @@ transport:
enable: "${LWM2M_BOOTSTRAP_ENABLED:true}"
id: "${LWM2M_SERVER_ID:111}"
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}"
secure:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
start_psk: "${START_SERVER_PSK_BS:true}"
start_rpk: "${START_SERVER_RPK_BS:true}"
start_x509: "${START_SERVER_X509_BS:true}"
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}"
bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}"
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}"
# Only RPK: Public & Private Key
bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"

View File

@ -25,20 +25,18 @@ import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuil
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
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.LwM2mDefaultBootstrapSessionManager;
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.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
@ -49,14 +47,13 @@ import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
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
@ -78,61 +75,42 @@ public class LwM2MTransportBootstrapServerConfiguration {
@Autowired
private LwM2MInMemoryBootstrapConfigStore lwM2MInMemoryBootstrapConfigStore;
@Primary
@Bean(name = "leshanBootstrapX509")
@ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_x509:false}'=='true')")
public LeshanBootstrapServer getLeshanBootstrapServerX509() {
log.info("Prepare and start BootstrapServerX509... PostConstruct");
return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecX509(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortX509(), X509);
@Bean
public LeshanBootstrapServer getLeshanBootstrapServer() {
log.info("Prepare and start BootstrapServer... PostConstruct");
return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), this.contextBs.getCtxBootStrap().getBootstrapPortSecurity());
}
@Bean(name = "leshanBootstrapPsk")
@ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_psk:false}'=='true')")
public LeshanBootstrapServer getLeshanBootstrapServerPsk() {
log.info("Prepare and start BootstrapServerRsk... PostConstruct");
return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecPsk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortPsk(), PSK);
}
@Bean(name = "leshanBootstrapRpk")
@ConditionalOnExpression("('${transport.lwm2m.bootstrap.secure.start_rpk:false}'=='true')")
public LeshanBootstrapServer getLeshanBootstrapServerRpk() {
log.info("Prepare and start BootstrapServerRpk... PostConstruct");
return getLeshanBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSecRpk(), this.contextBs.getCtxBootStrap().getBootstrapSecurePortRpk(), RPK);
}
public LeshanBootstrapServer getLeshanBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort, LwM2MSecurityMode dtlsMode) {
public LeshanBootstrapServer getLhBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort) {
LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec);
builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapSecureHost(), bootstrapSecurePort);
builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapHostSecurity(), bootstrapSecurePort);
/** Create CoAP Config */
builder.setCoapConfig(getCoapConfig (bootstrapPortNoSec, bootstrapSecurePort));
/** ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
/** SecurityStore */
builder.setSecurityStore(lwM2MBootstrapSecurityStore);
builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
/** Define model provider (Create Models )*/
builder.setModel(new StaticModel(contextS.getCtxServer().getModelsValue()));
/** Create credentials */
LwM2MSetSecurityStoreBootstrap(builder, dtlsMode);
this.setServerWithCredentials(builder);
/** Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
/** SecurityStore */
builder.setSecurityStore(lwM2MBootstrapSecurityStore);
/** Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
if (dtlsMode==PSK) {
dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers());
dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getCtxServer().isRecommendedSupportedGroups());
dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256);
}
else {
dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers());
// dtlsConfig.setRecommendedSupportedGroupsOnly(false);
}
builder.setDtlsConfig(dtlsConfig);
dtlsConfig.setRecommendedSupportedGroupsOnly(!this.contextS.getCtxServer().isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getCtxServer().isRecommendedCiphers());
dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
/** Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
builder.setSessionManager(sessionManager);
@ -141,150 +119,153 @@ public class LwM2MTransportBootstrapServerConfiguration {
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:
break;
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) {
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
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);
if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.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 if (this.setServerRPK(builder)) {
this.infoParamsServerRPK();
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for BootStrapServer");
this.infoParamsServerPSK();
}
}
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) {
private boolean 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) {
PrivateKey privateKey = (PrivateKey) this.contextS.getCtxServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getCtxServer().getKeyStorePasswordServer() == null ? null : this.contextS.getCtxServer().getKeyStorePasswordServer().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null &&
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
this.infoParamsX509(serverCertificate);
this.infoParamsServerX509(serverCertificate, publicKey, privateKey);
return true;
} else {
return false;
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
return false;
}
}
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) {
private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) {
try {
log.info("BootStrap uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
log.info("Bootstrap Server uses X509 : \n X509 Certificate (Hex): [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
Hex.encodeHexString(certificate.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()));
Hex.encodeHexString(publicKey.getEncoded()),
Hex.encodeHexString(privateKey.getEncoded()));
} catch (CertificateEncodingException e) {
log.error("", e);
}
}
private boolean setServerRPK(LeshanBootstrapServerBuilder builder) {
try {
this.generateKeyForBootstrapRPK();
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);
return true;
}
} catch (NoSuchAlgorithmException | InvalidParameterSpecException | InvalidKeySpecException e) {
log.error("Fail create Bootstrap Server with RPK", e);
}
return false;
}
/**
* From yml^ bootstrap
* public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"
* public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
* private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"
*/
private void generateKeyForBootstrapRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
/** 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);
}
}
private void infoParamsServerRPK() {
/** 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();
String privHex = Hex.encodeHexString(this.privateKey.getEncoded());
log.info("Server uses RPK -> serverNoSecureURI : [{}], serverSecureURI : [{}], \n" +
"Public Key (Hex): [{}] \n" +
"Private Key (Hex): [{}], \n" +
"- public_x : [{}] \n" +
"- public_y : [{}] \n" +
"- private_s : [{}] \n" +
"- Elliptic Curve parameters : [{}]",
this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(),
this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity(),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()),
Hex.encodeHexString(x),
Hex.encodeHexString(y),
privHex.substring(privHex.length() - 64),
params);
}
private void infoParamsServerPSK() {
log.info("Server uses PSK -> serverNoSecureURI : [{}], serverSecureURI : [{}]",
this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(),
this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity());
}
}

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.bootstrap;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
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;
@ -31,39 +30,20 @@ import javax.annotation.PreDestroy;
public class LwM2MTransportBootstrapServerInitializer {
@Autowired(required = false)
@Qualifier("leshanBootstrapX509")
private LeshanBootstrapServer lhBServerCert;
@Autowired(required = false)
@Qualifier("leshanBootstrapPsk")
private LeshanBootstrapServer lhBServerPsk;
@Autowired(required = false)
@Qualifier("leshanBootstrapRpk")
private LeshanBootstrapServer lhBServerRpk;
private LeshanBootstrapServer lhBServer;
@Autowired
private LwM2MTransportContextBootstrap contextBS;
@PostConstruct
public void init() {
if (this.contextBS.getCtxBootStrap().getBootstrapStartPsk()) {
this.lhBServerPsk.start();
}
if (this.contextBS.getCtxBootStrap().getBootstrapStartRpk()) {
this.lhBServerRpk.start();
}
if (this.contextBS.getCtxBootStrap().getBootstrapStartX509()) {
this.lhBServerCert.start();
}
this.lhBServer.start();
}
@PreDestroy
public void shutdown() throws InterruptedException {
log.info("Stopping LwM2M transport Bootstrap Server!");
lhBServerPsk.destroy();
lhBServerRpk.destroy();
lhBServerCert.destroy();
lhBServer.destroy();
log.info("LwM2M transport Bootstrap Server stopped!");
}
}

View File

@ -25,31 +25,20 @@ 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;
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.springframework.stereotype.Component;
import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurityStore;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
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.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
@ -60,20 +49,17 @@ import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
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
@ComponentScan("org.thingsboard.server.transport.lwm2m.server")
@ComponentScan("org.thingsboard.server.transport.lwm2m.utils")
@Configuration("LwM2MTransportServerConfiguration")
@Component("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;
@ -85,32 +71,16 @@ public class LwM2MTransportServerConfiguration {
@Autowired
private LwM2mInMemorySecurityStore lwM2mInMemorySecurityStore;
@Primary
@Bean(name = "leshanServerPsk")
@ConditionalOnExpression("('${transport.lwm2m.server.secure.start_psk:false}'=='true')")
public LeshanServer getLeshanServerPsk() {
log.info("Starting LwM2M transport ServerPsk... PostConstruct");
return getLeshanServer(this.context.getCtxServer().getServerPortNoSecPsk(), this.context.getCtxServer().getServerPortPsk(), PSK);
@Bean
public LeshanServer getLeshanServer() {
log.info("Starting LwM2M transport Server... PostConstruct");
return this.getLhServer(this.context.getCtxServer().getServerPortNoSec(), this.context.getCtxServer().getServerPortSecurity());
}
@Bean(name = "leshanServerRpk")
@ConditionalOnExpression("('${transport.lwm2m.server.secure.start_rpk:false}'=='true')")
public LeshanServer getLeshanServerRpk() {
log.info("Starting LwM2M transport ServerRpk... PostConstruct");
return getLeshanServer(this.context.getCtxServer().getServerPortNoSecRpk(), this.context.getCtxServer().getServerPortRpk(), RPK);
}
@Bean(name = "leshanServerX509")
@ConditionalOnExpression("('${transport.lwm2m.server.secure.start_x509:false}'=='true')")
public LeshanServer getLeshanServerX509() {
log.info("Starting LwM2M transport ServerX509... PostConstruct");
return getLeshanServer(this.context.getCtxServer().getServerPortNoSecX509(), this.context.getCtxServer().getServerPortX509(), X509);
}
private LeshanServer getLeshanServer(Integer serverPortNoSec, Integer serverSecurePort, LwM2MSecurityMode dtlsMode) {
private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) {
LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setLocalAddress(this.context.getCtxServer().getServerHost(), serverPortNoSec);
builder.setLocalSecureAddress(this.context.getCtxServer().getServerSecureHost(), serverSecurePort);
builder.setLocalSecureAddress(this.context.getCtxServer().getServerHostSecurity(), serverSecurePort);
builder.setEncoder(new DefaultLwM2mNodeEncoder());
LwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder();
builder.setDecoder(decoder);
@ -123,22 +93,19 @@ public class LwM2MTransportServerConfiguration {
LwM2mModelProvider modelProvider = new VersionedModelProvider(this.context.getCtxServer().getModelsValue());
builder.setObjectModelProvider(modelProvider);
/** Create DTLS security mode
* There can be only one DTLS security mode
*/
this.LwM2MSetSecurityStoreServer(builder, dtlsMode);
/** Create credentials */
this.setServerWithCredentials(builder);
/** Set securityStore with new registrationStore */
builder.setSecurityStore(lwM2mInMemorySecurityStore);
/** Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
if (dtlsMode==PSK) {
dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers());
dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getCtxServer().isRecommendedSupportedGroups());
dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256);
}
else {
dtlsConfig.setRecommendedSupportedGroupsOnly(!this.context.getCtxServer().isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(!this.context.getCtxServer().isRecommendedCiphers());
}
dtlsConfig.setRecommendedSupportedGroupsOnly(!this.context.getCtxServer().isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getCtxServer().isRecommendedCiphers());
dtlsConfig.setSupportedCipherSuites(TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
/** Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
@ -150,154 +117,42 @@ public class LwM2MTransportServerConfiguration {
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();
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().getServerPortPsk()));
}
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) {
private void setServerWithCredentials(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);
if (this.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]);
}
/** Set securityStore with registrationStore*/
builder.setAuthorizer(new DefaultAuthorizer(lwM2mInMemorySecurityStore, new SecurityChecker() {
@Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
String expectedX509CommonName) {
return endpoint.startsWith(expectedX509CommonName);
}
}));
} else if (this.setServerRPK(builder)) {
this.infoParamsServerRPK();
} else {
/** by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer");
this.infoParamsServerPSK();
}
} 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) {
private boolean 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
@ -305,38 +160,122 @@ public class LwM2MTransportServerConfiguration {
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});
this.infoParamsX509(serverCertificate, privateKey);
PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null &&
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
this.infoParamsServerX509(serverCertificate, publicKey, privateKey);
return true;
}
else {
return false;
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
return false;
}
// /**
// * 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().getServerPrivateS());
// this.privateKey = (PrivateKey) this.context.getCtxServer().getKeyStoreValue().getKey(this.context.getCtxServer().getServerAlias(), this.context.getCtxServer().getKeyStorePasswordServer() == null ? null : this.context.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.infoParamsX509(serverCertificate);
// }
// } catch (Exception ex) {
// log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
// }
}
private void infoParamsX509(X509Certificate certificate, PrivateKey privateKey) {
private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) {
try {
log.info("Server uses X509 : \n X509 Certificate (Hex): [{}] \n Private Key (Hex): [{}]",
log.info("Server uses X509 : \n X509 Certificate (Hex): [{}] \n Public Key (Hex): [{}] \n Private Key (Hex): [{}]",
Hex.encodeHexString(certificate.getEncoded()),
Hex.encodeHexString(publicKey.getEncoded()),
Hex.encodeHexString(privateKey.getEncoded()));
} catch (CertificateEncodingException e) {
log.error("", e);
}
}
private boolean setServerRPK(LeshanServerBuilder builder) {
try {
this.generateKeyForRPK();
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);
return true;
}
} catch (NoSuchAlgorithmException | InvalidParameterSpecException | InvalidKeySpecException e) {
log.error("Fail create Server with RPK", e);
}
return false;
}
/**
* From yml^ server
* public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
* public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
* private_s: "${LWM2M_SERVER_PRIVATE_S:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"
*/
private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
/** 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);
}
}
private void infoParamsServerRPK() {
/** 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();
String privHex = Hex.encodeHexString(this.privateKey.getEncoded());
log.info("Server uses RPK -> serverNoSecureURI : [{}], serverSecureURI : [{}], \n" +
"Public Key (Hex): [{}] \n" +
"Private Key (Hex): [{}], \n" +
"- public_x : [{}] \n" +
"- public_y : [{}] \n" +
"- private_s : [{}] \n" +
"- Elliptic Curve parameters : [{}]",
this.context.getCtxServer().getServerHost() + ":" + this.context.getCtxServer().getServerPortNoSec(),
this.context.getCtxServer().getServerHostSecurity() + ":" + this.context.getCtxServer().getServerPortSecurity(),
Hex.encodeHexString(this.publicKey.getEncoded()),
Hex.encodeHexString(this.privateKey.getEncoded()),
Hex.encodeHexString(x),
Hex.encodeHexString(y),
privHex.substring(privHex.length() - 64),
params);
}
private void infoParamsServerPSK() {
log.info("Server uses PSK -> serverNoSecureURI : [{}], serverSecureURI : [{}]",
this.context.getCtxServer().getServerHost() + ":" + Integer.toString(this.context.getCtxServer().getServerPortNoSec()),
this.context.getCtxServer().getServerHostSecurity() + ":" + Integer.toString(this.context.getCtxServer().getServerPortSecurity()));
}
}

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j;
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.Component;
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
@ -31,69 +30,35 @@ import javax.annotation.PreDestroy;
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' ) || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled}'=='true')")
public class LwM2MTransportServerInitializer {
@Autowired
private LwM2MTransportServiceImpl service;
@Autowired(required = false)
@Qualifier("leshanServerX509")
private LeshanServer lhServerX509;
@Autowired(required = false)
@Qualifier("leshanServerPsk")
private LeshanServer lhServerPsk;
@Autowired(required = false)
@Qualifier("leshanServerRpk")
private LeshanServer lhServerRpk;
private LeshanServer leshanServer;
@Autowired
private LwM2MTransportContextServer context;
@PostConstruct
public void init() {
if (this.context.getCtxServer().getEnableGenPskRpk()) new LWM2MGenerationPSkRPkECC();
if (this.context.getCtxServer().isServerStartPsk()) {
this.startLhServerPsk();
}
if (this.context.getCtxServer().isServerStartRpk()) {
this.startLhServerRpk();
}
if (this.context.getCtxServer().isServerStartX509()) {
this.startLhServerX509();
if (this.context.getCtxServer().getEnableGenPskRpk()) {
new LWM2MGenerationPSkRPkECC();
}
this.startLhServer();
}
private void startLhServerPsk() {
this.lhServerPsk.start();
LwM2mServerListener lhServerPskListener = new LwM2mServerListener(this.lhServerPsk, service);
this.lhServerPsk.getRegistrationService().addListener(lhServerPskListener.registrationListener);
this.lhServerPsk.getPresenceService().addListener(lhServerPskListener.presenceListener);
this.lhServerPsk.getObservationService().addListener(lhServerPskListener.observationListener);
}
private void startLhServerRpk() {
this.lhServerRpk.start();
LwM2mServerListener lhServerRpkListener = new LwM2mServerListener(this.lhServerRpk, service);
this.lhServerRpk.getRegistrationService().addListener(lhServerRpkListener.registrationListener);
this.lhServerRpk.getPresenceService().addListener(lhServerRpkListener.presenceListener);
this.lhServerRpk.getObservationService().addListener(lhServerRpkListener.observationListener);
}
private void startLhServerX509() {
this.lhServerX509.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.lhServerX509, service);
this.lhServerX509.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.lhServerX509.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.lhServerX509.getObservationService().addListener(lhServerCertListener.observationListener);
private void startLhServer() {
this.leshanServer.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.leshanServer, service);
this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener);
}
@PreDestroy
public void shutdown() {
log.info("Stopping LwM2M transport Server!");
lhServerPsk.destroy();
lhServerRpk.destroy();
lhServerX509.destroy();
leshanServer.destroy();
log.info("LwM2M transport Server stopped!");
}
}

View File

@ -39,49 +39,21 @@ public class LwM2MTransportConfigBootstrap {
@Value("${transport.lwm2m.bootstrap.id:}")
private Integer bootstrapServerId;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.start_psk:}")
private Boolean bootstrapStartPsk;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.start_rpk:}")
private Boolean bootstrapStartRpk;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.start_x509:}")
private Boolean bootstrapStartX509;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_address:}")
private String bootstrapHost;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_address:}")
private String bootstrapSecureHost;
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec:}")
private Integer bootstrapPortNoSec;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec_psk:}")
private Integer bootstrapPortNoSecPsk;
@Value("${transport.lwm2m.bootstrap.secure.bind_address_security:}")
private String bootstrapHostSecurity;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec_rpk:}")
private Integer bootstrapPortNoSecRpk;
@Getter
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec_x509:}")
private Integer bootstrapPortNoSecX509;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_port_psk:}")
private Integer bootstrapSecurePortPsk;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_port_rpk:}")
private Integer bootstrapSecurePortRpk;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_port_x509:}")
private Integer bootstrapSecurePortX509;
@Value("${transport.lwm2m.bootstrap.secure.bind_port_security:}")
private Integer bootstrapPortSecurity;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.public_x:}")

View File

@ -79,7 +79,7 @@ public class LwM2MTransportConfigServer {
private String BASE_DIR_PATH = System.getProperty("user.dir");
@Getter
// private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$";
// private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$";
private String PATH_DATA = "data";
@Getter
@ -146,58 +146,29 @@ public class LwM2MTransportConfigServer {
@Value("${transport.lwm2m.secure.root_alias:}")
private String rootAlias;
@Getter
@Value("${transport.lwm2m.server.secure.start_psk:}")
private boolean serverStartPsk;
@Getter
@Value("${transport.lwm2m.server.secure.start_rpk:}")
private boolean serverStartRpk;
@Getter
@Value("${transport.lwm2m.server.secure.start_x509:}")
private boolean serverStartX509;
@Getter
@Value("${transport.lwm2m.secure.enable_gen_psk_rpk:}")
private Boolean enableGenPskRpk;
@Getter
@Value("${transport.lwm2m.server.bind_address:}")
private String serverHost;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer serverId;
@Getter
@Value("${transport.lwm2m.server.secure.bind_address:}")
private String serverSecureHost;
@Value("${transport.lwm2m.server.bind_address:}")
private String serverHost;
@Getter
@Value("${transport.lwm2m.server.bind_port_no_sec_psk:}")
private Integer serverPortNoSecPsk;
@Value("${transport.lwm2m.server.secure.bind_address_security:}")
private String serverHostSecurity;
@Getter
@Value("${transport.lwm2m.server.bind_port_no_sec_rpk:}")
private Integer serverPortNoSecRpk;
@Value("${transport.lwm2m.server.bind_port_no_sec:}")
private Integer serverPortNoSec;
@Getter
@Value("${transport.lwm2m.server.bind_port_no_sec_x509:}")
private Integer serverPortNoSecX509;
@Getter
@Value("${transport.lwm2m.server.secure.bind_port_psk:}")
private Integer serverPortPsk;
@Getter
@Value("${transport.lwm2m.server.secure.bind_port_rpk:}")
private Integer serverPortRpk;
@Getter
@Value("${transport.lwm2m.server.secure.bind_port_x509:}")
private Integer serverPortX509;
@Value("${transport.lwm2m.server.secure.bind_port_security:}")
private Integer serverPortSecurity;
@Getter
@Value("${transport.lwm2m.server.secure.public_x:}")
@ -303,7 +274,4 @@ public class LwM2MTransportConfigServer {
ResourceModel resource = this.getResourceModel(registration, pathIds);
return (resource == null) ? ResourceModel.Operations.NONE : resource.operations;
}
}

View File

@ -60,7 +60,7 @@ transport:
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
secure:
# Only Certificate_x509:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
@ -73,18 +73,11 @@ transport:
server:
id: "${LWM2M_SERVER_ID:123}"
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_PSK:5685}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_RPK:5687}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_X509:5689}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}"
secure:
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
start_psk: "${START_SERVER_PSK:true}"
start_rpk: "${START_SERVER_RPK:true}"
start_x509: "${START_SERVER_X509:true}"
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK:5686}"
bind_port_rpk: "${LWM2M_BIND_PORT_SEC_RPK:5688}"
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509:5690}"
# Only RPK: Public & Private Key
bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
# create_rpk: "${CREATE_RPK:}"
public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
@ -92,28 +85,22 @@ transport:
# Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
bootstrap:
enable: "${BOOTSTRAP:true}"
enable: "${LWM2M_BOOTSTRAP_ENABLED:true}"
id: "${LWM2M_SERVER_ID:111}"
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_no_sec_psk: "${LWM2M_BIND_PORT_NO_SEC_BS:5691}"
bind_port_no_sec_rpk: "${LWM2M_BIND_PORT_NO_SEC_BS:5693}"
bind_port_no_sec_x509: "${LWM2M_BIND_PORT_NO_SEC_BS:5695}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}"
secure:
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
start_psk: "${START_SERVER_PSK_BS:true}"
start_rpk: "${START_SERVER_RPK_BS:true}"
start_x509: "${START_SERVER_X509_BS:true}"
bind_port_psk: "${LWM2M_BIND_PORT_SEC_PSK_BS:5692}"
bind_port_rpk: "${LWM2M_BIND_PORT_SER_RPK_BS:5694}"
bind_port_x509: "${LWM2M_BIND_PORT_SEC_X509_BS:5696}"
# Only RPK: Public & Private Key
bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SEC_BS:5688}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:993ef2b698c6a9c0c1d8be78b13a9383c0854c7c7c7a504d289b403794648183}"
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:267412d5fc4e5ceb2257cb7fd7f76ebdac2fa9aa100afb162e990074cc0bfaa2}"
private_s: "${LWM2M_SERVER_PRIVATE_S_BS:9dbdbb073fc63570693a9aaf1013414e261c571f27e27fc6a8c1c2ad9347875a}"
# Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_BOOTSTRAP:bootstrap}"
# Redis
# Redis
redis_url: "${LWM2M_REDIS_URL:''}"
sessions:
inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}"
report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:30000}"

View File

@ -27,7 +27,7 @@ export const DEFAULT_ID_SERVER = 123;
export const DEFAULT_ID_BOOTSTRAP = 111;
export const DEFAULT_HOST_NAME = 'localhost';
export const DEFAULT_PORT_SERVER_NO_SEC = 5685;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5691;
export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5686;
export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1;
export const DEFAULT_LIFE_TIME = 300;
export const DEFAULT_MIN_PERIOD = 1;