|
15 | 15 | */
|
16 | 16 | package com.mbed.coap.cli;
|
17 | 17 |
|
| 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; |
18 | 22 | import com.mbed.coap.server.CoapServerBuilder;
|
19 | 23 | 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; |
26 | 24 | import java.io.FileInputStream;
|
27 |
| -import java.io.FileWriter; |
28 | 25 | import java.io.IOException;
|
29 | 26 | import java.net.InetSocketAddress;
|
30 | 27 | import java.net.URI;
|
31 | 28 | import java.security.GeneralSecurityException;
|
32 | 29 | import java.security.KeyStore;
|
33 | 30 | 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; |
38 | 31 | import java.util.ArrayList;
|
39 |
| -import java.util.Base64; |
40 | 32 | 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; |
48 | 33 |
|
49 | 34 | public class CoapSchemes {
|
50 |
| - private static final Logger LOGGER = LoggerFactory.getLogger(CoapSchemes.class); |
| 35 | + public static char[] secret() { |
| 36 | + return "secret".toCharArray(); |
| 37 | + } |
51 | 38 |
|
52 |
| - static final CoapSchemes INSTANCE; |
53 |
| - static final char[] SECRET = "secret".toCharArray(); |
| 39 | + public final CoapServerBuilder create(TransportProvider transportProvider, String keystoreFile, URI uri) { |
54 | 40 |
|
55 |
| - static { |
56 | 41 | 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) { |
61 | 44 | throw new RuntimeException(e);
|
62 | 45 | }
|
63 | 46 | }
|
64 | 47 |
|
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)"; |
67 | 50 | }
|
68 | 51 |
|
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); |
72 | 54 |
|
73 |
| - protected CoapServerBuilder create(KeyStore ks, URI uri) { |
74 |
| - InetSocketAddress destAdr = new InetSocketAddress(uri.getHost(), uri.getPort()); |
75 | 55 | switch (uri.getScheme()) {
|
76 | 56 | case "coap":
|
77 |
| - return coapOverUdp(); |
| 57 | + return CoapServerBuilder.newBuilder() |
| 58 | + .transport(new PlainTextProvider().createUDP(CoapSerializer.UDP, destAdr, ks)); |
78 | 59 |
|
79 | 60 | 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)); |
81 | 67 |
|
82 | 68 | case "coaps+tcp":
|
83 |
| - return coapOverTls(destAdr, ks); |
| 69 | + return CoapServerBuilder.newBuilderForTcp() |
| 70 | + .transport(transportProvider.createTCP(CoapSerializer.TCP, destAdr, ks)); |
84 | 71 |
|
85 | 72 | 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)); |
97 | 75 |
|
98 | 76 | default:
|
99 |
| - throw new IllegalArgumentException("Protocol not supported: " + uri.getScheme()); |
| 77 | + throw new IllegalArgumentException("Scheme not supported: " + uri.getScheme()); |
100 | 78 | }
|
101 | 79 | }
|
102 | 80 |
|
| 81 | + protected static InetSocketAddress addressFromUri(URI uri) { |
| 82 | + return new InetSocketAddress(uri.getHost(), uri.getPort()); |
| 83 | + } |
103 | 84 |
|
104 | 85 | private static KeyStore loadKeystore(String keystoreFile) {
|
105 | 86 | KeyStore ks = null;
|
106 | 87 | if (keystoreFile != null) {
|
107 | 88 | try (FileInputStream f = new FileInputStream(keystoreFile)) {
|
108 | 89 | ks = KeyStore.getInstance("JKS");
|
109 |
| - ks.load(f, SECRET); |
| 90 | + ks.load(f, secret()); |
110 | 91 | } catch (Exception e) {
|
111 | 92 | throw new RuntimeException(e);
|
112 | 93 | }
|
113 | 94 | }
|
114 | 95 | return ks;
|
115 | 96 | }
|
116 | 97 |
|
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); |
185 | 108 | }
|
186 |
| - |
187 |
| - |
188 | 109 | }
|
189 | 110 |
|
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(); |
203 | 113 | }
|
204 | 114 |
|
205 |
| - |
206 | 115 | public static String findKeyAlias(KeyStore ks) throws KeyStoreException {
|
207 | 116 | ArrayList<String> aliases = Collections.list(ks.aliases());
|
208 | 117 |
|
|
0 commit comments