Skip to content

Commit 4cabc91

Browse files
authored
Merge pull request #63 from iot-dsa-v2/daniel-certs
cert/security service
2 parents 65b6c8c + 3e4833c commit 4cabc91

15 files changed

+883
-30
lines changed

dslink-core/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin/

dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package org.iot.dsa.dslink.websocket;
22

3+
import com.acuity.iot.dsa.dslink.sys.cert.SysCertManager;
34
import com.acuity.iot.dsa.dslink.transport.BufferedBinaryTransport;
45
import com.acuity.iot.dsa.dslink.transport.DSTransport;
56
import java.io.IOException;
67
import java.net.URI;
78
import java.nio.ByteBuffer;
89
import javax.websocket.*;
910
import org.glassfish.tyrus.client.ClientManager;
11+
import org.glassfish.tyrus.client.ClientProperties;
12+
import org.glassfish.tyrus.client.SslContextConfigurator;
13+
import org.glassfish.tyrus.client.SslEngineConfigurator;
1014
import org.iot.dsa.util.DSException;
1115

1216
/**
@@ -97,7 +101,13 @@ public DSTransport open() {
97101
}
98102
client.setDefaultMaxBinaryMessageBufferSize(64 * 1024);
99103
client.setDefaultMaxTextMessageBufferSize(64 * 1024);
100-
client.connectToServer(this, new URI(getConnectionUrl()));
104+
URI connUri = new URI(getConnectionUrl());
105+
if ("wss".equalsIgnoreCase(connUri.getScheme())) {
106+
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator());
107+
sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier());
108+
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
109+
}
110+
client.connectToServer(this, connUri);
101111
debug(debug() ? "Transport open" : null);
102112
} catch (Exception x) {
103113
DSException.throwRuntime(x);

dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.acuity.iot.dsa.dslink.io.DSCharBuffer;
44
import com.acuity.iot.dsa.dslink.io.DSIoException;
5+
import com.acuity.iot.dsa.dslink.sys.cert.SysCertManager;
56
import com.acuity.iot.dsa.dslink.transport.DSTextTransport;
67
import com.acuity.iot.dsa.dslink.transport.DSTransport;
78
import java.io.IOException;
@@ -18,6 +19,9 @@
1819
import javax.websocket.RemoteEndpoint;
1920
import javax.websocket.Session;
2021
import org.glassfish.tyrus.client.ClientManager;
22+
import org.glassfish.tyrus.client.ClientProperties;
23+
import org.glassfish.tyrus.client.SslContextConfigurator;
24+
import org.glassfish.tyrus.client.SslEngineConfigurator;
2125
import org.iot.dsa.util.DSException;
2226

2327
/**
@@ -149,7 +153,13 @@ public DSTransport open() {
149153
}
150154
client.setDefaultMaxBinaryMessageBufferSize(64 * 1024);
151155
client.setDefaultMaxTextMessageBufferSize(64 * 1024);
152-
client.connectToServer(this, new URI(getConnectionUrl()));
156+
URI connUri = new URI(getConnectionUrl());
157+
if ("wss".equalsIgnoreCase(connUri.getScheme())) {
158+
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator());
159+
sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier());
160+
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
161+
}
162+
client.connectToServer(this, connUri);
153163
} catch (Exception x) {
154164
DSException.throwRuntime(x);
155165
}

dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/backup/SysBackupService.java

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
import org.iot.dsa.node.action.DSAction;
2626
import org.iot.dsa.time.DSTime;
2727

28+
/**
29+
* @author Daniel Shapiro
30+
* @author Aaron Hansen
31+
*/
2832
public class SysBackupService extends DSNode implements Runnable {
2933

3034
static final String ENABLED = "Enabled";

dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java

+51-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
package com.acuity.iot.dsa.dslink.sys.cert;
22

33
import java.security.KeyStore;
4+
import java.security.NoSuchAlgorithmException;
5+
import java.security.NoSuchProviderException;
46
import java.security.Provider;
57
import java.security.Security;
68
import java.security.cert.CertificateException;
9+
import java.security.cert.PKIXCertPathBuilderResult;
10+
import java.security.cert.TrustAnchor;
711
import java.security.cert.X509Certificate;
812
import java.util.Arrays;
13+
import java.util.Collections;
14+
import java.util.HashSet;
915
import java.util.List;
16+
import java.util.Set;
1017
import javax.net.ssl.*;
1118

1219
/**
1320
* Adds support for self signed SSL. If anonymous is not allowed
1421
* falls back to the default Java trust manager.
1522
*
1623
* @author Aaron Hansen
24+
* @author Daniel Shapiro
1725
*/
1826
public class AnonymousTrustFactory extends TrustManagerFactorySpi {
1927

@@ -114,8 +122,13 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
114122
return;
115123
}
116124
if (defaultX509Mgr != null) {
117-
defaultX509Mgr.checkClientTrusted(chain, authType);
125+
try {
126+
defaultX509Mgr.checkClientTrusted(chain, authType);
127+
return;
128+
} catch (CertificateException e) {
129+
}
118130
}
131+
checkLocally(chain, authType);
119132
}
120133

