Skip to content

Commit e5f6d16

Browse files
committed
begin implementing basic quarantine/local trust store
1 parent d7910ba commit e5f6d16

File tree

4 files changed

+156
-2
lines changed

4 files changed

+156
-2
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,16 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
113113
if (certManager.allowAnonymousClients()) {
114114
return;
115115
}
116+
if (certManager.isInTrustStore(chain[0])) {
117+
return;
118+
}
116119
if (defaultX509Mgr != null) {
117-
defaultX509Mgr.checkClientTrusted(chain, authType);
120+
try {
121+
defaultX509Mgr.checkClientTrusted(chain, authType);
122+
} catch (CertificateException e) {
123+
certManager.addToQuarantine(chain[0]);
124+
throw e;
125+
}
118126
}
119127
}
120128

@@ -124,8 +132,16 @@ public void checkServerTrusted(X509Certificate[] chain, String authType)
124132
if (certManager.allowAnonymousServers()) {
125133
return;
126134
}
135+
if (certManager.isInTrustStore(chain[0])) {
136+
return;
137+
}
127138
if (defaultX509Mgr != null) {
128-
defaultX509Mgr.checkServerTrusted(chain, authType);
139+
try {
140+
defaultX509Mgr.checkServerTrusted(chain, authType);
141+
} catch (CertificateException e) {
142+
certManager.addToQuarantine(chain[0]);
143+
throw e;
144+
}
129145
}
130146
}
131147

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
10+
public class CertCollection extends DSNode {
11+
12+
public void addCertificate(X509Certificate cert) throws CertificateEncodingException {
13+
String name = certToName(cert);
14+
addCertificate(name, encodeCertificate(cert));
15+
}
16+
17+
public void addCertificate(String name, String cert) {
18+
put(name, new CertNode().updateValue(cert));
19+
}
20+
21+
public boolean containsCertificate(X509Certificate cert) {
22+
DSIObject obj = get(certToName(cert));
23+
String certStr;
24+
try {
25+
certStr = encodeCertificate(cert);
26+
} catch (CertificateEncodingException e) {
27+
warn(e);
28+
return false;
29+
}
30+
return obj != null && obj instanceof CertNode && certStr.equals(((CertNode) obj).toElement().toString());
31+
}
32+
33+
public static String certToName(X509Certificate cert) {
34+
return cert.getIssuerX500Principal().getName() + "-" + Integer.toHexString(cert.hashCode());
35+
}
36+
37+
public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException {
38+
Encoder encoder = Base64.getEncoder();
39+
return encoder.encodeToString(cert.getEncoded());
40+
}
41+
42+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
public class CertNode extends DSValueNode {
11+
12+
private static final String VALUE = "value";
13+
private static final String ALLOW = "Allow";
14+
private static final String REMOVE = "Remove";
15+
16+
private DSInfo value = getInfo(VALUE);
17+
private DSInfo allow = getInfo(ALLOW);
18+
private DSInfo remove = getInfo(REMOVE);
19+
20+
private SysCertManager certManager;
21+
22+
@Override
23+
protected void declareDefaults() {
24+
super.declareDefaults();
25+
declareDefault(VALUE, DSString.valueOf("")).setHidden(true).setReadOnly(true);
26+
declareDefault(ALLOW, DSAction.DEFAULT);
27+
declareDefault(REMOVE, DSAction.DEFAULT);
28+
}
29+
30+
public CertNode updateValue(String newVal) {
31+
put(VALUE, newVal);
32+
return this;
33+
}
34+
35+
@Override
36+
public DSInfo getValueChild() {
37+
return value;
38+
}
39+
40+
@Override
41+
public ActionResult onInvoke(DSInfo action, ActionInvocation invocation) {
42+
if (action == remove) {
43+
remove();
44+
} else if (action == allow) {
45+
allow();
46+
} else {
47+
super.onInvoke(action, invocation);
48+
}
49+
return null;
50+
}
51+
52+
private void remove() {
53+
getParent().remove(getInfo());
54+
}
55+
56+
private void allow() {
57+
getCertManager().allow(getInfo());
58+
}
59+
60+
public SysCertManager getCertManager() {
61+
if (certManager == null) {
62+
certManager = (SysCertManager) getAncestor(SysCertManager.class);
63+
}
64+
return certManager;
65+
}
66+
67+
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.acuity.iot.dsa.dslink.sys.cert;
22

33
import java.io.File;
4+
import java.security.cert.CertificateEncodingException;
5+
import java.security.cert.X509Certificate;
46
import org.iot.dsa.node.DSBool;
57
import org.iot.dsa.node.DSInfo;
68
import org.iot.dsa.node.DSNode;
@@ -24,6 +26,8 @@ public class SysCertManager extends DSNode {
2426
private static final String CERTFILE = "Cert_File";
2527
private static final String CERTFILE_PASS = "Cert_File_Pass";
2628
private static final String CERTFILE_TYPE = "Cert_File_Type";
29+
private static final String LOCAL_TRUSTSTORE = "Local_Truststore";
30+
private static final String QUARANTINE = "Quarantine";
2731

2832
// Fields
2933
// ------
@@ -33,6 +37,8 @@ public class SysCertManager extends DSNode {
3337
private DSInfo keystore = getInfo(CERTFILE);
3438
private DSInfo keystorePass = getInfo(CERTFILE_PASS);
3539
private DSInfo keystoreType = getInfo(CERTFILE_TYPE);
40+
private CertCollection localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject();
41+
private CertCollection quarantine = (CertCollection) getInfo(QUARANTINE).getObject();
3642

3743
// Methods
3844
// -------
@@ -58,6 +64,8 @@ public void declareDefaults() {
5864
declareDefault(CERTFILE, DSString.valueOf("dslink.jks"));
5965
declareDefault(CERTFILE_TYPE, DSString.valueOf("JKS"));
6066
declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks"));
67+
declareDefault(LOCAL_TRUSTSTORE, new CertCollection());
68+
declareDefault(QUARANTINE, new CertCollection()).setTransient(true);
6169
}
6270

6371
private String getCertFilePass() {
@@ -108,4 +116,25 @@ public void onStarted() {
108116
}
109117
}
110118

119+
public boolean isInTrustStore(X509Certificate cert) {
120+
return localTruststore.containsCertificate(cert);
121+
}
122+
123+
public void addToQuarantine(X509Certificate cert) {
124+
try {
125+
quarantine.addCertificate(cert);
126+
} catch (CertificateEncodingException e) {
127+
error("", e);
128+
}
129+
}
130+
131+
public void allow(DSInfo certInfo) {
132+
String name = certInfo.getName();
133+
CertNode certNode = (CertNode) certInfo.getNode();
134+
String certStr = certNode.toElement().toString();
135+
quarantine.remove(certInfo);
136+
localTruststore.addCertificate(name, certStr);
137+
}
138+
139+
111140
}

0 commit comments

Comments
 (0)