Improvements to key store read for lwm2m.
This commit is contained in:
parent
f58fe1647b
commit
ee55c0bf85
@ -30,8 +30,12 @@ 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.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
@ -62,49 +66,23 @@ public class LwM2MServerSecurityInfoRepository {
|
||||
bsServ.setPort(serverConfig.getPort());
|
||||
bsServ.setSecurityHost(serverConfig.getSecureHost());
|
||||
bsServ.setSecurityPort(serverConfig.getSecurePort());
|
||||
bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY()));
|
||||
bsServ.setServerPublicKey(getPublicKey(serverConfig));
|
||||
return bsServ;
|
||||
}
|
||||
|
||||
private String getPublicKey(String alias, String publicServerX, String publicServerY) {
|
||||
String publicKey = getServerPublicKeyX509(alias);
|
||||
return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
|
||||
}
|
||||
|
||||
private String getServerPublicKeyX509(String alias) {
|
||||
private String getPublicKey(LwM2MSecureServerConfig config) {
|
||||
try {
|
||||
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(alias);
|
||||
return Hex.encodeHexString(serverCertificate.getEncoded());
|
||||
} catch (CertificateEncodingException | KeyStoreException e) {
|
||||
e.printStackTrace();
|
||||
KeyStore keyStore = serverConfig.getKeyStoreValue();
|
||||
if (keyStore != null) {
|
||||
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(config.getCertificateAlias());
|
||||
return Hex.encodeHexString(serverCertificate.getPublicKey().getEncoded());
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.trace("Failed to fetch public key from key store!", e);
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getRPKPublicKey(String publicServerX, String publicServerY) {
|
||||
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 (publicServerX != null && !publicServerX.isEmpty() && publicServerY != null && !publicServerY.isEmpty()) {
|
||||
/** Get point values */
|
||||
byte[] publicX = Hex.decodeHex(publicServerX.toCharArray());
|
||||
byte[] publicY = Hex.decodeHex(publicServerY.toCharArray());
|
||||
/** Create key specs */
|
||||
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
|
||||
parameterSpec);
|
||||
/** Get keys */
|
||||
PublicKey publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
|
||||
if (publicKey != null && publicKey.getEncoded().length > 0) {
|
||||
return Hex.encodeHexString(publicKey.getEncoded());
|
||||
}
|
||||
}
|
||||
} catch (GeneralSecurityException | IllegalArgumentException e) {
|
||||
log.error("[{}] Failed generate Server RPK for profile", e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -649,12 +649,9 @@ transport:
|
||||
security:
|
||||
bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
|
||||
bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}"
|
||||
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
|
||||
public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
|
||||
public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
|
||||
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
|
||||
# Only Certificate_x509:
|
||||
alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
|
||||
alias: "${LWM2M_KEYSTORE_SERVER_ALIAS:server}"
|
||||
password: "${LWM2M_KEYSTORE_SERVER_PASSWORD:server_ks_password}"
|
||||
skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
|
||||
bootstrap:
|
||||
enable: "${LWM2M_ENABLED_BS:true}"
|
||||
@ -664,12 +661,9 @@ transport:
|
||||
security:
|
||||
bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}"
|
||||
bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
|
||||
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
|
||||
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
|
||||
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
|
||||
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
|
||||
# Only Certificate_x509:
|
||||
alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
|
||||
alias: "${LWM2M_KEYSTORE_BS_ALIAS:bootstrap}"
|
||||
password: "${LWM2M_KEYSTORE_BS_PASSWORD:server_ks_password}"
|
||||
security:
|
||||
# Certificate_x509:
|
||||
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
transport.lwm2m.security.key_store=lwm2m/credentials/serverKeyStore.jks
|
||||
transport.lwm2m.security.key_store_password=server
|
||||
edges.enabled=true
|
||||
transport.lwm2m.server.security.alias=server
|
||||
transport.lwm2m.server.security.password=server
|
||||
transport.lwm2m.bootstrap.security.alias=server
|
||||
transport.lwm2m.bootstrap.security.password=server
|
||||
@ -31,6 +31,7 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBoots
|
||||
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
|
||||
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
|
||||
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
|
||||
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mTransportService;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
@ -65,10 +66,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.g
|
||||
@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:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')")
|
||||
@RequiredArgsConstructor
|
||||
//TODO: @ybondarenko please refactor this to be similar to DefaultLwM2mTransportService
|
||||
public class LwM2MTransportBootstrapService {
|
||||
private PublicKey publicKey;
|
||||
private PrivateKey privateKey;
|
||||
private boolean pskMode = false;
|
||||
|
||||
private final LwM2MTransportServerConfig serverConfig;
|
||||
@ -101,44 +99,34 @@ public class LwM2MTransportBootstrapService {
|
||||
builder.setLocalAddress(bootstrapConfig.getHost(), bootstrapConfig.getPort());
|
||||
builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort());
|
||||
|
||||
/** Create CoAP Config */
|
||||
/* Create CoAP Config */
|
||||
builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig));
|
||||
|
||||
/** Define model provider (Create Models )*/
|
||||
/* Define model provider (Create Models )*/
|
||||
|
||||
/** Create credentials */
|
||||
/* Create credentials */
|
||||
this.setServerWithCredentials(builder);
|
||||
|
||||
// /** Set securityStore with new ConfigStore */
|
||||
// builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
|
||||
|
||||
/** SecurityStore */
|
||||
/* SecurityStore */
|
||||
builder.setSecurityStore(lwM2MBootstrapSecurityStore);
|
||||
|
||||
|
||||
/** Create and Set DTLS Config */
|
||||
/* Create and Set DTLS Config */
|
||||
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
|
||||
dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
|
||||
dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers());
|
||||
if (this.pskMode) {
|
||||
dtlsConfig.setSupportedCipherSuites(
|
||||
TLS_PSK_WITH_AES_128_CCM_8,
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA256);
|
||||
} else {
|
||||
dtlsConfig.setSupportedCipherSuites(
|
||||
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);
|
||||
}
|
||||
dtlsConfig.setSupportedCipherSuites(this.pskMode ? DefaultLwM2mTransportService.PSK_CIPHER_SUITES : DefaultLwM2mTransportService.RPK_OR_X509_CIPHER_SUITES);
|
||||
|
||||
/** Set DTLS Config */
|
||||
/* Set DTLS Config */
|
||||
builder.setDtlsConfig(dtlsConfig);
|
||||
|
||||
BootstrapSessionManager sessionManager = new LwM2mDefaultBootstrapSessionManager(lwM2MBootstrapSecurityStore);
|
||||
builder.setSessionManager(sessionManager);
|
||||
|
||||
/** Create BootstrapServer */
|
||||
/* Create BootstrapServer */
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@ -153,19 +141,15 @@ public class LwM2MTransportBootstrapService {
|
||||
trustedCertificates[0] = rootCAX509Cert;
|
||||
builder.setTrustedCertificates(trustedCertificates);
|
||||
} else {
|
||||
/** by default trust all */
|
||||
/* by default trust all */
|
||||
builder.setTrustedCertificates(new X509Certificate[0]);
|
||||
}
|
||||
}
|
||||
} else if (this.setServerRPK(builder)) {
|
||||
this.infoPramsUri("RPK");
|
||||
this.infoParamsBootstrapServerKey(this.publicKey, this.privateKey);
|
||||
} else {
|
||||
/** by default trust all */
|
||||
/* by default trust all */
|
||||
builder.setTrustedCertificates(new X509Certificate[0]);
|
||||
log.info("Unable to load X509 files for BootStrapServer");
|
||||
this.pskMode = true;
|
||||
this.infoPramsUri("PSK");
|
||||
}
|
||||
} catch (KeyStoreException ex) {
|
||||
log.error("[{}] Unable to load X509 files server", ex.getMessage());
|
||||
@ -173,19 +157,14 @@ public class LwM2MTransportBootstrapService {
|
||||
}
|
||||
|
||||
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) serverConfig.getKeyStoreValue().getCertificate(this.bootstrapConfig.getCertificateAlias());
|
||||
PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.bootstrapConfig.getCertificateAlias(), serverConfig.getKeyStorePassword() == null ? null : serverConfig.getKeyStorePassword().toCharArray());
|
||||
PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.bootstrapConfig.getCertificateAlias(), serverConfig.getCertificatePassword() == null ? null : serverConfig.getCertificatePassword().toCharArray());
|
||||
PublicKey publicKey = serverCertificate.getPublicKey();
|
||||
if (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;
|
||||
@ -196,105 +175,4 @@ public class LwM2MTransportBootstrapService {
|
||||
}
|
||||
}
|
||||
|
||||
private void infoParamsServerX509(X509Certificate certificate, PublicKey publicKey, PrivateKey privateKey) {
|
||||
try {
|
||||
this.infoPramsUri("X509");
|
||||
log.info("\n- X509 Certificate (Hex): [{}]",
|
||||
Hex.encodeHexString(certificate.getEncoded()));
|
||||
this.infoParamsBootstrapServerKey(publicKey, privateKey);
|
||||
} catch (CertificateEncodingException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void infoPramsUri(String mode) {
|
||||
log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]",
|
||||
mode,
|
||||
this.bootstrapConfig.getHost(),
|
||||
this.bootstrapConfig.getPort(),
|
||||
this.bootstrapConfig.getSecureHost(),
|
||||
this.bootstrapConfig.getSecurePort());
|
||||
}
|
||||
|
||||
|
||||
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.setCertificateChain(new X509Certificate[] { serverCertificate });
|
||||
/// Trust all certificates.
|
||||
builder.setTrustedCertificates(new X509Certificate[0]);
|
||||
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_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_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);
|
||||
LwM2MTransportBootstrapConfig serverConfig = this.bootstrapConfig;
|
||||
if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
|
||||
/** Get point values */
|
||||
byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
|
||||
byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
|
||||
/** Create key specs */
|
||||
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
|
||||
parameterSpec);
|
||||
/** Get public key */
|
||||
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
|
||||
}
|
||||
String privateEncodedKey = serverConfig.getPrivateEncoded();
|
||||
if (StringUtils.isNotEmpty(privateEncodedKey)) {
|
||||
/** Get private key */
|
||||
byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
|
||||
try {
|
||||
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
|
||||
} catch (InvalidKeySpecException ignore2) {
|
||||
log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void infoParamsBootstrapServerKey(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_BS:{}}\" \n" +
|
||||
"public_y: \"${LWM2M_SERVER_PUBLIC_Y_BS:{}}\" \n" +
|
||||
"private_encoded: \"${LWM2M_SERVER_PRIVATE_ENCODED_BS:{}}\" \n" +
|
||||
"- Elliptic Curve parameters : [{}]",
|
||||
Hex.encodeHexString(publicKey.getEncoded()),
|
||||
privHex,
|
||||
Hex.encodeHexString(x),
|
||||
Hex.encodeHexString(y),
|
||||
privHex,
|
||||
params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,12 +27,8 @@ public interface LwM2MSecureServerConfig {
|
||||
|
||||
Integer getSecurePort();
|
||||
|
||||
String getPublicX();
|
||||
|
||||
String getPublicY();
|
||||
|
||||
String getPrivateEncoded();
|
||||
|
||||
String getCertificateAlias();
|
||||
|
||||
String getCertificatePassword();
|
||||
|
||||
}
|
||||
|
||||
@ -46,20 +46,12 @@ public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig {
|
||||
@Value("${transport.lwm2m.bootstrap.security.bind_port:}")
|
||||
private Integer securePort;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.bootstrap.security.public_x:}")
|
||||
private String publicX;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.bootstrap.security.public_y:}")
|
||||
private String publicY;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.bootstrap.security.private_encoded:}")
|
||||
private String privateEncoded;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.bootstrap.security.alias:}")
|
||||
private String certificateAlias;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.bootstrap.security.password:}")
|
||||
private String certificatePassword;
|
||||
|
||||
}
|
||||
|
||||
@ -120,22 +120,14 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
|
||||
@Value("${transport.lwm2m.server.security.bind_port:}")
|
||||
private Integer securePort;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.server.security.public_x:}")
|
||||
private String publicX;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.server.security.public_y:}")
|
||||
private String publicY;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.server.security.private_encoded:}")
|
||||
private String privateEncoded;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.server.security.alias:}")
|
||||
private String certificateAlias;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.server.security.password:}")
|
||||
private String certificatePassword;
|
||||
|
||||
@Getter
|
||||
@Value("${transport.lwm2m.log_max_length:}")
|
||||
private int logMaxLength;
|
||||
|
||||
@ -21,14 +21,12 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
|
||||
import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
|
||||
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
|
||||
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.LeshanServerBuilder;
|
||||
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
|
||||
import org.eclipse.leshan.server.model.LwM2mModelProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
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.transport.lwm2m.config.LwM2MTransportServerConfig;
|
||||
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.PreDestroy;
|
||||
import java.math.BigInteger;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
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.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_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[] 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 LwM2MTransportServerConfig config;
|
||||
@ -97,14 +78,13 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
||||
new LWM2MGenerationPSkRPkECC();
|
||||
}
|
||||
this.server = getLhServer();
|
||||
/**
|
||||
/*
|
||||
* Add a resource to the server.
|
||||
* CoapResource ->
|
||||
* path = FW_PACKAGE or SW_PACKAGE
|
||||
* nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin"
|
||||
* "coap://host:port/{path}/{token}/{nameFile}"
|
||||
*/
|
||||
|
||||
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
|
||||
this.server.coap().getServer().add(otaCoapResource);
|
||||
this.startLhServer();
|
||||
@ -170,29 +150,23 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
||||
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
|
||||
builder.setAuthorizer(authorizer);
|
||||
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 {
|
||||
/* by default trust all */
|
||||
builder.setTrustedCertificates(new X509Certificate[0]);
|
||||
log.info("Unable to load X509 files for LWM2MServer");
|
||||
dtlsConfig.setSupportedCipherSuites(PSK_CIPHER_SUITES);
|
||||
this.infoPramsUri("PSK");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setBuilderX509(LeshanServerBuilder builder) {
|
||||
try {
|
||||
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();
|
||||
if (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;
|
||||
@ -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
|
||||
public String getName() {
|
||||
return "LWM2M";
|
||||
|
||||
@ -108,12 +108,9 @@ transport:
|
||||
security:
|
||||
bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
|
||||
bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}"
|
||||
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
|
||||
public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
|
||||
public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
|
||||
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
|
||||
# Only Certificate_x509:
|
||||
alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
|
||||
alias: "${LWM2M_KEYSTORE_SERVER_ALIAS:server}"
|
||||
password: "${LWM2M_KEYSTORE_SERVER_PASSWORD:server_ks_password}"
|
||||
skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
|
||||
bootstrap:
|
||||
enable: "${LWM2M_ENABLED_BS:true}"
|
||||
@ -123,12 +120,9 @@ transport:
|
||||
security:
|
||||
bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}"
|
||||
bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
|
||||
# Only for RPK: Public & Private Key. If the keystore file is missing or not working
|
||||
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
|
||||
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
|
||||
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
|
||||
# Only Certificate_x509:
|
||||
alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
|
||||
alias: "${LWM2M_KEYSTORE_BS_ALIAS:bootstrap}"
|
||||
password: "${LWM2M_KEYSTORE_BS_PASSWORD:server_ks_password}"
|
||||
security:
|
||||
# Certificate_x509:
|
||||
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user