committed by
GitHub
8 changed files with 305 additions and 150 deletions
@ -0,0 +1,167 @@ |
|||
/** |
|||
* 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.lwm2m.bootstrap.secure; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
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.dtls.x509.StaticCertificateVerifier; |
|||
import org.eclipse.californium.scandium.util.ServerNames; |
|||
import org.eclipse.leshan.server.security.SecurityChecker; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.msg.EncryptionUtil; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.common.transport.util.SslUtil; |
|||
import org.thingsboard.server.queue.util.TbLwM2mBootstrapTransportComponent; |
|||
import org.thingsboard.server.transport.lwm2m.bootstrap.store.LwM2MBootstrapSecurityStore; |
|||
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
|||
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; |
|||
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; |
|||
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; |
|||
import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.security.auth.x500.X500Principal; |
|||
import java.security.PublicKey; |
|||
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.Arrays; |
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@TbLwM2mBootstrapTransportComponent |
|||
@RequiredArgsConstructor |
|||
public class TbLwM2MDtlsBootstrapCertificateVerifier implements NewAdvancedCertificateVerifier { |
|||
|
|||
private final LwM2MTransportServerConfig config; |
|||
private final LwM2MBootstrapSecurityStore bsSecurityStore; |
|||
|
|||
@SuppressWarnings("deprecation") |
|||
private StaticCertificateVerifier staticCertificateVerifier; |
|||
|
|||
@Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}") |
|||
private boolean skipValidityCheckForClientCert; |
|||
|
|||
@Override |
|||
public List<CertificateType> getSupportedCertificateType() { |
|||
return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); |
|||
} |
|||
|
|||
@SuppressWarnings("deprecation") |
|||
@PostConstruct |
|||
public void init() { |
|||
try { |
|||
/* by default trust all */ |
|||
if (config.getTrustSslCredentials() != null) { |
|||
X509Certificate[] trustedCertificates = config.getTrustSslCredentials().getTrustedCertificates(); |
|||
staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates); |
|||
} |
|||
} catch (Exception e) { |
|||
log.info("Failed to initialize the certificate verifier", e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, |
|||
boolean truncateCertificatePath, CertificateMessage message, |
|||
DTLSSession session) { |
|||
CertPath certChain = message.getCertificateChain(); |
|||
if (certChain == null) { |
|||
//We trust all RPK on this layer, and use TbLwM2MAuthorizer
|
|||
PublicKey publicKey = message.getPublicKey(); |
|||
return new CertificateVerificationResult(cid, publicKey, null); |
|||
} else { |
|||
try { |
|||
boolean x509CredentialsFound = false; |
|||
X509Certificate[] chain = certChain.getCertificates().toArray(new X509Certificate[0]); |
|||
for (X509Certificate cert : chain) { |
|||
try { |
|||
if (!skipValidityCheckForClientCert) { |
|||
cert.checkValidity(); |
|||
} |
|||
TbLwM2MSecurityInfo securityInfo = null; |
|||
// verify if trust
|
|||
if (staticCertificateVerifier != null) { |
|||
try { |
|||
staticCertificateVerifier.verifyCertificate(message, session); |
|||
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN"); |
|||
if (StringUtils.isNotEmpty(endpoint)) { |
|||
securityInfo = bsSecurityStore.getX509ByEndpoint(endpoint); |
|||
} |
|||
} catch (HandshakeException e) { |
|||
log.trace("Certificate validation failed.", e); |
|||
} |
|||
} |
|||
// if not trust or cert trust securityInfo == null
|
|||
if (securityInfo == null || securityInfo.getMsg() == null) { |
|||
String strCert = SslUtil.getCertificateString(cert); |
|||
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); |
|||
try { |
|||
securityInfo = bsSecurityStore.getX509ByEndpoint(sha3Hash); |
|||
} catch (LwM2MAuthException e) { |
|||
log.trace("Failed to find security info: [{}]", sha3Hash, e); |
|||
} |
|||
} |
|||
ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null; |
|||
if (msg != null && StringUtils.isNotEmpty(msg.getCredentials())) { |
|||
x509CredentialsFound = true; |
|||
break; |
|||
} |
|||
} catch (CertificateEncodingException | |
|||
CertificateExpiredException | |
|||
CertificateNotYetValidException e) { |
|||
log.trace("Failed to find security info: [{}]", cert.getSubjectX500Principal().getName(), e); |
|||
} |
|||
} |
|||
if (!x509CredentialsFound) { |
|||
AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR, |
|||
session.getPeer()); |
|||
throw new HandshakeException("x509 verification not enabled!", alert); |
|||
} |
|||
return new CertificateVerificationResult(cid, certChain, 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) { |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue