Browse Source
* dtls init commit * added fixes after review * fix typo * changed translation for DeviceCredentialsType.X509_CERTIFICATEpull/4345/head
committed by
GitHub
19 changed files with 1050 additions and 113 deletions
@ -0,0 +1,161 @@ |
|||
/** |
|||
* 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.transport.coap; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.californium.elements.util.CertPathUtil; |
|||
import org.eclipse.californium.scandium.dtls.AlertMessage; |
|||
import org.eclipse.californium.scandium.dtls.CertificateMessage; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.CertificateVerificationResult; |
|||
import org.eclipse.californium.scandium.dtls.ConnectionId; |
|||
import org.eclipse.californium.scandium.dtls.DTLSSession; |
|||
import org.eclipse.californium.scandium.dtls.HandshakeException; |
|||
import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; |
|||
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; |
|||
import org.eclipse.californium.scandium.util.ServerNames; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
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.SessionInfoCreator; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.common.transport.util.SslUtil; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
|
|||
import javax.security.auth.x500.X500Principal; |
|||
import java.security.cert.CertPath; |
|||
import java.security.cert.CertificateEncodingException; |
|||
import java.security.cert.CertificateExpiredException; |
|||
import java.security.cert.CertificateNotYetValidException; |
|||
import java.security.cert.X509Certificate; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { |
|||
|
|||
private final TbCoapDtlsSessionInMemoryStorage tbCoapDtlsSessionInMemoryStorage; |
|||
|
|||
private TransportService transportService; |
|||
private TbServiceInfoProvider serviceInfoProvider; |
|||
private boolean skipValidityCheckForClientCert; |
|||
|
|||
public TbCoapDtlsCertificateVerifier(TransportService transportService, TbServiceInfoProvider serviceInfoProvider, long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout, boolean skipValidityCheckForClientCert) { |
|||
this.transportService = transportService; |
|||
this.serviceInfoProvider = serviceInfoProvider; |
|||
this.skipValidityCheckForClientCert = skipValidityCheckForClientCert; |
|||
this.tbCoapDtlsSessionInMemoryStorage = new TbCoapDtlsSessionInMemoryStorage(dtlsSessionInactivityTimeout, dtlsSessionReportTimeout); |
|||
} |
|||
|
|||
@Override |
|||
public List<CertificateType> getSupportedCertificateType() { |
|||
return Collections.singletonList(CertificateType.X_509); |
|||
} |
|||
|
|||
@Override |
|||
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) { |
|||
try { |
|||
String credentialsBody = null; |
|||
CertPath certpath = message.getCertificateChain(); |
|||
X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]); |
|||
for (X509Certificate cert : chain) { |
|||
try { |
|||
if (!skipValidityCheckForClientCert) { |
|||
cert.checkValidity(); |
|||
} |
|||
String strCert = SslUtil.getCertificateString(cert); |
|||
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); |
|||
final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1]; |
|||
CountDownLatch latch = new CountDownLatch(1); |
|||
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(), |
|||
new TransportServiceCallback<>() { |
|||
@Override |
|||
public void onSuccess(ValidateDeviceCredentialsResponse msg) { |
|||
if (!StringUtils.isEmpty(msg.getCredentials())) { |
|||
deviceCredentialsResponse[0] = msg; |
|||
} |
|||
latch.countDown(); |
|||
} |
|||
|
|||
@Override |
|||
public void onError(Throwable e) { |
|||
log.error(e.getMessage(), e); |
|||
latch.countDown(); |
|||
} |
|||
}); |
|||
latch.await(10, TimeUnit.SECONDS); |
|||
ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0]; |
|||
if (msg != null && strCert.equals(msg.getCredentials())) { |
|||
credentialsBody = msg.getCredentials(); |
|||
DeviceProfile deviceProfile = msg.getDeviceProfile(); |
|||
if (msg.hasDeviceInfo() && deviceProfile != null) { |
|||
TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, serviceInfoProvider.getServiceId(), UUID.randomUUID()); |
|||
tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(sessionInfoProto, deviceProfile)); |
|||
} |
|||
break; |
|||
} |
|||
} catch (InterruptedException | |
|||
CertificateEncodingException | |
|||
CertificateExpiredException | |
|||
CertificateNotYetValidException e) { |
|||
log.error(e.getMessage(), e); |
|||
} |
|||
} |
|||
if (credentialsBody == null) { |
|||
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.BAD_CERTIFICATE, |
|||
session.getPeer()); |
|||
throw new HandshakeException("Certificate chain could not be validated", alert); |
|||
} else { |
|||
return new CertificateVerificationResult(cid, certpath, null); |
|||
} |
|||
} catch (HandshakeException e) { |
|||
log.trace("Certificate validation failed!", e); |
|||
return new CertificateVerificationResult(cid, e, null); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public List<X500Principal> getAcceptedIssuers() { |
|||
return CertPathUtil.toSubjects(null); |
|||
} |
|||
|
|||
@Override |
|||
public void setResultHandler(HandshakeResultHandler resultHandler) { |
|||
// empty implementation
|
|||
} |
|||
|
|||
public ConcurrentMap<String, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionIdsMap() { |
|||
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionIdMap(); |
|||
} |
|||
|
|||
public void evictTimeoutSessions() { |
|||
tbCoapDtlsSessionInMemoryStorage.evictTimeoutSessions(); |
|||
} |
|||
|
|||
public long getDtlsSessionReportTimeout() { |
|||
return tbCoapDtlsSessionInMemoryStorage.getDtlsSessionReportTimeout(); |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* 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.transport.coap; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class TbCoapDtlsSessionInMemoryStorage { |
|||
|
|||
private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap = new ConcurrentHashMap<>(); |
|||
private long dtlsSessionInactivityTimeout; |
|||
private long dtlsSessionReportTimeout; |
|||
|
|||
|
|||
public TbCoapDtlsSessionInMemoryStorage(long dtlsSessionInactivityTimeout, long dtlsSessionReportTimeout) { |
|||
this.dtlsSessionInactivityTimeout = dtlsSessionInactivityTimeout; |
|||
this.dtlsSessionReportTimeout = dtlsSessionReportTimeout; |
|||
} |
|||
|
|||
public void put(String dtlsSessionId, TbCoapDtlsSessionInfo dtlsSessionInfo) { |
|||
log.trace("DTLS session added to in-memory store: [{}] timestamp: [{}]", dtlsSessionId, dtlsSessionInfo.getLastActivityTime()); |
|||
dtlsSessionIdMap.putIfAbsent(dtlsSessionId, dtlsSessionInfo); |
|||
} |
|||
|
|||
public void evictTimeoutSessions() { |
|||
long expTime = System.currentTimeMillis() - dtlsSessionInactivityTimeout; |
|||
dtlsSessionIdMap.entrySet().removeIf(entry -> { |
|||
if (entry.getValue().getLastActivityTime() < expTime) { |
|||
log.trace("DTLS session was removed from in-memory store: [{}]", entry.getKey()); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* 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.transport.coap; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
@Data |
|||
public class TbCoapDtlsSessionInfo { |
|||
|
|||
private TransportProtos.SessionInfoProto sessionInfoProto; |
|||
private DeviceProfile deviceProfile; |
|||
private long lastActivityTime; |
|||
|
|||
|
|||
public TbCoapDtlsSessionInfo(TransportProtos.SessionInfoProto sessionInfoProto, DeviceProfile deviceProfile) { |
|||
this.sessionInfoProto = sessionInfoProto; |
|||
this.deviceProfile = deviceProfile; |
|||
this.lastActivityTime = System.currentTimeMillis(); |
|||
} |
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
/** |
|||
* 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.transport.coap; |
|||
|
|||
import com.google.common.io.Resources; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.TransportService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.InetAddress; |
|||
import java.net.InetSocketAddress; |
|||
import java.net.UnknownHostException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.Optional; |
|||
|
|||
@Slf4j |
|||
@ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.coap.enabled}'=='true')") |
|||
@Component |
|||
public class TbCoapDtlsSettings { |
|||
|
|||
@Value("${transport.coap.bind_address}") |
|||
private String host; |
|||
|
|||
@Value("${transport.coap.bind_port}") |
|||
private Integer port; |
|||
|
|||
@Value("${transport.coap.dtls.mode}") |
|||
private String mode; |
|||
|
|||
@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; |
|||
|
|||
@Value("${transport.coap.dtls.key_alias}") |
|||
private String keyAlias; |
|||
|
|||
@Value("${transport.coap.dtls.skip_validity_check_for_client_cert}") |
|||
private boolean skipValidityCheckForClientCert; |
|||
|
|||
@Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}") |
|||
private long dtlsSessionInactivityTimeout; |
|||
|
|||
@Value("${transport.coap.dtls.x509.dtls_session_report_timeout}") |
|||
private long dtlsSessionReportTimeout; |
|||
|
|||
@Autowired |
|||
private TransportService transportService; |
|||
|
|||
@Autowired |
|||
private TbServiceInfoProvider serviceInfoProvider; |
|||
|
|||
public DtlsConnectorConfig dtlsConnectorConfig() throws UnknownHostException { |
|||
Optional<SecurityMode> securityModeOpt = SecurityMode.parse(mode); |
|||
if (securityModeOpt.isEmpty()) { |
|||
log.warn("Incorrect configuration of securityMode {}", mode); |
|||
throw new RuntimeException("Failed to parse mode property: " + mode + "!"); |
|||
} else { |
|||
DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(); |
|||
configBuilder.setAddress(getInetSocketAddress()); |
|||
String keyStoreFilePath = Resources.getResource(keyStoreFile).getPath(); |
|||
SslContextUtil.Credentials serverCredentials = loadServerCredentials(keyStoreFilePath); |
|||
SecurityMode securityMode = securityModeOpt.get(); |
|||
if (securityMode.equals(SecurityMode.NO_AUTH)) { |
|||
configBuilder.setClientAuthenticationRequired(false); |
|||
configBuilder.setServerOnly(true); |
|||
} else { |
|||
configBuilder.setAdvancedCertificateVerifier( |
|||
new TbCoapDtlsCertificateVerifier( |
|||
transportService, |
|||
serviceInfoProvider, |
|||
dtlsSessionInactivityTimeout, |
|||
dtlsSessionReportTimeout, |
|||
skipValidityCheckForClientCert |
|||
) |
|||
); |
|||
} |
|||
configBuilder.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), |
|||
Collections.singletonList(CertificateType.X_509)); |
|||
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 void loadTrustedCertificates(DtlsConnectorConfig.Builder config, String keyStoreFilePath) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreFilePath, keyAlias, |
|||
keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
if (trustBuilder.hasTrusts()) { |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
} |
|||
} catch (GeneralSecurityException | IOException e) { |
|||
throw new RuntimeException("Failed to load trusted certificates due to: ", e); |
|||
} |
|||
} |
|||
|
|||
private InetSocketAddress getInetSocketAddress() throws UnknownHostException { |
|||
InetAddress addr = InetAddress.getByName(host); |
|||
return new InetSocketAddress(addr, port); |
|||
} |
|||
|
|||
private enum SecurityMode { |
|||
X509, |
|||
NO_AUTH; |
|||
|
|||
static Optional<SecurityMode> parse(String name) { |
|||
SecurityMode mode = null; |
|||
if (name != null) { |
|||
for (SecurityMode securityMode : SecurityMode.values()) { |
|||
if (securityMode.name().equalsIgnoreCase(name)) { |
|||
mode = securityMode; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return Optional.ofNullable(mode); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
/** |
|||
* 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.Principal; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class NoSecClient { |
|||
|
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public NoSecClient(String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys)); |
|||
this.coapClient = new CoapClient(uri); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) { |
|||
return "coap://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.NoSecClient " + |
|||
"host port accessToken clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String accessToken = args[2]; |
|||
String clientKeys = args[3]; |
|||
String sharedKeys = args[4]; |
|||
|
|||
NoSecClient client = new NoSecClient(host, port, accessToken, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
/** |
|||
* 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.core.network.CoapEndpoint; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.DTLSConnector; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.Principal; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class SecureClientNoAuth { |
|||
|
|||
private final DTLSConnector dtlsConnector; |
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public SecureClientNoAuth(DTLSConnector dtlsConnector, String host, int port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
this.dtlsConnector = dtlsConnector; |
|||
this.coapClient = getCoapClient(host, port, accessToken, clientKeys, sharedKeys); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private CoapClient getCoapClient(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, accessToken, clientKeys, sharedKeys)); |
|||
CoapClient client = new CoapClient(uri); |
|||
CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); |
|||
builder.setConnector(dtlsConnector); |
|||
|
|||
client.setEndpoint(builder.build()); |
|||
return client; |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String accessToken, String clientKeys, String sharedKeys) { |
|||
return "coaps://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientNoAuth " + |
|||
"host port accessToken keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String accessToken = args[2]; |
|||
String clientKeys = args[7]; |
|||
String sharedKeys = args[8]; |
|||
|
|||
String keyStoreUriPath = args[3]; |
|||
String keyStoreAlias = args[4]; |
|||
String trustedAliasPattern = args[5]; |
|||
String keyStorePassword = args[6]; |
|||
|
|||
|
|||
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); |
|||
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); |
|||
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); |
|||
SecureClientNoAuth client = new SecureClientNoAuth(dtlsConnector, host, port, accessToken, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
|
|||
private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials( |
|||
keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray()); |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); |
|||
} catch (GeneralSecurityException e) { |
|||
System.err.println("certificates are invalid!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} catch (IOException e) { |
|||
System.err.println("certificates are missing!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
/** |
|||
* 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.transport.coap.client; |
|||
|
|||
import org.eclipse.californium.core.CoapClient; |
|||
import org.eclipse.californium.core.CoapResponse; |
|||
import org.eclipse.californium.core.Utils; |
|||
import org.eclipse.californium.core.network.CoapEndpoint; |
|||
import org.eclipse.californium.elements.DtlsEndpointContext; |
|||
import org.eclipse.californium.elements.EndpointContext; |
|||
import org.eclipse.californium.elements.exception.ConnectorException; |
|||
import org.eclipse.californium.elements.util.SslContextUtil; |
|||
import org.eclipse.californium.scandium.DTLSConnector; |
|||
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
|||
import org.eclipse.californium.scandium.dtls.CertificateType; |
|||
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; |
|||
|
|||
import java.io.IOException; |
|||
import java.net.URI; |
|||
import java.net.URISyntaxException; |
|||
import java.security.GeneralSecurityException; |
|||
import java.security.Principal; |
|||
import java.security.cert.Certificate; |
|||
import java.util.Collections; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
public class SecureClientX509 { |
|||
|
|||
private final DTLSConnector dtlsConnector; |
|||
private ExecutorService executor = Executors.newFixedThreadPool(1); |
|||
private CoapClient coapClient; |
|||
|
|||
public SecureClientX509(DTLSConnector dtlsConnector, String host, int port, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
this.dtlsConnector = dtlsConnector; |
|||
this.coapClient = getCoapClient(host, port, clientKeys, sharedKeys); |
|||
} |
|||
|
|||
public void test() { |
|||
executor.submit(() -> { |
|||
try { |
|||
while (!Thread.interrupted()) { |
|||
CoapResponse response = null; |
|||
try { |
|||
response = coapClient.get(); |
|||
} catch (ConnectorException | IOException e) { |
|||
System.err.println("Error occurred while sending request: " + e); |
|||
System.exit(-1); |
|||
} |
|||
if (response != null) { |
|||
|
|||
System.out.println(response.getCode() + " - " + response.getCode().name()); |
|||
System.out.println(response.getOptions()); |
|||
System.out.println(response.getResponseText()); |
|||
System.out.println(); |
|||
System.out.println("ADVANCED:"); |
|||
EndpointContext context = response.advanced().getSourceContext(); |
|||
Principal identity = context.getPeerIdentity(); |
|||
if (identity != null) { |
|||
System.out.println(context.getPeerIdentity()); |
|||
} else { |
|||
System.out.println("anonymous"); |
|||
} |
|||
System.out.println(context.get(DtlsEndpointContext.KEY_CIPHER)); |
|||
System.out.println(Utils.prettyPrint(response)); |
|||
} else { |
|||
System.out.println("No response received."); |
|||
} |
|||
Thread.sleep(5000); |
|||
} |
|||
} catch (Exception e) { |
|||
System.out.println("Error occurred while sending COAP requests."); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private CoapClient getCoapClient(String host, Integer port, String clientKeys, String sharedKeys) throws URISyntaxException { |
|||
URI uri = new URI(getFutureUrl(host, port, clientKeys, sharedKeys)); |
|||
CoapClient client = new CoapClient(uri); |
|||
CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); |
|||
builder.setConnector(dtlsConnector); |
|||
|
|||
client.setEndpoint(builder.build()); |
|||
return client; |
|||
} |
|||
|
|||
private String getFutureUrl(String host, Integer port, String clientKeys, String sharedKeys) { |
|||
return "coaps://" + host + ":" + port + "/api/v1/attributes?clientKeys=" + clientKeys + "&sharedKeys=" + sharedKeys; |
|||
} |
|||
|
|||
public static void main(String[] args) throws URISyntaxException { |
|||
System.out.println("Usage: java -cp ... org.thingsboard.server.transport.coap.client.SecureClientX509 " + |
|||
"host port keyStoreUriPath keyStoreAlias trustedAliasPattern clientKeys sharedKeys"); |
|||
|
|||
String host = args[0]; |
|||
int port = Integer.parseInt(args[1]); |
|||
String clientKeys = args[6]; |
|||
String sharedKeys = args[7]; |
|||
|
|||
String keyStoreUriPath = args[2]; |
|||
String keyStoreAlias = args[3]; |
|||
String trustedAliasPattern = args[4]; |
|||
String keyStorePassword = args[5]; |
|||
|
|||
|
|||
DtlsConnectorConfig.Builder builder = new DtlsConnectorConfig.Builder(); |
|||
setupCredentials(builder, keyStoreUriPath, keyStoreAlias, trustedAliasPattern, keyStorePassword); |
|||
DTLSConnector dtlsConnector = new DTLSConnector(builder.build()); |
|||
SecureClientX509 client = new SecureClientX509(dtlsConnector, host, port, clientKeys, sharedKeys); |
|||
client.test(); |
|||
} |
|||
|
|||
private static void setupCredentials(DtlsConnectorConfig.Builder config, String keyStoreUriPath, String keyStoreAlias, String trustedAliasPattern, String keyStorePassword) { |
|||
StaticNewAdvancedCertificateVerifier.Builder trustBuilder = StaticNewAdvancedCertificateVerifier.builder(); |
|||
try { |
|||
SslContextUtil.Credentials serverCredentials = SslContextUtil.loadCredentials( |
|||
keyStoreUriPath, keyStoreAlias, keyStorePassword.toCharArray(), keyStorePassword.toCharArray()); |
|||
Certificate[] trustedCertificates = SslContextUtil.loadTrustedCertificates( |
|||
keyStoreUriPath, trustedAliasPattern, keyStorePassword.toCharArray()); |
|||
trustBuilder.setTrustedCertificates(trustedCertificates); |
|||
config.setAdvancedCertificateVerifier(trustBuilder.build()); |
|||
config.setIdentity(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(), Collections.singletonList(CertificateType.X_509)); |
|||
} catch (GeneralSecurityException e) { |
|||
System.err.println("certificates are invalid!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} catch (IOException e) { |
|||
System.err.println("certificates are missing!"); |
|||
throw new IllegalArgumentException(e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue