From c189a8c23caf88a3213b57eaad24229f2b31e81b Mon Sep 17 00:00:00 2001 From: Simon Bernard Date: Wed, 18 Nov 2020 18:24:14 +0100 Subject: [PATCH] Cf-2.5.0 : remove deprecate usage of TrustedRpkStore/CertificateVerifier --- .../CaliforniumEndpointsManager.java | 26 ++------ .../DefaultLeshanCertificateVerifier.java | 47 +++++++++++--- .../californium/LeshanServerBuilder.java | 62 +++++++++++------- .../LeshanBootstrapServerBuilder.java | 65 ++++++++++++------- 4 files changed, 124 insertions(+), 76 deletions(-) diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/CaliforniumEndpointsManager.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/CaliforniumEndpointsManager.java index 9fa169e878..0c3b1d8d0d 100644 --- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/CaliforniumEndpointsManager.java +++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/CaliforniumEndpointsManager.java @@ -34,7 +34,8 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; import org.eclipse.californium.scandium.dtls.pskstore.AdvancedSinglePskStore; -import org.eclipse.californium.scandium.dtls.rpkstore.TrustedRpkStore; +import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; +import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier; import org.eclipse.leshan.client.EndpointsManager; import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.servers.ServerIdentity.Role; @@ -112,23 +113,9 @@ public synchronized ServerIdentity createEndpoint(ServerInfo serverInfo) { newBuilder.setIdentity(serverInfo.privateKey, serverInfo.publicKey); // set RPK truststore final PublicKey expectedKey = serverInfo.serverPublicKey; - newBuilder.setRpkTrustStore(new TrustedRpkStore() { - @Override - public boolean isTrusted(RawPublicKeyIdentity id) { - PublicKey receivedKey = id.getKey(); - if (receivedKey == null) { - LOG.warn("The server public key is null {}", id); - return false; - } - if (!receivedKey.equals(expectedKey)) { - LOG.debug( - "Server public key received does match with the expected one.\nReceived: {}\nExpected: {}", - receivedKey, expectedKey); - return false; - } - return true; - } - }); + NewAdvancedCertificateVerifier rpkVerifier = new StaticNewAdvancedCertificateVerifier.Builder() + .setTrustedRPKs(new RawPublicKeyIdentity(expectedKey)).build(); + newBuilder.setAdvancedCertificateVerifier(rpkVerifier); serverIdentity = Identity.rpk(serverInfo.getAddress(), expectedKey); filterCipherSuites(newBuilder, dtlsConfigbuilder.getIncompleteConfig().getSupportedCipherSuites(), false, true); @@ -137,7 +124,8 @@ public boolean isTrusted(RawPublicKeyIdentity id) { newBuilder.setIdentity(serverInfo.privateKey, new Certificate[] { serverInfo.clientCertificate }); // set X509 verifier - newBuilder.setCertificateVerifier(new DefaultLeshanCertificateVerifier(serverInfo.serverCertificate)); + newBuilder.setAdvancedCertificateVerifier( + new DefaultLeshanCertificateVerifier(serverInfo.serverCertificate)); serverIdentity = Identity.x509(serverInfo.getAddress(), EndpointContextUtil.extractCN( ((X509Certificate) serverInfo.serverCertificate).getSubjectX500Principal().getName())); filterCipherSuites(newBuilder, dtlsConfigbuilder.getIncompleteConfig().getSupportedCipherSuites(), diff --git a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/DefaultLeshanCertificateVerifier.java b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/DefaultLeshanCertificateVerifier.java index 823dcd409a..f785c9c580 100644 --- a/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/DefaultLeshanCertificateVerifier.java +++ b/leshan-client-cf/src/main/java/org/eclipse/leshan/client/californium/DefaultLeshanCertificateVerifier.java @@ -1,34 +1,58 @@ package org.eclipse.leshan.client.californium; import java.security.cert.Certificate; -import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +import javax.security.auth.x500.X500Principal; import org.eclipse.californium.scandium.dtls.AlertMessage; import org.eclipse.californium.scandium.dtls.AlertMessage.AlertDescription; import org.eclipse.californium.scandium.dtls.AlertMessage.AlertLevel; 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.x509.CertificateVerifier; +import org.eclipse.californium.scandium.dtls.HandshakeResultHandler; +import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; +import org.eclipse.californium.scandium.util.ServerNames; + +public class DefaultLeshanCertificateVerifier implements NewAdvancedCertificateVerifier { -public class DefaultLeshanCertificateVerifier implements CertificateVerifier { private final Certificate expectedServerCertificate; + private final List supportedCertificateType; public DefaultLeshanCertificateVerifier(Certificate expectedServerCertificate) { this.expectedServerCertificate = expectedServerCertificate; + this.supportedCertificateType = new ArrayList<>(1); + this.supportedCertificateType.add(CertificateType.X_509); + } + + @Override + public List getAcceptedIssuers() { + return null; } @Override - public void verifyCertificate(CertificateMessage message, DTLSSession session) throws HandshakeException { + public List getSupportedCertificateType() { + return supportedCertificateType; + } + + @Override + public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, + Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) { // As specify in the LWM2M spec 1.0, we only support "domain-issued certificate" usage // Defined in : https://tools.ietf.org/html/rfc6698#section-2.1.1 (3 -- Certificate usage 3) // Get server certificate from certificate message - if (message.getCertificateChain().getCertificates().size() == 0) { + if (message.getCertificateChain() == null || message.getCertificateChain().getCertificates().size() == 0) { AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE, session.getPeer()); - throw new HandshakeException("Certificate chain could not be validated : server cert chain is empty", - alert); + HandshakeException handshakeException = new HandshakeException( + "Certificate chain could not be validated : server cert chain is empty", alert); + return new CertificateVerificationResult(cid, handshakeException, null); } Certificate receivedServerCertificate = message.getCertificateChain().getCertificates().get(0); @@ -36,14 +60,17 @@ public void verifyCertificate(CertificateMessage message, DTLSSession session) t if (!expectedServerCertificate.equals(receivedServerCertificate)) { AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE, session.getPeer()); - throw new HandshakeException( + HandshakeException handshakeException = new HandshakeException( "Certificate chain could not be validated: server certificate does not match expected one ('domain-issue certificate' usage)", alert); + return new CertificateVerificationResult(cid, handshakeException, null); } + + return new CertificateVerificationResult(cid, message.getCertificateChain(), null); } @Override - public X509Certificate[] getAcceptedIssuers() { - return null; + public void setResultHandler(HandshakeResultHandler resultHandler) { + // we don't use async mode. } } diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanServerBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanServerBuilder.java index c98eac8bdb..11a97d7d37 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanServerBuilder.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/LeshanServerBuilder.java @@ -34,6 +34,7 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder; import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.x509.BridgeCertificateVerifier; import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.californium.DefaultEndpointFactory; import org.eclipse.leshan.core.californium.EndpointFactory; @@ -401,6 +402,7 @@ public static NetworkConfig createDefaultNetworkConfig() { * @return the LWM2M server. * @throws IllegalStateException if builder configuration is not consistent. */ + @SuppressWarnings("deprecation") public LeshanServer build() { if (localAddress == null) localAddress = new InetSocketAddress(LwM2m.DEFAULT_COAP_PORT); @@ -467,20 +469,6 @@ public LeshanServer build() { if (incompleteConfig.getStaleConnectionThreshold() == null) dtlsConfigBuilder.setStaleConnectionThreshold(coapConfig.getLong(Keys.MAX_PEER_INACTIVITY_PERIOD)); - // handle trusted certificates - if (trustedCertificates != null) { - if (incompleteConfig.getCertificateVerifier() == null) { - if (incompleteConfig.getTrustStore() == null) { - dtlsConfigBuilder.setTrustStore(trustedCertificates); - } else if (!Arrays.equals(trustedCertificates, incompleteConfig.getTrustStore())) { - throw new IllegalStateException(String.format( - "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for trusted Certificates (trustStore) : \n%s != \n%s", - Arrays.toString(trustedCertificates), - Arrays.toString(incompleteConfig.getTrustStore()))); - } - } - } - // check conflict for private key if (privateKey != null) { if (incompleteConfig.getPrivateKey() != null && !incompleteConfig.getPrivateKey().equals(privateKey)) { @@ -497,10 +485,6 @@ public LeshanServer build() { publicKey, incompleteConfig.getPublicKey())); } - // by default trust all RPK - if (incompleteConfig.getRpkTrustStore() == null) { - dtlsConfigBuilder.setRpkTrustAll(); - } dtlsConfigBuilder.setIdentity(privateKey, publicKey); } // if in X.509 mode set the private key, certificate chain, public key is extracted from the certificate @@ -512,13 +496,47 @@ public LeshanServer build() { Arrays.toString(certificateChain), incompleteConfig.getCertificateChain())); } - // by default trust all RPK - if (incompleteConfig.getRpkTrustStore() == null) { - dtlsConfigBuilder.setRpkTrustAll(); - } dtlsConfigBuilder.setIdentity(privateKey, certificateChain, CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); } + + // handle trusted certificates or RPK + if (incompleteConfig.getAdvancedCertificateVerifier() != null) { + if (trustedCertificates != null) { + throw new IllegalStateException( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder: if a AdvancedCertificateVerifier is set, trustedCertificates must not be set."); + } + } else { + BridgeCertificateVerifier.Builder verifierBuilder = new BridgeCertificateVerifier.Builder(); + if (incompleteConfig.getRpkTrustStore() != null) { + verifierBuilder.setTrustedRPKs(incompleteConfig.getRpkTrustStore()); + } else { + // by default trust all RPK + verifierBuilder.setTrustAllRPKs(); + } + if (incompleteConfig.getTrustStore() != null) { + if (trustedCertificates != null + && !Arrays.equals(trustedCertificates, incompleteConfig.getTrustStore())) { + throw new IllegalStateException(String.format( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for trusted Certificates (trustStore) : \n%s != \n%s", + Arrays.toString(trustedCertificates), + Arrays.toString(incompleteConfig.getTrustStore()))); + } + verifierBuilder.setTrustedCertificates(incompleteConfig.getTrustStore()); + } else { + if (trustedCertificates != null) { + verifierBuilder.setTrustedCertificates(trustedCertificates); + } + } + if (incompleteConfig.getCertificateVerifier() != null) { + if (trustedCertificates != null) { + throw new IllegalStateException( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder: if a CertificateVerifier is set, trustedCertificates must not be set."); + } + verifierBuilder.setCertificateVerifier(incompleteConfig.getCertificateVerifier()); + } + dtlsConfigBuilder.setAdvancedCertificateVerifier(verifierBuilder.build()); + } } // Deactivate SNI by default diff --git a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java index 223965a551..67337e128d 100644 --- a/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java +++ b/leshan-server-cf/src/main/java/org/eclipse/leshan/server/californium/bootstrap/LeshanBootstrapServerBuilder.java @@ -31,6 +31,7 @@ import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder; import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.x509.BridgeCertificateVerifier; import org.eclipse.leshan.core.LwM2m; import org.eclipse.leshan.core.californium.DefaultEndpointFactory; import org.eclipse.leshan.core.californium.EndpointFactory; @@ -398,6 +399,7 @@ public NetworkConfig createDefaultNetworkConfig() { * @return the LWM2M Bootstrap server. * @throws IllegalStateException if builder configuration is not consistent. */ + @SuppressWarnings("deprecation") public LeshanBootstrapServer build() { if (localAddress == null) localAddress = new InetSocketAddress(LwM2m.DEFAULT_COAP_PORT); @@ -462,23 +464,6 @@ public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrap if (incompleteConfig.getStaleConnectionThreshold() == null) dtlsConfigBuilder.setStaleConnectionThreshold(coapConfig.getLong(Keys.MAX_PEER_INACTIVITY_PERIOD)); - // handle trusted certificates - if (trustedCertificates != null) { - if (incompleteConfig.getCertificateVerifier() == null) { - if (incompleteConfig.getTrustStore() == null) { - dtlsConfigBuilder.setTrustStore(trustedCertificates); - } else if (!Arrays.equals(trustedCertificates, incompleteConfig.getTrustStore())) { - throw new IllegalStateException(String.format( - "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for trusted Certificates (trustStore) : \n%s != \n%s", - Arrays.toString(trustedCertificates), - Arrays.toString(incompleteConfig.getTrustStore()))); - } - } else if (trustedCertificates != null) { - throw new IllegalStateException( - "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder: if a CertificateVerifier is set, trustedCertificates must not be set."); - } - } - if (privateKey != null) { // check conflict for private key if (incompleteConfig.getPrivateKey() != null && !incompleteConfig.getPrivateKey().equals(privateKey)) { @@ -494,10 +479,6 @@ public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrap "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for public key: %s != %s", publicKey, incompleteConfig.getPublicKey())); } - // by default trust all RPK - if (incompleteConfig.getRpkTrustStore() == null) { - dtlsConfigBuilder.setRpkTrustAll(); - } dtlsConfigBuilder.setIdentity(privateKey, publicKey); } // if in X.509 mode set the private key, certificate chain, public key is extracted from the certificate @@ -509,13 +490,47 @@ public BootstrapHandler create(BootstrapConfigurationStore store, LwM2mBootstrap Arrays.toString(certificateChain), incompleteConfig.getCertificateChain())); } - // by default trust all RPK - if (incompleteConfig.getRpkTrustStore() == null) { - dtlsConfigBuilder.setRpkTrustAll(); - } dtlsConfigBuilder.setIdentity(privateKey, certificateChain, CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY); } + + // handle trusted certificates or RPK + if (incompleteConfig.getAdvancedCertificateVerifier() != null) { + if (trustedCertificates != null) { + throw new IllegalStateException( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder: if a AdvancedCertificateVerifier is set, trustedCertificates must not be set."); + } + } else { + BridgeCertificateVerifier.Builder verifierBuilder = new BridgeCertificateVerifier.Builder(); + if (incompleteConfig.getRpkTrustStore() != null) { + verifierBuilder.setTrustedRPKs(incompleteConfig.getRpkTrustStore()); + } else { + // by default trust all RPK + verifierBuilder.setTrustAllRPKs(); + } + if (incompleteConfig.getTrustStore() != null) { + if (trustedCertificates != null + && !Arrays.equals(trustedCertificates, incompleteConfig.getTrustStore())) { + throw new IllegalStateException(String.format( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for trusted Certificates (trustStore) : \n%s != \n%s", + Arrays.toString(trustedCertificates), + Arrays.toString(incompleteConfig.getTrustStore()))); + } + verifierBuilder.setTrustedCertificates(incompleteConfig.getTrustStore()); + } else { + if (trustedCertificates != null) { + verifierBuilder.setTrustedCertificates(trustedCertificates); + } + } + if (incompleteConfig.getCertificateVerifier() != null) { + if (trustedCertificates != null) { + throw new IllegalStateException( + "Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder: if a CertificateVerifier is set, trustedCertificates must not be set."); + } + verifierBuilder.setCertificateVerifier(incompleteConfig.getCertificateVerifier()); + } + dtlsConfigBuilder.setAdvancedCertificateVerifier(verifierBuilder.build()); + } } // Bootstrap Server acts as Server only : It does not need to initiate handshake