Skip to content

Commit 97974d5

Browse files
committed
example-client: added flag for secure provider, simplified schemes
1 parent 470d573 commit 97974d5

File tree

11 files changed

+397
-186
lines changed

11 files changed

+397
-186
lines changed

example-client/README.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,23 @@ This is an example coap client application, that can be use with any LwM2M Serve
66
Usage
77
-----
88

9-
./run.sh [-k KEYSTORE_FILE] <scheme>://<registration-url>
9+
./run.sh [-k KEYSTORE_FILE] [-s SSL_PROVIDER] <scheme>://<registration-url>
1010

1111
### Schemes
1212

13-
- `coap://` (UDP)
14-
- `coap+tcp://` (TCP)
15-
- `coaps+tcp://` (TLS)
16-
- `coaps+tcp-d2://` (TLS)
17-
- `openssl-coap://` (DTLS)
18-
- `stdio-coap://`
19-
- `stdio-coap+tcp://`
13+
- `coap://` (RFC 7252)
14+
- `coaps://` (RFC 7252)
15+
- `coap+tcp://` (RFC 8323)
16+
- `coaps+tcp://` (RFC 8323)
2017

2118
### Examples
2219
2320
./run.sh 'coap://localhost:5683/rd?ep=device007&lt=3600&b=U'
2421

2522

26-
#### Running with openssl (dtls):
23+
#### Running with openssl:
2724

28-
./run.sh -k device01.jks 'openssl-coaps://localhost:5684/rd?ep=device001'
25+
./run.sh -k device01.jks -s openssl 'coaps://localhost:5684/rd?ep=device001'
2926