121134
@Override
@@ -125,7 +138,43 @@ public void checkServerTrusted(X509Certificate[] chain, String authType)
125138
return;
126139
}
127140
if (defaultX509Mgr != null) {
128-
defaultX509Mgr.checkServerTrusted(chain, authType);
141+
try {
142+
defaultX509Mgr.checkServerTrusted(chain, authType);
143+
return;
144+
} catch (CertificateException e) {
145+
}
146+
}
147+
checkLocally(chain, authType);
148+
}
149+
150+
private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException {
151+
Set<X509Certificate> chainAsSet = new HashSet<X509Certificate>();
152+
Collections.addAll(chainAsSet, chain);
153+
X509Certificate anchorCert;
154+
try {
155+
if (CertificateVerifier.isSelfSigned(chain[0])) {
156+
anchorCert = chain[0];
157+
} else {
158+
PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet);
159+
TrustAnchor anchor = result.getTrustAnchor();
160+
anchorCert = anchor.getTrustedCert();
161+
}
162+
163+
if (anchorCert == null) {
164+
throw new CertificateException();
165+
}
166+
167+
if (!certManager.isInTrustStore(anchorCert)) {
168+
certManager.addToQuarantine(anchorCert);
169+
throw new CertificateException();
170+
}
171+
172+
} catch (CertificateVerificationException e1) {
173+
throw new CertificateException();
174+
} catch (NoSuchAlgorithmException e) {
175+
throw new CertificateException();
176+
} catch (NoSuchProviderException e) {
177+
throw new CertificateException();
129178
}
130179
}
131180

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.acuity.iot.dsa.dslink.sys.cert;
2+
3+
import java.security.cert.CertificateEncodingException;
4+
import java.security.cert.X509Certificate;
5+
import java.util.Base64;
6+
import java.util.Base64.Encoder;
7+
import org.iot.dsa.node.DSIObject;
8+
import org.iot.dsa.node.DSNode;
9+
import org.iot.dsa.time.DSTime;
10+
11+
/**
12+
* @author Daniel Shapiro
13+
*/
14+
public class CertCollection extends DSNode {
15+
16+
public void addCertificate(X509Certificate cert) throws CertificateEncodingException {
17+
String name = certToName(cert);
18+
addCertificate(name, encodeCertificate(cert));
19+
}
20+
21+
public void addCertificate(String name, String cert) {
22+
put(name, new CertNode().updateValue(cert));
23+
}
24+
25+
public boolean containsCertificate(X509Certificate cert) {
26+
DSIObject obj = get(certToName(cert));
27+
String certStr;
28+
try {
29+
certStr = encodeCertificate(cert);
30+
} catch (CertificateEncodingException e) {
31+
warn(e);
32+
return false;
33+
}
34+
return obj != null && obj instanceof CertNode && certStr.equals(((CertNode) obj).toElement().toString());
35+
}
36+
37+
public static String certToName(X509Certificate cert) {
38+
return DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder(cert.getIssuerX500Principal().getName())).toString();
39+
}
40+
41+
public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException {
42+
Encoder encoder = Base64.getEncoder();
43+
return encoder.encodeToString(cert.getEncoded());
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.acuity.iot.dsa.dslink.sys.cert;
2+
3+
import org.iot.dsa.node.DSInfo;
4+
import org.iot.dsa.node.DSString;
5+
import org.iot.dsa.node.DSValueNode;
6+
import org.iot.dsa.node.action.ActionInvocation;
7+
import org.iot.dsa.node.action.ActionResult;
8+
import org.iot.dsa.node.action.DSAction;
9+
10+
/**
11+
* @author Daniel Shapiro
12+
*/
13+
public class CertNode extends DSValueNode {
14+
15+
private static final String VALUE = "value";
16+
private static final String ALLOW = "Allow";
17+
private static final String REMOVE = "Remove";
18+
19+
private DSInfo value = getInfo(VALUE);
20+
private DSInfo allow = getInfo(ALLOW);
21+
private DSInfo remove = getInfo(REMOVE);
22+
23+
private SysCertManager certManager;
24+
25+
@Override
26+
protected void declareDefaults() {
27+
super.declareDefaults();
28+
declareDefault(VALUE, DSString.valueOf("")).setHidden(true).setReadOnly(true);
29+
declareDefault(ALLOW, DSAction.DEFAULT);
30+
declareDefault(REMOVE, DSAction.DEFAULT);
31+
}
32+
33+
public CertNode updateValue(String newVal) {
34+
put(VALUE, newVal);
35+
return this;
36+
}
37+
38+
@Override
39+
public DSInfo getValueChild() {
40+
return value;
41+
}
42+
43+
@Override
44+
public ActionResult onInvoke(DSInfo action, ActionInvocation invocation) {
45+
if (action == remove) {
46+
remove();
47+
} else if (action == allow) {
48+
allow();
49+
} else {
50+
super.onInvoke(action, invocation);
51+
}
52+
return null;
53+
}
54+
55+
private void remove() {
56+
getParent().remove(getInfo());
57+
}
58+
59+
private void allow() {
60+
getCertManager().allow(getInfo());
61+
}
62+
63+
public SysCertManager getCertManager() {
64+
if (certManager == null) {
65+
certManager = (SysCertManager) getAncestor(SysCertManager.class);
66+
}
67+
return certManager;
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.acuity.iot.dsa.dslink.sys.cert;
2+
3+
/**
4+
* This class wraps an exception that could be thrown during
5+
* the certificate verification process.
6+
*
7+
* @author Svetlin Nakov
8+
*/
9+
public class CertificateVerificationException extends Exception {
10+
private static final long serialVersionUID = 1L;
11+
12+
public CertificateVerificationException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
16+
public CertificateVerificationException(String message) {
17+
super(message);
18+
}
19+
}

0 commit comments

Comments
 (0)