Browse Source

Unified transport SSL credentials

pull/5451/head
Igor Kulikov 5 years ago
parent
commit
362ccf9f31
  1. 26
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java
  2. 145
      application/src/main/resources/thingsboard.yml
  3. 37
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java
  4. 24
      common/data/src/main/java/org/thingsboard/server/common/data/ResourceUtils.java
  5. 52
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  6. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java
  7. 24
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java
  8. 69
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java
  9. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  10. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  11. 42
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
  12. 8
      common/transport/transport-api/pom.xml
  13. 191
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java
  14. 52
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java
  15. 130
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java
  16. 45
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentials.java
  17. 66
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java
  18. 21
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsType.java
  19. 36
      transport/coap/src/main/resources/tb-coap-transport.yml
  20. 83
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  21. 30
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml

26
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java

@ -22,28 +22,11 @@ import org.eclipse.leshan.core.util.Hex;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
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;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
@Slf4j
@Service
@RequiredArgsConstructor
@ -72,10 +55,9 @@ public class LwM2MServerSecurityInfoRepository {
private String getPublicKey(LwM2MSecureServerConfig config) {
try {
KeyStore keyStore = serverConfig.getKeyStoreValue();
if (keyStore != null) {
X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(config.getCertificateAlias());
return Hex.encodeHexString(serverCertificate.getPublicKey().getEncoded());
SslCredentials sslCredentials = config.getSslCredentials();
if (sslCredentials != null) {
return Hex.encodeHexString(sslCredentials.getPublicKey().getEncoded());
}
} catch (Exception e) {
log.trace("Failed to fetch public key from key store!", e);

145
application/src/main/resources/thingsboard.yml

@ -619,14 +619,28 @@ transport:
bind_port: "${MQTT_SSL_BIND_PORT:8883}"
# SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
# Path to the key store that holds the SSL certificate
key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${MQTT_SSL_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${MQTT_SSL_PEM_CERT:mqttserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${MQTT_SSL_PEM_KEY:mqttserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${MQTT_SSL_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
# Local CoAP transport parameters
@ -645,14 +659,30 @@ transport:
bind_address: "${COAP_DTLS_BIND_ADDRESS:0.0.0.0}"
# CoAP DTLS bind port
bind_port: "${COAP_DTLS_BIND_PORT:5684}"
# Path to the key store that holds the certificate
key_store: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
# Password used to access the key store
key_store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
# Server DTLS credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${COAP_DTLS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${COAP_DTLS_PEM_CERT:coapserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${COAP_DTLS_PEM_KEY:coapserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${COAP_DTLS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${COAP_DTLS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
# Password used to access the key store
store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
x509:
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
@ -669,9 +699,33 @@ transport:
security:
bind_address: "${LWM2M_SECURITY_BIND_ADDRESS:0.0.0.0}"
bind_port: "${LWM2M_SECURITY_BIND_PORT:5686}"
# Server X509 Certificates support
credentials:
# Whether to enable LWM2M server X509 Certificate/RPK support
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${LWM2M_SERVER_PEM_CERT:lwm2mserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${LWM2M_SERVER_PEM_KEY:lwm2mserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${LWM2M_SERVER_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${LWM2M_SERVER_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_SERVER_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_SERVER_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${LWM2M_SERVER_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${LWM2M_SERVER_KEY_ALIAS:server}"
# Only Certificate_x509:
key_alias: "${LWM2M_SERVER_KEY_ALIAS:server}"
key_password: "${LWM2M_SERVER_KEY_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}"
@ -681,18 +735,51 @@ transport:
security:
bind_address: "${LWM2M_BS_SECURITY_BIND_ADDRESS:0.0.0.0}"
bind_port: "${LWM2M_BS_SECURITY_BIND_PORT:5688}"
# Only Certificate_x509:
key_alias: "${LWM2M_BS_KEY_ALIAS:bootstrap}"
key_password: "${LWM2M_BS_KEY_PASSWORD:server_ks_password}"
# Bootstrap server X509 Certificates support
credentials:
# Whether to enable LWM2M bootstrap server X509 Certificate/RPK support
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${LWM2M_BS_PEM_CERT:lwm2mserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${LWM2M_BS_PEM_KEY:lwm2mserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${LWM2M_BS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${LWM2M_BS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_BS_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_BS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${LWM2M_BS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${LWM2M_BS_KEY_ALIAS:bootstrap}"
security:
# 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}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store: "${LWM2M_KEYSTORE:lwm2mserver.jks}"
key_store_password: "${LWM2M_KEYSTORE_PASSWORD:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA_ALIAS:rootca}"
# X509 trust certificates
trust-credentials:
# Whether to load X509 trust certificates
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}"
# Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore)
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
# PEM certificates
pem:
# Path to the certificates file (holds trust certificates)
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}"
# Keystore with trust certificates
keystore:
# Type of the key store
type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the X509 certificates
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
timeout: "${LWM2M_TIMEOUT:120000}"

37
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java

@ -20,11 +20,16 @@ import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import java.io.IOException;
@ -45,17 +50,15 @@ public class TbCoapDtlsSettings {
@Value("${transport.coap.dtls.bind_port}")
private Integer port;
@Value("${transport.coap.dtls.key_store}")
private String keyStoreFile;
@Value("${transport.coap.dtls.key_store_password}")
private String keyStorePassword;
@Value("${transport.coap.dtls.key_password}")
private String keyPassword;
@Bean
@ConfigurationProperties(prefix = "transport.coap.dtls.credentials")
public SslCredentialsConfig coapDtlsCredentials() {
return new SslCredentialsConfig("COAP DTLS Credentials", false);
}
@Value("${transport.coap.dtls.key_alias}")
private String keyAlias;
@Autowired
@Qualifier("coapDtlsCredentials")
private SslCredentialsConfig coapDtlsCredentialsConfig;
@Value("${transport.coap.dtls.x509.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert;
@ -75,8 +78,9 @@ public class TbCoapDtlsSettings {
public DtlsConnectorConfig dtlsConnectorConfig() throws UnknownHostException {
DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder();
configBuilder.setAddress(getInetSocketAddress());
String keyStoreFilePath = ResourceUtils.getUri(this, keyStoreFile);
SslContextUtil.Credentials serverCredentials = loadServerCredentials(keyStoreFilePath);
SslCredentials sslCredentials = this.coapDtlsCredentialsConfig.getCredentials();
SslContextUtil.Credentials serverCredentials =
new SslContextUtil.Credentials(sslCredentials.getPrivateKey(), null, sslCredentials.getCertificateChain());
configBuilder.setServerOnly(true);
configBuilder.setClientAuthenticationRequired(false);
configBuilder.setClientAuthenticationWanted(true);
@ -94,15 +98,6 @@ public class TbCoapDtlsSettings {
return configBuilder.build();
}
private SslContextUtil.Credentials loadServerCredentials(String keyStoreFilePath) {
try {
return SslContextUtil.loadCredentials(keyStoreFilePath, keyAlias, keyStorePassword.toCharArray(),
keyPassword.toCharArray());
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException("Failed to load serverCredentials due to: ", e);
}
}
private InetSocketAddress getInetSocketAddress() throws UnknownHostException {
InetAddress addr = InetAddress.getByName(host);
return new InetSocketAddress(addr, port);

24
common/data/src/main/java/org/thingsboard/server/common/data/ResourceUtils.java

@ -27,6 +27,30 @@ import java.net.URL;
@Slf4j
public class ResourceUtils {
public static boolean resourceExists(Object classLoaderSource, String filePath) {
return resourceExists(classLoaderSource.getClass().getClassLoader(), filePath);
}
public static boolean resourceExists(ClassLoader classLoader, String filePath) {
File resourceFile = new File(filePath);
if (resourceFile.exists()) {
return true;
} else {
InputStream classPathStream = classLoader.getResourceAsStream(filePath);
if (classPathStream != null) {
return true;
} else {
try {
URL url = Resources.getResource(filePath);
if (url != null) {
return true;
}
} catch (IllegalArgumentException e) {}
}
}
return false;
}
public static InputStream getInputStream(Object classLoaderSource, String filePath) {
return getInputStream(classLoaderSource.getClass().getClassLoader(), filePath);
}

52
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java

@ -27,6 +27,7 @@ import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServer;
import org.eclipse.leshan.server.californium.bootstrap.LeshanBootstrapServerBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
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.LwM2MInMemoryBootstrapConfigurationAdapter;
@ -114,49 +115,22 @@ public class LwM2MTransportBootstrapService {
}
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
try {
if (serverConfig.getKeyStoreValue() != null) {
KeyStore keyStoreServer = serverConfig.getKeyStoreValue();
if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(serverConfig.getRootCertificateAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
}
if (this.bootstrapConfig.getSslCredentials() != null) {
SslCredentials sslCredentials = this.bootstrapConfig.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain());
if (this.serverConfig.getTrustSslCredentials() != null) {
builder.setTrustedCertificates(this.serverConfig.getTrustSslCredentials().getTrustedCertificates());
} else {
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for BootStrapServer");
this.pskMode = true;
}
} catch (KeyStoreException ex) {
log.error("[{}] Unable to load X509 files server", ex.getMessage());
} else {
/* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for BootStrapServer");
this.pskMode = true;
}
}
private boolean setBuilderX509(LeshanBootstrapServerBuilder builder) {
try {
X509Certificate[] certificateChain = SslContextUtil.asX509Certificates(serverConfig.getKeyStoreValue().getCertificateChain(this.bootstrapConfig.getCertificateAlias()));
X509Certificate serverCertificate = certificateChain[0];
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(certificateChain);
return true;
} else {
return false;
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
return false;
}
}
}

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.transport.lwm2m.config;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
public interface LwM2MSecureServerConfig {
Integer getId();
@ -27,8 +29,6 @@ public interface LwM2MSecureServerConfig {
Integer getSecurePort();
String getCertificateAlias();
String getCertificatePassword();
SslCredentials getSslCredentials();
}

24
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java

@ -17,9 +17,15 @@ package org.thingsboard.server.transport.lwm2m.config;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
@Slf4j
@Component
@ -46,12 +52,18 @@ public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig {
@Value("${transport.lwm2m.bootstrap.security.bind_port:}")
private Integer securePort;
@Getter
@Value("${transport.lwm2m.bootstrap.security.key_alias:}")
private String certificateAlias;
@Bean
@ConfigurationProperties(prefix = "transport.lwm2m.bootstrap.security.credentials")
public SslCredentialsConfig lwm2mBootstrapCredentials() {
return new SslCredentialsConfig("LWM2M Bootstrap DTLS Credentials", false);
}
@Getter
@Value("${transport.lwm2m.bootstrap.security.key_password:}")
private String certificatePassword;
@Autowired
@Qualifier("lwm2mBootstrapCredentials")
private SslCredentialsConfig credentialsConfig;
@Override
public SslCredentials getSslCredentials() {
return this.credentialsConfig.getCredentials();
}
}

69
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java

@ -18,10 +18,16 @@ package org.thingsboard.server.transport.lwm2m.config;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import javax.annotation.PostConstruct;
import java.io.InputStream;
@ -64,26 +70,6 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
@Value("${transport.lwm2m.clean_period_in_sec:}")
private int cleanPeriodInSec;
@Getter
@Value("${transport.lwm2m.security.key_store_type:}")
private String keyStoreType;
@Getter
@Value("${transport.lwm2m.security.key_store:}")
private String keyStoreFilePath;
@Getter
@Setter
private KeyStore keyStoreValue;
@Getter
@Value("${transport.lwm2m.security.key_store_password:}")
private String keyStorePassword;
@Getter
@Value("${transport.lwm2m.security.root_alias:}")
private String rootCertificateAlias;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer id;
@ -104,14 +90,6 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
@Value("${transport.lwm2m.server.security.bind_port:}")
private Integer securePort;
@Getter
@Value("${transport.lwm2m.server.security.key_alias:}")
private String certificateAlias;
@Getter
@Value("${transport.lwm2m.server.security.key_password:}")
private String certificatePassword;
@Getter
@Value("${transport.lwm2m.log_max_length:}")
private int logMaxLength;
@ -124,15 +102,32 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
@Value("${transport.lwm2m.paging_transmission_window:10000}")
private long pagingTransmissionWindow;
@PostConstruct
public void init() {
try {
InputStream keyStoreInputStream = ResourceUtils.getInputStream(this, keyStoreFilePath);
keyStoreValue = KeyStore.getInstance(keyStoreType);
keyStoreValue.load(keyStoreInputStream, keyStorePassword == null ? null : keyStorePassword.toCharArray());
} catch (Exception e) {
log.info("Unable to lookup LwM2M keystore. Reason: {}, {}", keyStoreFilePath, e.getMessage());
}
@Bean
@ConfigurationProperties(prefix = "transport.lwm2m.server.security.credentials")
public SslCredentialsConfig lwm2mServerCredentials() {
return new SslCredentialsConfig("LWM2M Server DTLS Credentials", false);
}
@Autowired
@Qualifier("lwm2mServerCredentials")
private SslCredentialsConfig credentialsConfig;
@Bean
@ConfigurationProperties(prefix = "transport.lwm2m.security.trust-credentials")
public SslCredentialsConfig lwm2mTrustCredentials() {
return new SslCredentialsConfig("LWM2M Trust Credentials", true);
}
@Autowired
@Qualifier("lwm2mTrustCredentials")
private SslCredentialsConfig trustCredentialsConfig;
@Override
public SslCredentials getSslCredentials() {
return this.credentialsConfig.getCredentials();
}
public SslCredentials getTrustSslCredentials() {
return this.trustCredentialsConfig.getCredentials();
}
}

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -87,12 +87,8 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
try {
/* by default trust all */
X509Certificate[] trustedCertificates = new X509Certificate[0];
if (config.getKeyStoreValue() != null) {
X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
if (rootCAX509Cert != null) {
trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
}
if (config.getTrustSslCredentials() != null) {
trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates();
}
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates);
} catch (Exception e) {

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -29,6 +29,7 @@ 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.DataConstants;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
@ -141,7 +142,11 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
}
private void setServerWithCredentials(LeshanServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) {
if (config.getKeyStoreValue() != null && this.setBuilderX509(builder)) {
if (this.config.getSslCredentials() != null) {
SslCredentials sslCredentials = this.config.getSslCredentials();
builder.setPublicKey(sslCredentials.getPublicKey());
builder.setPrivateKey(sslCredentials.getPrivateKey());
builder.setCertificateChain(sslCredentials.getCertificateChain());
dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
builder.setAuthorizer(authorizer);
dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES);
@ -153,26 +158,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
}
}
private boolean setBuilderX509(LeshanServerBuilder builder) {
try {
X509Certificate[] certificateChain = SslContextUtil.asX509Certificates(config.getKeyStoreValue().getCertificateChain(config.getCertificateAlias()));
X509Certificate serverCertificate = certificateChain[0];
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(certificateChain);
return true;
} else {
return false;
}
} catch (Exception ex) {
log.error("[{}] Unable to load KeyStore files server", ex.getMessage());
return false;
}
}
@Override
public String getName() {
return DataConstants.LWM2M_TRANSPORT_NAME;

42
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java

@ -18,16 +18,20 @@ package org.thingsboard.server.transport.mqtt;
import io.netty.handler.ssl.SslHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.config.ssl.SslCredentials;
import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -38,8 +42,6 @@ import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@ -56,18 +58,20 @@ public class MqttSslHandlerProvider {
@Value("${transport.mqtt.ssl.protocol}")
private String sslProtocol;
@Value("${transport.mqtt.ssl.key_store}")
private String keyStoreFile;
@Value("${transport.mqtt.ssl.key_store_password}")
private String keyStorePassword;
@Value("${transport.mqtt.ssl.key_password}")
private String keyPassword;
@Value("${transport.mqtt.ssl.key_store_type}")
private String keyStoreType;
@Autowired
private TransportService transportService;
@Bean
@ConfigurationProperties(prefix = "transport.mqtt.ssl.credentials")
public SslCredentialsConfig mqttSslCredentials() {
return new SslCredentialsConfig("MQTT SSL Credentials", false);
}
@Autowired
@Qualifier("mqttSslCredentials")
private SslCredentialsConfig mqttSslCredentialsConfig;
private SSLContext sslContext;
public SslHandler getSslHandler() {
@ -86,19 +90,9 @@ public class MqttSslHandlerProvider {
private SSLContext createSslContext() {
try {
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStore = KeyStore.getInstance(keyStoreType);
try (InputStream tsFileInputStream = ResourceUtils.getInputStream(this, keyStoreFile)) {
trustStore.load(tsFileInputStream, keyStorePassword.toCharArray());
}
tmFactory.init(trustStore);
KeyStore ks = KeyStore.getInstance(keyStoreType);
try (InputStream ksFileInputStream = ResourceUtils.getInputStream(this, keyStoreFile)) {
ks.load(ksFileInputStream, keyStorePassword.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyPassword.toCharArray());
SslCredentials sslCredentials = this.mqttSslCredentialsConfig.getCredentials();
TrustManagerFactory tmFactory = sslCredentials.createTrustManagerFactory();
KeyManagerFactory kmf = sslCredentials.createKeyManagerFactory();
KeyManager[] km = kmf.getKeyManagers();
TrustManager x509wrapped = getX509TrustManager(tmFactory);

8
common/transport/transport-api/pom.xml

@ -129,6 +129,14 @@
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-cf</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
</dependencies>
<build>

191
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java

@ -0,0 +1,191 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
import org.thingsboard.server.common.data.StringUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
public abstract class AbstractSslCredentials implements SslCredentials {
private char[] keyPasswordArray;
private KeyStore keyStore;
private PrivateKey privateKey;
private PublicKey publicKey;
private X509Certificate[] chain;
private X509Certificate[] trusts;
@Override
public void init(boolean trustsOnly) throws IOException, GeneralSecurityException {
String keyPassword = getKeyPassword();
if (StringUtils.isEmpty(keyPassword)) {
this.keyPasswordArray = new char[0];
} else {
this.keyPasswordArray = keyPassword.toCharArray();
}
this.keyStore = this.loadKeyStore(trustsOnly, this.keyPasswordArray);
Set<X509Certificate> trustedCerts = getTrustedCerts(this.keyStore);
this.trusts = trustedCerts.toArray(new X509Certificate[0]);
if (!trustsOnly) {
PrivateKeyEntry privateKeyEntry = null;
String keyAlias = this.getKeyAlias();
if (!StringUtils.isEmpty(keyAlias)) {
privateKeyEntry = tryGetPrivateKeyEntry(this.keyStore, keyAlias, this.keyPasswordArray);
} else {
for (Enumeration<String> e = this.keyStore.aliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
privateKeyEntry = tryGetPrivateKeyEntry(this.keyStore, alias, this.keyPasswordArray);
if (privateKeyEntry != null) {
break;
}
}
}
if (privateKeyEntry == null) {
throw new IllegalArgumentException("Failed to get private key from the keystore or pem files. " +
"Please check if the private key exists in the keystore or pem files and if the provided private key password is valid.");
}
this.chain = asX509Certificates(privateKeyEntry.getCertificateChain());
this.privateKey = privateKeyEntry.getPrivateKey();
if (this.chain.length > 0) {
this.publicKey = this.chain[0].getPublicKey();
}
}
}
@Override
public PrivateKey getPrivateKey() {
return this.privateKey;
}
@Override
public PublicKey getPublicKey() {
return this.publicKey;
}
@Override
public X509Certificate[] getCertificateChain() {
return this.chain;
}
@Override
public X509Certificate[] getTrustedCertificates() {
return this.trusts;
}
@Override
public TrustManagerFactory createTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init(this.keyStore);
return tmFactory;
}
@Override
public KeyManagerFactory createKeyManagerFactory() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(this.keyStore, this.keyPasswordArray);
return kmf;
}
protected abstract boolean canUse();
protected abstract String getKeyPassword();
protected abstract String getKeyAlias();
protected abstract KeyStore loadKeyStore(boolean isPrivateKeyRequired, char[] keyPasswordArray) throws IOException, GeneralSecurityException;
private static X509Certificate[] asX509Certificates(Certificate[] certificates) {
if (null == certificates || 0 == certificates.length) {
throw new IllegalArgumentException("certificates missing!");
}
X509Certificate[] x509Certificates = new X509Certificate[certificates.length];
for (int index = 0; certificates.length > index; ++index) {
if (null == certificates[index]) {
throw new IllegalArgumentException("[" + index + "] is null!");
}
try {
x509Certificates[index] = (X509Certificate) certificates[index];
} catch (ClassCastException e) {
throw new IllegalArgumentException("[" + index + "] is not a x509 certificate! Instead it's a "
+ certificates[index].getClass().getName());
}
}
return x509Certificates;
}
private static PrivateKeyEntry tryGetPrivateKeyEntry(KeyStore keyStore, String alias, char[] pwd) {
PrivateKeyEntry entry = null;
try {
if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
try {
entry = (KeyStore.PrivateKeyEntry) keyStore
.getEntry(alias, new KeyStore.PasswordProtection(pwd));
} catch (UnsupportedOperationException e) {
PrivateKey key = (PrivateKey) keyStore.getKey(alias, pwd);
Certificate[] certs = keyStore.getCertificateChain(alias);
entry = new KeyStore.PrivateKeyEntry(key, certs);
}
}
} catch (KeyStoreException | UnrecoverableEntryException | NoSuchAlgorithmException ignored) {}
return entry;
}
private static Set<X509Certificate> getTrustedCerts(KeyStore ks) {
Set<X509Certificate> set = new HashSet<>();
try {
for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
if (ks.isCertificateEntry(alias)) {
Certificate cert = ks.getCertificate(alias);
if (cert instanceof X509Certificate) {
set.add((X509Certificate)cert);
}
} else if (ks.isKeyEntry(alias)) {
Certificate[] certs = ks.getCertificateChain(alias);
if ((certs != null) && (certs.length > 0) &&
(certs[0] instanceof X509Certificate)) {
set.add((X509Certificate)certs[0]);
}
}
}
} catch (KeyStoreException ignored) {}
return Collections.unmodifiableSet(set);
}
}

52
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/KeystoreSslCredentials.java

@ -0,0 +1,52 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.data.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
@Data
@EqualsAndHashCode(callSuper = false)
public class KeystoreSslCredentials extends AbstractSslCredentials {
private String type;
private String storeFile;
private String storePassword;
private String keyPassword;
private String keyAlias;
@Override
protected boolean canUse() {
return ResourceUtils.resourceExists(this, this.storeFile);
}
@Override
protected KeyStore loadKeyStore(boolean trustsOnly, char[] keyPasswordArray) throws IOException, GeneralSecurityException {
String keyStoreType = StringUtils.isEmpty(this.type) ? KeyStore.getDefaultType() : this.type;
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
try (InputStream tsFileInputStream = ResourceUtils.getInputStream(this, this.storeFile)) {
keyStore.load(tsFileInputStream, StringUtils.isEmpty(this.storePassword) ? new char[0] : this.storePassword.toCharArray());
}
return keyStore;
}
}

130
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/PemSslCredentials.java

@ -0,0 +1,130 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.thingsboard.server.common.data.ResourceUtils;
import org.thingsboard.server.common.data.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Data
@EqualsAndHashCode(callSuper = false)
public class PemSslCredentials extends AbstractSslCredentials {
private String certFile;
private String keyFile;
private String keyPassword;
private final String keyAlias = "serveralias";
@Override
protected boolean canUse() {
return ResourceUtils.resourceExists(this, this.certFile);
}
@Override
protected KeyStore loadKeyStore(boolean trustsOnly, char[] keyPasswordArray) throws IOException, GeneralSecurityException {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
List<X509Certificate> certificates = new ArrayList<>();
PrivateKey privateKey = null;
JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter();
try (InputStream inStream = ResourceUtils.getInputStream(this, this.certFile)) {
try (PEMParser pemParser = new PEMParser(new InputStreamReader(inStream))) {
Object object;
while((object = pemParser.readObject()) != null) {
if (object instanceof X509CertificateHolder) {
X509Certificate x509Cert = certConverter.getCertificate((X509CertificateHolder) object);
certificates.add(x509Cert);
} else if (object instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(keyPasswordArray);
privateKey = keyConverter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate();
} else if (object instanceof PEMKeyPair) {
privateKey = keyConverter.getKeyPair((PEMKeyPair) object).getPrivate();
} else if (object instanceof PrivateKeyInfo) {
privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) object);
}
}
}
}
if (privateKey == null && !StringUtils.isEmpty(this.keyFile)) {
if (ResourceUtils.resourceExists(this, this.keyFile)) {
try (InputStream inStream = ResourceUtils.getInputStream(this, this.keyFile)) {
try (PEMParser pemParser = new PEMParser(new InputStreamReader(inStream))) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof PEMEncryptedKeyPair) {
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(keyPasswordArray);
privateKey = keyConverter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate();
break;
} else if (object instanceof PEMKeyPair) {
privateKey = keyConverter.getKeyPair((PEMKeyPair) object).getPrivate();
break;
} else if (object instanceof PrivateKeyInfo) {
privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) object);
}
}
}
}
}
}
if (certificates.isEmpty()) {
throw new IllegalArgumentException("No certificates found in certFile: " + this.certFile);
}
if (privateKey == null && !trustsOnly) {
throw new IllegalArgumentException("Unable to load private key neither from certFile: " + this.certFile + " nor from keyFile: " + this.keyFile);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
List<Certificate> unique = certificates.stream().distinct().collect(Collectors.toList());
for (int i = 0; i < unique.size(); i++) {
keyStore.setCertificateEntry("root-" + i, unique.get(i));
}
if (privateKey != null) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
CertPath certPath = factory.generateCertPath(certificates);
List<? extends Certificate> path = certPath.getCertificates();
Certificate[] x509Certificates = path.toArray(new Certificate[0]);
keyStore.setKeyEntry(this.keyAlias, privateKey, keyPasswordArray, x509Certificates);
}
return keyStore;
}
}

45
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentials.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
public interface SslCredentials {
void init(boolean trustsOnly) throws IOException, GeneralSecurityException;
PrivateKey getPrivateKey();
PublicKey getPublicKey();
X509Certificate[] getCertificateChain();
X509Certificate[] getTrustedCertificates();
TrustManagerFactory createTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException;
KeyManagerFactory createKeyManagerFactory() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException;
}

66
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java

@ -0,0 +1,66 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
@Slf4j
@Data
public class SslCredentialsConfig {
private boolean enabled = true;
private SslCredentialsType type;
private PemSslCredentials pem;
private KeystoreSslCredentials keystore;
private SslCredentials credentials;
private final String name;
private final boolean trustsOnly;
public SslCredentialsConfig(String name, boolean trustsOnly) {
this.name = name;
this.trustsOnly = trustsOnly;
}
@PostConstruct
public void init() {
if (this.enabled) {
log.info("{}: Initializing SSL credentials.", name);
if (SslCredentialsType.PEM.equals(type) && pem.canUse()) {
this.credentials = this.pem;
} else if (keystore.canUse()) {
if (SslCredentialsType.PEM.equals(type)) {
log.warn("{}: Specified PEM configuration is not valid. Using SSL keystore configuration as fallback.", name);
}
this.credentials = this.keystore;
} else {
throw new RuntimeException(name + ": Invalid SSL credentials configuration. None of the PEM or KEYSTORE configurations can be used!");
}
try {
this.credentials.init(this.trustsOnly);
} catch (Exception e) {
throw new RuntimeException(name + ": Failed to init SSL credentials configuration.", e);
}
} else {
log.info("{}: Skipping initialization of disabled SSL credentials.", name);
}
}
}

21
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsType.java

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.transport.config.ssl;
public enum SslCredentialsType {
PEM,
KEYSTORE
}

36
transport/coap/src/main/resources/tb-coap-transport.yml

@ -98,17 +98,33 @@ transport:
bind_address: "${COAP_DTLS_BIND_ADDRESS:0.0.0.0}"
# CoAP DTLS bind port
bind_port: "${COAP_DTLS_BIND_PORT:5684}"
# Path to the key store that holds the certificate
key_store: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
# Password used to access the key store
key_store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
# Server DTLS credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${COAP_DTLS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${COAP_DTLS_PEM_CERT:coapserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${COAP_DTLS_PEM_KEY:coapserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${COAP_DTLS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${COAP_DTLS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${COAP_DTLS_KEY_STORE:coapserver.jks}"
# Password used to access the key store
store_password: "${COAP_DTLS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
x509:
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
sessions:

83
transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml

@ -111,9 +111,33 @@ transport:
security:
bind_address: "${LWM2M_SECURITY_BIND_ADDRESS:0.0.0.0}"
bind_port: "${LWM2M_SECURITY_BIND_PORT:5686}"
# Server X509 Certificates support
credentials:
# Whether to enable LWM2M server X509 Certificate/RPK support
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${LWM2M_SERVER_PEM_CERT:lwm2mserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${LWM2M_SERVER_PEM_KEY:lwm2mserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${LWM2M_SERVER_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${LWM2M_SERVER_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_SERVER_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_SERVER_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${LWM2M_SERVER_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${LWM2M_SERVER_KEY_ALIAS:server}"
# Only Certificate_x509:
key_alias: "${LWM2M_SERVER_KEY_ALIAS:server}"
key_password: "${LWM2M_SERVER_KEY_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,18 +147,51 @@ transport:
security:
bind_address: "${LWM2M_BS_SECURITY_BIND_ADDRESS:0.0.0.0}"
bind_port: "${LWM2M_BS_SECURITY_BIND_PORT:5688}"
# Only Certificate_x509:
key_alias: "${LWM2M_BS_KEY_ALIAS:bootstrap}"
key_password: "${LWM2M_BS_KEY_PASSWORD:server_ks_password}"
# Bootstrap server X509 Certificates support
credentials:
# Whether to enable LWM2M bootstrap server X509 Certificate/RPK support
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${LWM2M_BS_PEM_CERT:lwm2mserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${LWM2M_BS_PEM_KEY:lwm2mserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${LWM2M_BS_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${LWM2M_BS_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${LWM2M_BS_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_BS_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${LWM2M_BS_KEY_PASSWORD:server_key_password}"
# Key alias
key_alias: "${LWM2M_BS_KEY_ALIAS:bootstrap}"
security:
# 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}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store: "${LWM2M_KEYSTORE:lwm2mserver.jks}"
key_store_password: "${LWM2M_KEYSTORE_PASSWORD:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA_ALIAS:rootca}"
# X509 trust certificates
trust-credentials:
# Whether to load X509 trust certificates
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}"
# Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore)
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
# PEM certificates
pem:
# Path to the certificates file (holds trust certificates)
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}"
# Keystore with trust certificates
keystore:
# Type of the key store
type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the X509 certificates
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}"
# Password used to access the key store
store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
timeout: "${LWM2M_TIMEOUT:120000}"

30
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -106,14 +106,28 @@ transport:
bind_port: "${MQTT_SSL_BIND_PORT:8883}"
# SSL protocol: See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext
protocol: "${MQTT_SSL_PROTOCOL:TLSv1.2}"
# Path to the key store that holds the SSL certificate
key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${MQTT_SSL_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
pem:
# Path to the server certificate file (holds server certificate or certificate chain, may include server private key)
cert_file: "${MQTT_SSL_PEM_CERT:mqttserver.pem}"
# Path to the server certificate private key file (optional)
key_file: "${MQTT_SSL_PEM_KEY:mqttserver_key.pem}"
# Server certificate private key password (optional)
key_password: "${MQTT_SSL_PEM_KEY_PASSWORD:server_key_password}"
# Keystore server credentials
keystore:
# Type of the key store
type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the SSL certificate
store_file: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
sessions:

Loading…
Cancel
Save