3027
_Note: this requires openssl installed in your system that support dtls.
3128
Use environment variable `OPENSSL_BIN_PATH` to point to specific location of `openssl` binary._
@@ -34,10 +31,10 @@ Use environment variable `OPENSSL_BIN_PATH` to point to specific location of `op
3431
#### Running with standard io (bidirectional pipe):
3532

3633
mkfifo fifo
37-
./run.sh "stdio-coap://127.0.0.1:5683/rd?ep=test&aid=dm&lt=60" < fifo | \
34+
./run.sh -s stdio "coaps://127.0.0.1:5683/rd?ep=test&aid=dm&lt=60" < fifo | \
3835
socat -d -d - udp-sendto:localhost:5683 > fifo
3936

4037
or
4138

42-
./run.sh "stdio-coap://127.0.0.1:5683/rd?ep=test&aid=dm&lt=60" < fifo | \
39+
./run.sh -s stdio "coaps://127.0.0.1:5683/rd?ep=test&aid=dm&lt=60" < fifo | \
4340
openssl s_client -connect localhost:5684 -cipher PSK-AES128-CBC-SHA -psk 01010101010101010101010101010101 -psk_identity device-0000000001 -quiet > fifo

example-client/src/main/java/com/mbed/coap/cli/CoapCli.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,29 @@
2626
import java.io.IOException;
2727
import java.net.InetSocketAddress;
2828
import java.net.URI;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
2931

3032
/**
3133
* Created by szymon
3234
*/
3335
public class CoapCli {
3436

37+
private static final Logger LOGGER = LoggerFactory.getLogger(CoapCli.class);
38+
3539
public static void main(String[] args) {
40+
main(args, new CoapSchemes());
41+
}
42+
43+
public static void main(String[] args, CoapSchemes providers) {
3644
if (args.length == 0) {
3745
System.out.println("Usage: [options...] <method> <scheme>://<host>:<port>/<uri-path> [<payload>]");
3846
System.out.println("Method: GET | POST | PUT | DELETE");
39-
System.out.println("Schemes: " + CoapSchemes.INSTANCE.supportedSchemas().replaceAll("\n", "\n "));
47+
System.out.println("Schemes: " + providers.supportedSchemes().replaceAll("\n", "\n "));
4048
System.out.println("Options:");
49+
System.out.println(" -s <ssl provider> jdk <default>,");
50+
System.out.println(" openssl (requires installed openssl that supports dtls),");
51+
System.out.println(" stdio (standard IO)");
4152
System.out.println(" -k <file> KeyStore file");
4253
System.out.println(" -b <block size> Block size, one of: 16, 32, 64, 128, 256, 512, 1024");
4354
System.out.println(" -p <proxy> Proxy-Uri");
@@ -51,6 +62,7 @@ public static void main(String[] args) {
5162
String keystoreFile = null;
5263
String proxyUri = null;
5364
BlockSize blockSize = null;
65+
TransportProvider transportProvider = providers.defaultProvider();
5466
int i;
5567
for (i = 0; i < args.length; i++) {
5668
if (args[i].equals("-k")) {
@@ -59,6 +71,10 @@ public static void main(String[] args) {
5971
proxyUri = args[++i];
6072
} else if (args[i].equals("-b")) {
6173
blockSize = BlockSize.valueOf("S_" + args[++i]);
74+
} else if (args[i].equals("-s")) {
75+
transportProvider = providers.transportProviderFor(args[++i]);
76+
} else if (args[i].charAt(0) == '-') {
77+
throw new IllegalArgumentException("Unrecognised flag: " + args[i]);
6278
} else {
6379
break;
6480
}
@@ -69,16 +85,18 @@ public static void main(String[] args) {
6985

7086
String payload = (args.length > i) ? args[i] : null;
7187

72-
new CoapCli(keystoreFile, blockSize, proxyUri, method, uri, payload);
88+
new CoapCli(providers, transportProvider, keystoreFile, blockSize, proxyUri, method, uri, payload);
89+
} catch (IllegalArgumentException ex) {
90+
LOGGER.error(ex.getMessage());
7391
} catch (Exception ex) {
7492
ex.printStackTrace();
7593
}
7694
System.exit(0);
7795
}
7896

79-
public CoapCli(String keystoreFile, BlockSize blockSize, String proxyUri, String method, URI uri, String payload) throws IOException, InterruptedException, CoapException {
97+
public CoapCli(CoapSchemes providers, TransportProvider transportProvider, String keystoreFile, BlockSize blockSize, String proxyUri, String method, URI uri, String payload) throws IOException, InterruptedException, CoapException {
8098

81-
CoapServer cliServer = CoapSchemes.INSTANCE.create(keystoreFile, uri).build().start();
99+
CoapServer cliServer = providers.create(transportProvider, keystoreFile, uri).build().start();
82100

83101
InetSocketAddress destination = new InetSocketAddress(uri.getHost(), uri.getPort());
84102
CoapClient cli = CoapClientBuilder.clientFor(destination, cliServer);
@@ -92,8 +110,10 @@ public CoapCli(String keystoreFile, BlockSize blockSize, String proxyUri, String
92110
.payload(payload)
93111
.sync().invokeMethod(Method.valueOf(method));
94112

95-
System.out.println("");
96-
System.out.println(resp.getPayloadString());
113+
if (resp.getPayload().length > 0) {
114+
System.out.println();
115+
System.out.println(resp.getPayloadString());
116+
}
97117

98118
cliServer.stop();
99119
}

example-client/src/main/java/com/mbed/coap/cli/CoapSchemes.java

Lines changed: 43 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -15,194 +15,103 @@
1515
*/
1616
package com.mbed.coap.cli;
1717

18+
import com.mbed.coap.cli.providers.JdkProvider;
19+
import com.mbed.coap.cli.providers.OpensslProvider;
20+
import com.mbed.coap.cli.providers.PlainTextProvider;
21+
import com.mbed.coap.cli.providers.StandardIoProvider;
1822
import com.mbed.coap.server.CoapServerBuilder;
1923
import com.mbed.coap.transport.javassl.CoapSerializer;
20-
import com.mbed.coap.transport.javassl.SSLSocketClientTransport;
21-
import com.mbed.coap.transport.javassl.SocketClientTransport;
22-
import com.mbed.coap.transport.stdio.OpensslProcessTransport;
23-
import com.mbed.coap.transport.stdio.StreamBlockingTransport;
24-
import com.mbed.coap.transport.udp.DatagramSocketTransport;
25-
import java.io.File;
2624
import java.io.FileInputStream;
27-
import java.io.FileWriter;
2825
import java.io.IOException;
2926
import java.net.InetSocketAddress;
3027
import java.net.URI;
3128
import java.security.GeneralSecurityException;
3229
import java.security.KeyStore;
3330
import java.security.KeyStoreException;
34-
import java.security.NoSuchAlgorithmException;
35-
import java.security.UnrecoverableKeyException;
36-
import java.security.cert.CertificateEncodingException;
37-
import java.security.cert.X509Certificate;
3831
import java.util.ArrayList;
39-
import java.util.Base64;
4032
import java.util.Collections;
41-
import java.util.Enumeration;
42-
import javax.net.SocketFactory;
43-
import javax.net.ssl.KeyManagerFactory;
44-
import javax.net.ssl.SSLContext;
45-
import javax.net.ssl.TrustManagerFactory;
46-
import org.slf4j.Logger;
47-
import org.slf4j.LoggerFactory;
4833

4934
public class CoapSchemes {
50-
private static final Logger LOGGER = LoggerFactory.getLogger(CoapSchemes.class);
35+
public static char[] secret() {
36+
return "secret".toCharArray();
37+
}
5138

52-
static final CoapSchemes INSTANCE;
53-
static final char[] SECRET = "secret".toCharArray();
39+
public final CoapServerBuilder create(TransportProvider transportProvider, String keystoreFile, URI uri) {
5440

55-
static {
5641
try {
57-
Class coapTransportBuilderClass = Class.forName(System.getProperty("coap.cli.CoapSchemes", "com.mbed.coap.cli.CoapSchemes"));
58-
INSTANCE = ((CoapSchemes) coapTransportBuilderClass.newInstance());
59-
60-
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
42+
return create(transportProvider, loadKeystore(keystoreFile), uri);
43+
} catch (GeneralSecurityException | IOException e) {
6144
throw new RuntimeException(e);
6245
}
6346
}
6447

65-
public final CoapServerBuilder create(String keystoreFile, URI uri) {
66-
return create(loadKeystore(keystoreFile), uri);
48+
public String supportedSchemes() {
49+
return "coap, coap+tcp, coaps, coaps+tcp, coaps+tcp-d2 (draft 2)";
6750
}
6851

69-
public String supportedSchemas() {
70-
return "coap, coap+tcp, coaps+tcp, coaps+tcp-d2 (draft 2), \nopenssl-coap (requires installed openssl that supports dtls), \nstdio-coap (standard IO), stdio-coap+tcp";
71-
}
52+
protected CoapServerBuilder create(TransportProvider transportProvider, KeyStore ks, URI uri) throws GeneralSecurityException, IOException {
53+
InetSocketAddress destAdr = addressFromUri(uri);
7254

73-
protected CoapServerBuilder create(KeyStore ks, URI uri) {
74-
InetSocketAddress destAdr = new InetSocketAddress(uri.getHost(), uri.getPort());
7555
switch (uri.getScheme()) {
7656
case "coap":
77-
return coapOverUdp();
57+
return CoapServerBuilder.newBuilder()
58+
.transport(new PlainTextProvider().createUDP(CoapSerializer.UDP, destAdr, ks));
7859

7960
case "coap+tcp":
80-
return coapOverTcp(destAdr);
61+
return CoapServerBuilder.newBuilderForTcp()
62+
.transport(new PlainTextProvider().createTCP(CoapSerializer.TCP, destAdr, ks));
63+
64+
case "coaps":
65+
return CoapServerBuilder.newBuilder()
66+
.transport(transportProvider.createUDP(CoapSerializer.UDP, destAdr, ks));
8167

8268
case "coaps+tcp":
83-
return coapOverTls(destAdr, ks);
69+
return CoapServerBuilder.newBuilderForTcp()
70+
.transport(transportProvider.createTCP(CoapSerializer.TCP, destAdr, ks));
8471

8572
case "coaps+tcp-d2":
86-
return coapOverTlsDraft2(destAdr, ks);
87-
88-
case "openssl-coap":
89-
return coapOverDtls(destAdr, ks);
90-
91-
case "stdio-coap":
92-
return stdIoCoap(destAdr, CoapSerializer.UDP);
93-
94-
case "stdio-coap+tcp":
95-
return stdIoCoap(destAdr, CoapSerializer.TCP);
96-
73+
return CoapServerBuilder.newBuilder()
74+
.transport(transportProvider.createTCP(CoapSerializer.UDP, destAdr, ks));
9775

9876
default:
99-
throw new IllegalArgumentException("Protocol not supported: " + uri.getScheme());
77+
throw new IllegalArgumentException("Scheme not supported: " + uri.getScheme());
10078
}
10179
}
10280

81+
protected static InetSocketAddress addressFromUri(URI uri) {
82+
return new InetSocketAddress(uri.getHost(), uri.getPort());
83+
}
10384

10485
private static KeyStore loadKeystore(String keystoreFile) {
10586
KeyStore ks = null;
10687
if (keystoreFile != null) {
10788
try (FileInputStream f = new FileInputStream(keystoreFile)) {
10889
ks = KeyStore.getInstance("JKS");
109-
ks.load(f, SECRET);
90+
ks.load(f, secret());
11091
} catch (Exception e) {
11192
throw new RuntimeException(e);
11293
}
11394
}
11495
return ks;
11596
}
11697

117-
private CoapServerBuilder coapOverTcp(InetSocketAddress destAdr) {
118-
return CoapServerBuilder.newBuilderForTcp().transport(
119-
new SocketClientTransport(destAdr, SocketFactory.getDefault(), CoapSerializer.TCP, true)
120-
);
121-
}
122-
123-
private CoapServerBuilder coapOverTlsDraft2(InetSocketAddress destAdr, KeyStore ks) {
124-
SSLContext sslContext = sslContextFromKeystore(ks);
125-
return CoapServerBuilder.newBuilder().transport(
126-
new SSLSocketClientTransport(destAdr, sslContext.getSocketFactory(), CoapSerializer.UDP, true)
127-
);
128-
}
129-
130-
private CoapServerBuilder coapOverTls(InetSocketAddress destAdr, KeyStore ks) {
131-
SSLContext sslContext = sslContextFromKeystore(ks);
132-
return CoapServerBuilder.newBuilderForTcp().transport(
133-
new SSLSocketClientTransport(destAdr, sslContext.getSocketFactory(), CoapSerializer.TCP, true)
134-
);
135-
}
136-
137-
private CoapServerBuilder coapOverUdp() {
138-
return CoapServerBuilder.newBuilder().transport(new DatagramSocketTransport(0));
139-
}
140-
141-
private CoapServerBuilder stdIoCoap(InetSocketAddress destAdr, CoapSerializer serializer) {
142-
return CoapServerBuilder.newBuilder().transport(StreamBlockingTransport.forStandardIO(destAdr, serializer));
143-
}
144-
145-
protected static SSLContext sslContextFromKeystore(KeyStore ks) {
146-
try {
147-
148-
final KeyManagerFactory kmf;
149-
kmf = KeyManagerFactory.getInstance("SunX509");
150-
kmf.init(ks, SECRET);
151-
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
152-
tmf.init(ks);
153-
154-
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
155-
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
156-
157-
//print all certificates subject
158-
Enumeration<String> aliases = ks.aliases();
159-
while (aliases.hasMoreElements()) {
160-
String alias = aliases.nextElement();
161-
String certCN = ((X509Certificate) ks.getCertificate(alias)).getSubjectDN().toString();
162-
163-
if (ks.isKeyEntry(alias)) {
164-
LOGGER.info("Using certificate: " + certCN);
165-
} else {
166-
LOGGER.info("Using trusted certificate: " + certCN);
167-
}
168-
}
169-
170-
return sslContext;
171-
} catch (Exception e) {
172-
throw new RuntimeException(e);
173-
}
174-
}
175-
176-
private CoapServerBuilder coapOverDtls(InetSocketAddress destAdr, KeyStore ks) {
177-
try {
178-
String alias = findKeyAlias(ks);
179-
File temp = keyPairToTempFile(alias, ks);
180-
181-
OpensslProcessTransport transport = OpensslProcessTransport.create(temp.getAbsolutePath(), destAdr);
182-
return CoapServerBuilder.newBuilder().transport(transport);
183-
} catch (IOException | GeneralSecurityException e) {
184-
throw new RuntimeException(e);
98+
public TransportProvider transportProviderFor(String transport) {
99+
switch (transport.toLowerCase()) {
100+
case "jdk":
101+
return new JdkProvider();
102+
case "openssl":
103+
return new OpensslProvider();
104+
case "stdio":
105+
return new StandardIoProvider();
106+
default:
107+
throw new IllegalArgumentException("Not supported transport: " + transport);
185108
}
186-
187-
188109
}
189110

190-
private static File keyPairToTempFile(String alias, KeyStore ks) throws KeyStoreException, IOException, CertificateEncodingException, NoSuchAlgorithmException, UnrecoverableKeyException {
191-
File temp = File.createTempFile("client", ".pem");
192-
try (FileWriter writer = new FileWriter(temp)) {
193-
writer.write("-----BEGIN CERTIFICATE-----\n");
194-
writer.write(Base64.getEncoder().encodeToString(ks.getCertificate(alias).getEncoded()));
195-
writer.write("\n-----END CERTIFICATE-----\n");
196-
writer.write("-----BEGIN PRIVATE KEY-----\n");
197-
writer.write(Base64.getEncoder().encodeToString(ks.getKey(alias, SECRET).getEncoded()));
198-
writer.write("\n-----END PRIVATE KEY-----\n");
199-
writer.flush();
200-
}
201-
temp.deleteOnExit();
202-
return temp;
111+
public TransportProvider defaultProvider() {
112+
return new JdkProvider();
203113
}
204114

205-
206115
public static String findKeyAlias(KeyStore ks) throws KeyStoreException {
207116
ArrayList<String> aliases = Collections.list(ks.aliases());
208117

0 commit comments

Comments
 (0)