Skip to content

Commit

Permalink
#436: Add X509 support to leshan client
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Sep 13, 2018
1 parent 0d55627 commit 5e564b8
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;

Expand All @@ -29,8 +31,15 @@
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig.Builder;
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.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.pskstore.StaticPskStore;
import org.eclipse.californium.scandium.dtls.rpkstore.TrustedRpkStore;
import org.eclipse.californium.scandium.dtls.x509.CertificateVerifier;
import org.eclipse.leshan.SecurityMode;
import org.eclipse.leshan.client.servers.EndpointsManager;
import org.eclipse.leshan.client.servers.Server;
Expand Down Expand Up @@ -101,8 +110,53 @@ public boolean isTrusted(RawPublicKeyIdentity id) {
return true;
}
});
} else if (serverInfo.secureMode == SecurityMode.X509) {
// set identity
newBuilder.setIdentity(serverInfo.privateKey, new Certificate[] { serverInfo.clientCertificate },
false);

// set X509 verifier
final Certificate expectedServerCertificate = serverInfo.serverCertificate;
newBuilder.setCertificateVerifier(new CertificateVerifier() {

@Override
public void verifyCertificate(CertificateMessage message, DTLSSession session)
throws HandshakeException {
// 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) {
AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE,
session.getPeer());
throw new HandshakeException("Certificate chain could not be validated", alert);
}
Certificate receivedServerCertificate = message.getCertificateChain().getCertificates().get(0);

// Validate certificate
if (!expectedServerCertificate.equals(receivedServerCertificate)) {
AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE,
session.getPeer());
throw new HandshakeException("Certificate chain could not be validated", alert);
}
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
});

// disable the possibility to use RPK as client should use X509.
// TODO add a way in Scandium to say that we don't accept RPK certificate type
newBuilder.setRpkTrustStore(new TrustedRpkStore() {
@Override
public boolean isTrusted(RawPublicKeyIdentity id) {
return false;
}
});

}
// TODO add support X509
if (endpointFactory != null) {
currentEndpoint = endpointFactory.createSecuredEndpoint(newBuilder.build(), coapConfig, null);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ public static Security rpk(String serverUri, int shortServerId, byte[] clientPub
clientPrivateKey.clone(), shortServerId);
}

/**
* Returns a new security instance (X509) for a device management server.
*/
public static Security x509(String serverUri, int shortServerId, byte[] clientCertificate, byte[] clientPrivateKey,
byte[] serverPublicKey) {
return new Security(serverUri, false, SecurityMode.X509.code, clientCertificate.clone(),
serverPublicKey.clone(), clientPrivateKey.clone(), shortServerId);
}

@Override
public WriteResponse write(int resourceId, LwM2mResource value) {
LOG.debug("Write on resource {}: {}", resourceId, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.net.URISyntaxException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;

import org.eclipse.leshan.LwM2m;
import org.eclipse.leshan.SecurityMode;
Expand All @@ -38,9 +39,13 @@ public class ServerInfo {
public byte[] pskKey;

public PublicKey publicKey;
public PrivateKey privateKey;
public PublicKey serverPublicKey;

public Certificate clientCertificate;
public Certificate serverCertificate;

public PrivateKey privateKey;

public InetSocketAddress getAddress() {
return getAddress(serverUri);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
import static org.eclipse.leshan.LwM2mId.*;
import static org.eclipse.leshan.client.request.ServerIdentity.SYSTEM;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
Expand Down Expand Up @@ -79,6 +84,10 @@ public static ServersInfo getInfo(Map<Integer, LwM2mObjectEnabler> objectEnabler
info.publicKey = getPublicKey(security);
info.privateKey = getPrivateKey(security);
info.serverPublicKey = getServerPublicKey(security);
} else if (info.secureMode == SecurityMode.X509) {
info.clientCertificate = getClientCertificate(security);
info.serverCertificate = getServerCertificate(security);
info.privateKey = getPrivateKey(security);
}
infos.bootstrap = info;
}
Expand All @@ -95,6 +104,10 @@ public static ServersInfo getInfo(Map<Integer, LwM2mObjectEnabler> objectEnabler
info.publicKey = getPublicKey(security);
info.privateKey = getPrivateKey(security);
info.serverPublicKey = getServerPublicKey(security);
} else if (info.secureMode == SecurityMode.X509) {
info.clientCertificate = getClientCertificate(security);
info.serverCertificate = getServerCertificate(security);
info.privateKey = getPrivateKey(security);
}
// search corresponding device management server
for (LwM2mObjectInstance server : servers.getInstances().values()) {
Expand Down Expand Up @@ -187,4 +200,30 @@ private static PublicKey getServerPublicKey(LwM2mObjectInstance securityInstance
}
return null;
}

private static Certificate getServerCertificate(LwM2mObjectInstance securityInstance) {
byte[] encodedCert = (byte[]) securityInstance.getResource(SEC_SERVER_PUBKEY).getValue();
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
try (ByteArrayInputStream in = new ByteArrayInputStream(encodedCert)) {
return cf.generateCertificate(in);
}
} catch (CertificateException | IOException e) {
LOG.debug("Failed to decode X.509 certificate", e);
return null;
}
}

private static Certificate getClientCertificate(LwM2mObjectInstance securityInstance) {
byte[] encodedCert = (byte[]) securityInstance.getResource(SEC_PUBKEY_IDENTITY).getValue();
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
try (ByteArrayInputStream in = new ByteArrayInputStream(encodedCert)) {
return cf.generateCertificate(in);
}
} catch (CertificateException | IOException e) {
LOG.debug("Failed to decode X.509 certificate", e);
return null;
}
}
}
Binary file modified leshan-integration-tests/credentials/clientKeyStore.jks
Binary file not shown.
Binary file modified leshan-integration-tests/credentials/serverKeyStore.jks
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.cert.Certificate;

import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
Expand Down Expand Up @@ -60,7 +59,7 @@ public void createServerWithRPK() {
}

@Override
public void createServerWithX509Cert(Certificate[] trustedCertificates) {
public void createServerWithX509Cert() {
throw new UnsupportedOperationException("Not implemented");
}

Expand Down
Loading

0 comments on commit 5e564b8

Please sign in to comment.