/******************************************************************************* * Copyright (c) 2013-2015 Sierra Wireless and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.html. * * Contributors: * Zebra Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.leshan.integration.tests; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.spec.KeySpec; import java.util.List; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.CoapEndpoint; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.LwM2mId; import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.object.Device; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.core.request.BindingMode; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.impl.InMemorySecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.util.Hex; public class SecureIntegrationTestHelper extends IntegrationTestHelper { public static final String GOOD_PSK_ID = "Good_Client_identity"; public static final byte[] GOOD_PSK_KEY = Hex.decodeHex("73656372657450534b".toCharArray()); public static final String GOOD_ENDPOINT = "good_endpoint"; public static final String BAD_PSK_ID = "Bad_Client_identity"; public static final byte[] BAD_PSK_KEY = Hex.decodeHex("010101010101010101".toCharArray()); public static final String BAD_ENDPOINT = "bad_endpoint"; public final PublicKey clientPublicKey; public final PrivateKey clientPrivateKey; public final PublicKey serverPublicKey; public final PrivateKey serverPrivateKey; public final PrivateKey clientPrivateKeyFromCert; public final PrivateKey serverPrivateKeyFromCert; public final X509Certificate[] clientX509CertChain = new X509Certificate[2]; public final X509Certificate[] serverX509CertChain = new X509Certificate[2]; public final Certificate[] trustedCertificates = new Certificate[2]; public final X509Certificate clientCAX509Cert; public final X509Certificate serverCAX509Cert; public SecureIntegrationTestHelper() { // create client credentials try { // Get point values byte[] publicX = Hex .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray()); byte[] publicY = Hex .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray()); byte[] privateS = Hex .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray()); // Get Elliptic Curve Parameter spec for secp256r1 AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); algoParameters.init(new ECGenParameterSpec("secp256r1")); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); // Create key specs KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), parameterSpec); KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); // Get keys clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); // Get certificates from key store char[] clientKeyStorePwd = "client".toCharArray(); KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream clientKeyStoreFile = new FileInputStream("./credentials/clientKeyStore.jks")) { clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd); } clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd); clientCAX509Cert = (X509Certificate) clientKeyStore.getCertificate("clientCA"); clientX509CertChain[0] = (X509Certificate) clientKeyStore.getCertificate("client"); clientX509CertChain[1] = clientCAX509Cert; trustedCertificates[0] = clientCAX509Cert; } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } // create server credentials try { // Get point values byte[] publicX = Hex .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray()); byte[] publicY = Hex .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray()); byte[] privateS = Hex .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray()); // Get Elliptic Curve Parameter spec for secp256r1 AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); algoParameters.init(new ECGenParameterSpec("secp256r1")); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); // Create key specs KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), parameterSpec); KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec); // Get keys serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec); // Get certificates from key store char[] serverKeyStorePwd = "server".toCharArray(); KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream serverKeyStoreFile = new FileInputStream("./credentials/serverKeyStore.jks")) { serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd); } serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd); serverCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("serverCA"); serverX509CertChain[0] = (X509Certificate) serverKeyStore.getCertificate("server"); serverX509CertChain[1] = serverCAX509Cert; trustedCertificates[1] = serverCAX509Cert; } catch (GeneralSecurityException | IOException e) { throw new RuntimeException(e); } } public void createPSKClient() { ObjectsInitializer initializer = new ObjectsInitializer(); initializer.setInstancesForObject(LwM2mId.SECURITY, Security.psk( "coaps://" + server.getSecureAddress().getHostString() + ":" + server.getSecureAddress().getPort(), 12345, GOOD_PSK_ID.getBytes(StandardCharsets.UTF_8), GOOD_PSK_KEY)); initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false)); initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U")); List<LwM2mObjectEnabler> objects = initializer.createMandatory(); objects.add(initializer.create(2)); InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint()); builder.setLocalAddress(clientAddress.getHostString(), clientAddress.getPort()); builder.setObjects(objects); client = builder.build(); } // TODO implement RPK support for client public void createRPKClient() { ObjectsInitializer initializer = new ObjectsInitializer(); initializer.setInstancesForObject(LwM2mId.SECURITY, Security.rpk( "coaps://" + server.getSecureAddress().getHostString() + ":" + server.getSecureAddress().getPort(), 12345, clientPublicKey.getEncoded(), clientPrivateKey.getEncoded(), serverPublicKey.getEncoded())); initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false)); initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U")); List<LwM2mObjectEnabler> objects = initializer.createMandatory(); objects.add(initializer.create(2)); InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); DtlsConnectorConfig.Builder config = new DtlsConnectorConfig.Builder(clientAddress); // TODO we should read the config from the security object // TODO no way to provide a dynamic config with the current scandium API config.setIdentity(clientPrivateKey, clientPublicKey); CoapServer coapServer = new CoapServer(); coapServer.addEndpoint(new CoapEndpoint(new DTLSConnector(config.build()), NetworkConfig.getStandard())); LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint()); builder.setLocalAddress(clientAddress.getHostString(), clientAddress.getPort()); builder.setObjects(objects); client = builder.build(); } // TODO implement X509 support for client public void createX509CertClient(PrivateKey privatekey, Certificate[] trustedCertificates) { ObjectsInitializer initializer = new ObjectsInitializer(); // TODO security instance with certificate info initializer.setInstancesForObject(LwM2mId.SECURITY, Security.noSec( "coaps://" + server.getSecureAddress().getHostString() + ":" + server.getSecureAddress().getPort(), 12345)); initializer.setInstancesForObject(LwM2mId.SERVER, new Server(12345, LIFETIME, BindingMode.U, false)); initializer.setInstancesForObject(LwM2mId.DEVICE, new Device("Eclipse Leshan", MODEL_NUMBER, "12345", "U")); List<LwM2mObjectEnabler> objects = initializer.createMandatory(); objects.add(initializer.create(2)); InetSocketAddress clientAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); DtlsConnectorConfig.Builder config = new DtlsConnectorConfig.Builder(clientAddress); // TODO we should read the config from the security object config.setIdentity(privatekey, clientX509CertChain, false); config.setTrustStore(trustedCertificates); CoapServer coapServer = new CoapServer(); coapServer.addEndpoint(new CoapEndpoint(new DTLSConnector(config.build()), NetworkConfig.getStandard())); LeshanClientBuilder builder = new LeshanClientBuilder(getCurrentEndpoint()); builder.setLocalAddress(clientAddress.getHostString(), clientAddress.getPort()); builder.setObjects(objects); client = builder.build(); } public void createServerWithRPK() { LeshanServerBuilder builder = new LeshanServerBuilder(); builder.setLocalAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); builder.setLocalSecureAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); builder.setPublicKey(serverPublicKey); builder.setPrivateKey(serverPrivateKey); builder.setSecurityStore(new InMemorySecurityStore()); server = builder.build(); // monitor client registration setupRegistrationMonitoring(); } public void createServerWithX509Cert(Certificate[] trustedCertificates) { LeshanServerBuilder builder = new LeshanServerBuilder(); builder.setLocalAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); builder.setLocalSecureAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); builder.setPrivateKey(serverPrivateKeyFromCert); builder.setCertificateChain(serverX509CertChain); builder.setTrustedCertificates(trustedCertificates); builder.setSecurityStore(new InMemorySecurityStore()); server = builder.build(); // monitor client registration setupRegistrationMonitoring(); } public PublicKey getServerPublicKey() { return serverPublicKey; } public EditableSecurityStore getSecurityStore() { return (EditableSecurityStore) server.getSecurityStore(); } @Override public void dispose() { getSecurityStore().remove(getCurrentEndpoint()); getSecurityStore().remove(BAD_ENDPOINT); getSecurityStore().remove(GOOD_ENDPOINT); super.dispose(); } }