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