|
|
@ -21,14 +21,12 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|
|
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; |
|
|
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; |
|
|
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; |
|
|
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; |
|
|
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; |
|
|
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; |
|
|
import org.eclipse.leshan.core.util.Hex; |
|
|
|
|
|
import org.eclipse.leshan.server.californium.LeshanServer; |
|
|
import org.eclipse.leshan.server.californium.LeshanServer; |
|
|
import org.eclipse.leshan.server.californium.LeshanServerBuilder; |
|
|
import org.eclipse.leshan.server.californium.LeshanServerBuilder; |
|
|
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; |
|
|
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; |
|
|
import org.eclipse.leshan.server.model.LwM2mModelProvider; |
|
|
import org.eclipse.leshan.server.model.LwM2mModelProvider; |
|
|
import org.springframework.stereotype.Component; |
|
|
import org.springframework.stereotype.Component; |
|
|
import org.thingsboard.server.cache.ota.OtaPackageDataCache; |
|
|
import org.thingsboard.server.cache.ota.OtaPackageDataCache; |
|
|
import org.thingsboard.server.common.data.StringUtils; |
|
|
|
|
|
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
|
|
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
|
|
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
|
|
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
|
|
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; |
|
|
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; |
|
|
@ -41,24 +39,9 @@ import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; |
|
|
|
|
|
|
|
|
import javax.annotation.PostConstruct; |
|
|
import javax.annotation.PostConstruct; |
|
|
import javax.annotation.PreDestroy; |
|
|
import javax.annotation.PreDestroy; |
|
|
import java.math.BigInteger; |
|
|
|
|
|
import java.security.AlgorithmParameters; |
|
|
|
|
|
import java.security.KeyFactory; |
|
|
|
|
|
import java.security.NoSuchAlgorithmException; |
|
|
|
|
|
import java.security.PrivateKey; |
|
|
import java.security.PrivateKey; |
|
|
import java.security.PublicKey; |
|
|
import java.security.PublicKey; |
|
|
import java.security.cert.CertificateEncodingException; |
|
|
|
|
|
import java.security.cert.X509Certificate; |
|
|
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.ECPublicKeySpec; |
|
|
|
|
|
import java.security.spec.InvalidKeySpecException; |
|
|
|
|
|
import java.security.spec.InvalidParameterSpecException; |
|
|
|
|
|
import java.security.spec.KeySpec; |
|
|
|
|
|
import java.security.spec.PKCS8EncodedKeySpec; |
|
|
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
|
|
|
|
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; |
|
|
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; |
|
|
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; |
|
|
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; |
|
|
@ -75,8 +58,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
|
|
|
|
|
|
|
|
public static final CipherSuite[] RPK_OR_X509_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}; |
|
|
public static final CipherSuite[] RPK_OR_X509_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}; |
|
|
public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256}; |
|
|
public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256}; |
|
|
private PublicKey publicKey; |
|
|
|
|
|
private PrivateKey privateKey; |
|
|
|
|
|
|
|
|
|
|
|
private final LwM2mTransportContext context; |
|
|
private final LwM2mTransportContext context; |
|
|
private final LwM2MTransportServerConfig config; |
|
|
private final LwM2MTransportServerConfig config; |
|
|
@ -97,14 +78,13 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
|
|
new LWM2MGenerationPSkRPkECC(); |
|
|
new LWM2MGenerationPSkRPkECC(); |
|
|
} |
|
|
} |
|
|
this.server = getLhServer(); |
|
|
this.server = getLhServer(); |
|
|
/** |
|
|
/* |
|
|
* Add a resource to the server. |
|
|
* Add a resource to the server. |
|
|
* CoapResource -> |
|
|
* CoapResource -> |
|
|
* path = FW_PACKAGE or SW_PACKAGE |
|
|
* path = FW_PACKAGE or SW_PACKAGE |
|
|
* nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin" |
|
|
* nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin" |
|
|
* "coap://host:port/{path}/{token}/{nameFile}" |
|
|
* "coap://host:port/{path}/{token}/{nameFile}" |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE); |
|
|
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE); |
|
|
this.server.coap().getServer().add(otaCoapResource); |
|
|
this.server.coap().getServer().add(otaCoapResource); |
|
|
this.startLhServer(); |
|
|
this.startLhServer(); |
|
|
@ -170,29 +150,23 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
|
|
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier); |
|
|
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier); |
|
|
builder.setAuthorizer(authorizer); |
|
|
builder.setAuthorizer(authorizer); |
|
|
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES); |
|
|
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES); |
|
|
} else if (this.setServerRPK(builder)) { |
|
|
|
|
|
this.infoPramsUri("RPK"); |
|
|
|
|
|
this.infoParamsServerKey(this.publicKey, this.privateKey); |
|
|
|
|
|
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
/* by default trust all */ |
|
|
/* by default trust all */ |
|
|
builder.setTrustedCertificates(new X509Certificate[0]); |
|
|
builder.setTrustedCertificates(new X509Certificate[0]); |
|
|
log.info("Unable to load X509 files for LWM2MServer"); |
|
|
log.info("Unable to load X509 files for LWM2MServer"); |
|
|
dtlsConfig.setSupportedCipherSuites(PSK_CIPHER_SUITES); |
|
|
dtlsConfig.setSupportedCipherSuites(PSK_CIPHER_SUITES); |
|
|
this.infoPramsUri("PSK"); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private boolean setBuilderX509(LeshanServerBuilder builder) { |
|
|
private boolean setBuilderX509(LeshanServerBuilder builder) { |
|
|
try { |
|
|
try { |
|
|
X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias()); |
|
|
X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias()); |
|
|
PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getKeyStorePassword() == null ? null : config.getKeyStorePassword().toCharArray()); |
|
|
PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getCertificatePassword() == null ? null : config.getCertificatePassword().toCharArray()); |
|
|
PublicKey publicKey = serverCertificate.getPublicKey(); |
|
|
PublicKey publicKey = serverCertificate.getPublicKey(); |
|
|
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { |
|
|
if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { |
|
|
builder.setPublicKey(serverCertificate.getPublicKey()); |
|
|
builder.setPublicKey(serverCertificate.getPublicKey()); |
|
|
builder.setPrivateKey(privateKey); |
|
|
builder.setPrivateKey(privateKey); |
|
|
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); |
|
|
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); |
|
|
this.infoParamsServerX509(serverCertificate, publicKey, privateKey); |
|
|
|
|
|
return true; |
|
|
return true; |
|
|
} else { |
|
|
} else { |
|
|
return false; |
|
|
return false; |
|
|
@ -203,93 +177,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) { |
|
|
|
|
|
try { |
|
|
|
|
|
infoPramsUri("X509"); |
|
|
|
|
|
log.info("\n- X509 Certificate (Hex): [{}]", |
|
|
|
|
|
Hex.encodeHexString(certificate.getEncoded())); |
|
|
|
|
|
this.infoParamsServerKey(publicKey, privateKey); |
|
|
|
|
|
} catch (CertificateEncodingException e) { |
|
|
|
|
|
log.error("", e); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void infoPramsUri(String mode) { |
|
|
|
|
|
LwM2MTransportServerConfig lwM2MTransportServerConfig = config; |
|
|
|
|
|
log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode, |
|
|
|
|
|
lwM2MTransportServerConfig.getHost(), |
|
|
|
|
|
lwM2MTransportServerConfig.getPort(), |
|
|
|
|
|
lwM2MTransportServerConfig.getSecureHost(), |
|
|
|
|
|
lwM2MTransportServerConfig.getSecurePort()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean setServerRPK(LeshanServerBuilder builder) { |
|
|
|
|
|
try { |
|
|
|
|
|
this.loadOrGenerateRPKKeys(); |
|
|
|
|
|
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; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void loadOrGenerateRPKKeys() 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); |
|
|
|
|
|
LwM2MTransportServerConfig serverConfig = config; |
|
|
|
|
|
if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) { |
|
|
|
|
|
byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray()); |
|
|
|
|
|
byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray()); |
|
|
|
|
|
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), |
|
|
|
|
|
parameterSpec); |
|
|
|
|
|
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); |
|
|
|
|
|
} |
|
|
|
|
|
String privateEncodedKey = serverConfig.getPrivateEncoded(); |
|
|
|
|
|
if (StringUtils.isNotEmpty(privateEncodedKey)) { |
|
|
|
|
|
byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray()); |
|
|
|
|
|
try { |
|
|
|
|
|
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); |
|
|
|
|
|
} catch (InvalidKeySpecException ignore2) { |
|
|
|
|
|
log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) { |
|
|
|
|
|
/* Get x coordinate */ |
|
|
|
|
|
byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray(); |
|
|
|
|
|
if (x[0] == 0) |
|
|
|
|
|
x = Arrays.copyOfRange(x, 1, x.length); |
|
|
|
|
|
|
|
|
|
|
|
/* Get Y coordinate */ |
|
|
|
|
|
byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray(); |
|
|
|
|
|
if (y[0] == 0) |
|
|
|
|
|
y = Arrays.copyOfRange(y, 1, y.length); |
|
|
|
|
|
|
|
|
|
|
|
/* Get Curves params */ |
|
|
|
|
|
String params = ((ECPublicKey) publicKey).getParams().toString(); |
|
|
|
|
|
String privHex = Hex.encodeHexString(privateKey.getEncoded()); |
|
|
|
|
|
log.info(" \n- Public Key (Hex): [{}] \n" + |
|
|
|
|
|
"- Private Key (Hex): [{}], \n" + |
|
|
|
|
|
"public_x: \"${LWM2M_SERVER_PUBLIC_X:{}}\" \n" + |
|
|
|
|
|
"public_y: \"${LWM2M_SERVER_PUBLIC_Y:{}}\" \n" + |
|
|
|
|
|
"private_encoded: \"${LWM2M_SERVER_PRIVATE_ENCODED:{}}\" \n" + |
|
|
|
|
|
"- Elliptic Curve parameters : [{}]", |
|
|
|
|
|
Hex.encodeHexString(publicKey.getEncoded()), |
|
|
|
|
|
privHex, |
|
|
|
|
|
Hex.encodeHexString(x), |
|
|
|
|
|
Hex.encodeHexString(y), |
|
|
|
|
|
privHex, |
|
|
|
|
|
params); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
public String getName() { |
|
|
public String getName() { |
|
|
return "LWM2M"; |
|
|
return "LWM2M"; |
|
|
|