From e5f6d16f6b111b1e4c6ab7c56d2784197001e847 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 15:17:59 -0700 Subject: [PATCH 01/22] begin implementing basic quarantine/local trust store --- .../sys/cert/AnonymousTrustFactory.java | 20 +++++- .../dsa/dslink/sys/cert/CertCollection.java | 42 ++++++++++++ .../iot/dsa/dslink/sys/cert/CertNode.java | 67 +++++++++++++++++++ .../dsa/dslink/sys/cert/SysCertManager.java | 29 ++++++++ 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index 8072668a..76763af9 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -113,8 +113,16 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousClients()) { return; } + if (certManager.isInTrustStore(chain[0])) { + return; + } if (defaultX509Mgr != null) { - defaultX509Mgr.checkClientTrusted(chain, authType); + try { + defaultX509Mgr.checkClientTrusted(chain, authType); + } catch (CertificateException e) { + certManager.addToQuarantine(chain[0]); + throw e; + } } } @@ -124,8 +132,16 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousServers()) { return; } + if (certManager.isInTrustStore(chain[0])) { + return; + } if (defaultX509Mgr != null) { - defaultX509Mgr.checkServerTrusted(chain, authType); + try { + defaultX509Mgr.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + certManager.addToQuarantine(chain[0]); + throw e; + } } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java new file mode 100644 index 00000000..05b9781a --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java @@ -0,0 +1,42 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Base64.Encoder; +import org.iot.dsa.node.DSIObject; +import org.iot.dsa.node.DSNode; + +public class CertCollection extends DSNode { + + public void addCertificate(X509Certificate cert) throws CertificateEncodingException { + String name = certToName(cert); + addCertificate(name, encodeCertificate(cert)); + } + + public void addCertificate(String name, String cert) { + put(name, new CertNode().updateValue(cert)); + } + + public boolean containsCertificate(X509Certificate cert) { + DSIObject obj = get(certToName(cert)); + String certStr; + try { + certStr = encodeCertificate(cert); + } catch (CertificateEncodingException e) { + warn(e); + return false; + } + return obj != null && obj instanceof CertNode && certStr.equals(((CertNode) obj).toElement().toString()); + } + + public static String certToName(X509Certificate cert) { + return cert.getIssuerX500Principal().getName() + "-" + Integer.toHexString(cert.hashCode()); + } + + public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException { + Encoder encoder = Base64.getEncoder(); + return encoder.encodeToString(cert.getEncoded()); + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java new file mode 100644 index 00000000..1d579c33 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java @@ -0,0 +1,67 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSString; +import org.iot.dsa.node.DSValueNode; +import org.iot.dsa.node.action.ActionInvocation; +import org.iot.dsa.node.action.ActionResult; +import org.iot.dsa.node.action.DSAction; + +public class CertNode extends DSValueNode { + + private static final String VALUE = "value"; + private static final String ALLOW = "Allow"; + private static final String REMOVE = "Remove"; + + private DSInfo value = getInfo(VALUE); + private DSInfo allow = getInfo(ALLOW); + private DSInfo remove = getInfo(REMOVE); + + private SysCertManager certManager; + + @Override + protected void declareDefaults() { + super.declareDefaults(); + declareDefault(VALUE, DSString.valueOf("")).setHidden(true).setReadOnly(true); + declareDefault(ALLOW, DSAction.DEFAULT); + declareDefault(REMOVE, DSAction.DEFAULT); + } + + public CertNode updateValue(String newVal) { + put(VALUE, newVal); + return this; + } + + @Override + public DSInfo getValueChild() { + return value; + } + + @Override + public ActionResult onInvoke(DSInfo action, ActionInvocation invocation) { + if (action == remove) { + remove(); + } else if (action == allow) { + allow(); + } else { + super.onInvoke(action, invocation); + } + return null; + } + + private void remove() { + getParent().remove(getInfo()); + } + + private void allow() { + getCertManager().allow(getInfo()); + } + + public SysCertManager getCertManager() { + if (certManager == null) { + certManager = (SysCertManager) getAncestor(SysCertManager.class); + } + return certManager; + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 657cea24..35731636 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -1,6 +1,8 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.io.File; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSNode; @@ -24,6 +26,8 @@ public class SysCertManager extends DSNode { private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; + private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; + private static final String QUARANTINE = "Quarantine"; // Fields // ------ @@ -33,6 +37,8 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); + private CertCollection localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); + private CertCollection quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); // Methods // ------- @@ -58,6 +64,8 @@ public void declareDefaults() { declareDefault(CERTFILE, DSString.valueOf("dslink.jks")); declareDefault(CERTFILE_TYPE, DSString.valueOf("JKS")); declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); + declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); + declareDefault(QUARANTINE, new CertCollection()).setTransient(true); } private String getCertFilePass() { @@ -108,4 +116,25 @@ public void onStarted() { } } + public boolean isInTrustStore(X509Certificate cert) { + return localTruststore.containsCertificate(cert); + } + + public void addToQuarantine(X509Certificate cert) { + try { + quarantine.addCertificate(cert); + } catch (CertificateEncodingException e) { + error("", e); + } + } + + public void allow(DSInfo certInfo) { + String name = certInfo.getName(); + CertNode certNode = (CertNode) certInfo.getNode(); + String certStr = certNode.toElement().toString(); + quarantine.remove(certInfo); + localTruststore.addCertificate(name, certStr); + } + + } From f9a70756c827ecaacdcd5fb802cc8d009a1147cb Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 15:41:10 -0700 Subject: [PATCH 02/22] bugfix --- .../dsa/dslink/sys/cert/SysCertManager.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 35731636..b9ec4b5e 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -37,9 +37,23 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); - private CertCollection localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); - private CertCollection quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); + private CertCollection localTruststore; + private CertCollection quarantine; + private CertCollection getLocalTruststore() { + if (localTruststore == null) { + localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); + } + return localTruststore; + } + + private CertCollection getQuarantine() { + if (quarantine == null) { + quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); + } + return quarantine; + } + // Methods // ------- @@ -117,12 +131,12 @@ public void onStarted() { } public boolean isInTrustStore(X509Certificate cert) { - return localTruststore.containsCertificate(cert); + return getLocalTruststore().containsCertificate(cert); } public void addToQuarantine(X509Certificate cert) { try { - quarantine.addCertificate(cert); + getQuarantine().addCertificate(cert); } catch (CertificateEncodingException e) { error("", e); } @@ -132,8 +146,8 @@ public void allow(DSInfo certInfo) { String name = certInfo.getName(); CertNode certNode = (CertNode) certInfo.getNode(); String certStr = certNode.toElement().toString(); - quarantine.remove(certInfo); - localTruststore.addCertificate(name, certStr); + getQuarantine().remove(certInfo); + getLocalTruststore().addCertificate(name, certStr); } From dba39c47690b10ff9ebb573b67504631b7035c24 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 17:18:34 -0700 Subject: [PATCH 03/22] work on making certificate verification more correct --- .../sys/cert/AnonymousTrustFactory.java | 41 +++-- .../CertificateVerificationException.java | 19 +++ .../dslink/sys/cert/CertificateVerifier.java | 161 ++++++++++++++++++ 3 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index 76763af9..fad9a705 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -4,9 +4,14 @@ import java.security.Provider; import java.security.Security; import java.security.cert.CertificateException; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.net.ssl.*; /** @@ -113,17 +118,14 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousClients()) { return; } - if (certManager.isInTrustStore(chain[0])) { - return; - } if (defaultX509Mgr != null) { try { defaultX509Mgr.checkClientTrusted(chain, authType); + return; } catch (CertificateException e) { - certManager.addToQuarantine(chain[0]); - throw e; } } + checkLocally(chain, authType); } @Override @@ -132,17 +134,36 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousServers()) { return; } - if (certManager.isInTrustStore(chain[0])) { - return; - } if (defaultX509Mgr != null) { try { defaultX509Mgr.checkServerTrusted(chain, authType); + return; } catch (CertificateException e) { - certManager.addToQuarantine(chain[0]); - throw e; } } + checkLocally(chain, authType); + } + + private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException { + Set chainAsSet = new HashSet(); + Collections.addAll(chainAsSet, chain); + try { + PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); + TrustAnchor anchor = result.getTrustAnchor(); + X509Certificate anchorCert = anchor.getTrustedCert(); + + if (anchorCert == null) { + throw new CertificateException(); + } + + if (!certManager.isInTrustStore(anchorCert)) { + certManager.addToQuarantine(anchorCert); + throw new CertificateException(); + } + + } catch (CertificateVerificationException e1) { + throw new CertificateException(); + } } @Override diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java new file mode 100644 index 00000000..8c51f3c0 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java @@ -0,0 +1,19 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +/** + * This class wraps an exception that could be thrown during + * the certificate verification process. + * + * @author Svetlin Nakov + */ +public class CertificateVerificationException extends Exception { + private static final long serialVersionUID = 1L; + + public CertificateVerificationException(String message, Throwable cause) { + super(message, cause); + } + + public CertificateVerificationException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java new file mode 100644 index 00000000..2ab9ea95 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -0,0 +1,161 @@ +package com.acuity.iot.dsa.dslink.sys.cert; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertStore; +import java.security.cert.CertificateException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.Set; + +/** + * Class for building a certification chain for given certificate and verifying + * it. Relies on a set of root CA certificates and intermediate certificates + * that will be used for building the certification chain. The verification + * process assumes that all self-signed certificates in the set are trusted + * root CA certificates and all other certificates in the set are intermediate + * certificates. + * + * @author Svetlin Nakov + */ +public class CertificateVerifier { + + /** + * Attempts to build a certification chain for given certificate and to verify + * it. Relies on a set of root CA certificates and intermediate certificates + * that will be used for building the certification chain. The verification + * process assumes that all self-signed certificates in the set are trusted + * root CA certificates and all other certificates in the set are intermediate + * certificates. + * + * @param cert - certificate for validation + * @param additionalCerts - set of trusted root CA certificates that will be + * used as "trust anchors" and intermediate CA certificates that will be + * used as part of the certification chain. All self-signed certificates + * are considered to be trusted root CA certificates. All the rest are + * considered to be intermediate CA certificates. + * @return the certification chain (if verification is successful) + * @throws CertificateVerificationException - if the certification is not + * successful (e.g. certification path cannot be built or some + * certificate in the chain is expired or CRL checks are failed) + */ + public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, + Set additionalCerts) + throws CertificateVerificationException { + try { + // Check for self-signed certificate + if (isSelfSigned(cert)) { + throw new CertificateVerificationException( + "The certificate is self-signed."); + } + + // Prepare a set of trusted root CA certificates + // and a set of intermediate certificates + Set trustedRootCerts = new HashSet(); + Set intermediateCerts = new HashSet(); + for (X509Certificate additionalCert : additionalCerts) { + if (isSelfSigned(additionalCert)) { + trustedRootCerts.add(additionalCert); + } else { + intermediateCerts.add(additionalCert); + } + } + + // Attempt to build the certification chain and verify it + PKIXCertPathBuilderResult verifiedCertChain = + verifyCertificate(cert, trustedRootCerts, intermediateCerts); + + // Check whether the certificate is revoked by the CRL + // given in its CRL distribution point extension +// CRLVerifier.verifyCertificateCRLs(cert); //TODO + + // The chain is built and verified. Return it as a result + return verifiedCertChain; + } catch (CertPathBuilderException certPathEx) { + throw new CertificateVerificationException( + "Error building certification path: " + + cert.getSubjectX500Principal(), certPathEx); + } catch (CertificateVerificationException cvex) { + throw cvex; + } catch (Exception ex) { + throw new CertificateVerificationException( + "Error verifying the certificate: " + + cert.getSubjectX500Principal(), ex); + } + } + + /** + * Checks whether given X.509 certificate is self-signed. + */ + public static boolean isSelfSigned(X509Certificate cert) + throws CertificateException, NoSuchAlgorithmException, + NoSuchProviderException { + try { + // Try to verify certificate signature with its own public key + PublicKey key = cert.getPublicKey(); + cert.verify(key); + return true; + } catch (SignatureException sigEx) { + // Invalid signature --> not self-signed + return false; + } catch (InvalidKeyException keyEx) { + // Invalid key --> not self-signed + return false; + } + } + + /** + * Attempts to build a certification chain for given certificate and to verify + * it. Relies on a set of root CA certificates (trust anchors) and a set of + * intermediate certificates (to be used as part of the chain). + * @param cert - certificate for validation + * @param trustedRootCerts - set of trusted root CA certificates + * @param intermediateCerts - set of intermediate certificates + * @return the certification chain (if verification is successful) + * @throws GeneralSecurityException - if the verification is not successful + * (e.g. certification path cannot be built or some certificate in the + * chain is expired) + */ + private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set trustedRootCerts, + Set intermediateCerts) throws GeneralSecurityException { + + // Create the selector that specifies the starting certificate + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(cert); + + // Create the trust anchors (set of root CA certificates) + Set trustAnchors = new HashSet(); + for (X509Certificate trustedRootCert : trustedRootCerts) { + trustAnchors.add(new TrustAnchor(trustedRootCert, null)); + } + + // Configure the PKIX certificate builder algorithm parameters + PKIXBuilderParameters pkixParams = + new PKIXBuilderParameters(trustAnchors, selector); + + // Disable CRL checks (this is done manually as additional step) + pkixParams.setRevocationEnabled(false); + + // Specify a list of intermediate certificates + CertStore intermediateCertStore = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(intermediateCerts), "BC"); + pkixParams.addCertStore(intermediateCertStore); + + // Build and verify the certification chain + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); + PKIXCertPathBuilderResult result = + (PKIXCertPathBuilderResult) builder.build(pkixParams); + return result; + } + +} \ No newline at end of file From e9a00cf7407596efa9c1494ed220d6257e2e0fd1 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Wed, 25 Jul 2018 16:14:21 -0700 Subject: [PATCH 04/22] check CRLs when verifying a cert; add action that generates a CSR --- dslink-v2/build.gradle | 2 + .../sys/cert/AnonymousTrustFactory.java | 17 +- .../iot/dsa/dslink/sys/cert/CRLVerifier.java | 189 ++++++++++++++++++ .../dslink/sys/cert/CertificateVerifier.java | 2 +- .../dsa/dslink/sys/cert/SysCertManager.java | 87 ++++++++ 5 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java diff --git a/dslink-v2/build.gradle b/dslink-v2/build.gradle index 07a12bc1..f40a6d72 100644 --- a/dslink-v2/build.gradle +++ b/dslink-v2/build.gradle @@ -4,6 +4,8 @@ artifacts { } dependencies { + compile 'org.bouncycastle:bcprov-jdk15on:+' + compile 'org.bouncycastle:bcpkix-jdk15on:+' testImplementation 'junit:junit:[4.12,)' } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index fad9a705..89811d2b 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -1,6 +1,8 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; import java.security.cert.CertificateException; @@ -147,10 +149,15 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException { Set chainAsSet = new HashSet(); Collections.addAll(chainAsSet, chain); + X509Certificate anchorCert; try { - PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); - TrustAnchor anchor = result.getTrustAnchor(); - X509Certificate anchorCert = anchor.getTrustedCert(); + if (CertificateVerifier.isSelfSigned(chain[0])) { + anchorCert = chain[0]; + } else { + PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); + TrustAnchor anchor = result.getTrustAnchor(); + anchorCert = anchor.getTrustedCert(); + } if (anchorCert == null) { throw new CertificateException(); @@ -163,6 +170,10 @@ private void checkLocally(X509Certificate[] chain, String authType) throws Certi } catch (CertificateVerificationException e1) { throw new CertificateException(); + } catch (NoSuchAlgorithmException e) { + throw new CertificateException(); + } catch (NoSuchProviderException e) { + throw new CertificateException(); } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java new file mode 100644 index 00000000..97db1ca5 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java @@ -0,0 +1,189 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.X509Extensions; + +/** + * Class that verifies CRLs for given X509 certificate. Extracts the CRL + * distribution points from the certificate (if available) and checks the + * certificate revocation status against the CRLs coming from the + * distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. + * + * @author Svetlin Nakov + */ +public class CRLVerifier { + + /** + * Extracts the CRL distribution points from the certificate (if available) + * and checks the certificate revocation status against the CRLs coming from + * the distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. + * + * @param cert the certificate to be checked for revocation + * @throws CertificateVerificationException if the certificate is revoked + */ + public static void verifyCertificateCRLs(X509Certificate cert) + throws CertificateVerificationException { + try { + List crlDistPoints = getCrlDistributionPoints(cert); + for (String crlDP : crlDistPoints) { + X509CRL crl = downloadCRL(crlDP); + if (crl.isRevoked(cert)) { + throw new CertificateVerificationException( + "The certificate is revoked by CRL: " + crlDP); + } + } + } catch (Exception ex) { + if (ex instanceof CertificateVerificationException) { + throw (CertificateVerificationException) ex; + } else { + throw new CertificateVerificationException( + "Can not verify CRL for certificate: " + + cert.getSubjectX500Principal()); + } + } + } + + /** + * Downloads CRL from given URL. Supports http, https, ftp and ldap based URLs. + */ + private static X509CRL downloadCRL(String crlURL) throws IOException, + CertificateException, CRLException, + CertificateVerificationException, NamingException { + if (crlURL.startsWith("http://") || crlURL.startsWith("https://") + || crlURL.startsWith("ftp://")) { + X509CRL crl = downloadCRLFromWeb(crlURL); + return crl; + } else if (crlURL.startsWith("ldap://")) { + X509CRL crl = downloadCRLFromLDAP(crlURL); + return crl; + } else { + throw new CertificateVerificationException( + "Can not download CRL from certificate " + + "distribution point: " + crlURL); + } + } + + /** + * Downloads a CRL from given LDAP url, e.g. + * ldap://ldap.infonotary.com/dc=identity-ca,dc=infonotary,dc=com + */ + private static X509CRL downloadCRLFromLDAP(String ldapURL) + throws CertificateException, NamingException, CRLException, + CertificateVerificationException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapURL); + + DirContext ctx = new InitialDirContext(env); + Attributes avals = ctx.getAttributes(""); + Attribute aval = avals.get("certificateRevocationList;binary"); + byte[] val = (byte[])aval.get(); + if ((val == null) || (val.length == 0)) { + throw new CertificateVerificationException( + "Can not download CRL from: " + ldapURL); + } else { + InputStream inStream = new ByteArrayInputStream(val); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL)cf.generateCRL(inStream); + return crl; + } + } + + /** + * Downloads a CRL from given HTTP/HTTPS/FTP URL, e.g. + * http://crl.infonotary.com/crl/identity-ca.crl + */ + private static X509CRL downloadCRLFromWeb(String crlURL) + throws MalformedURLException, IOException, CertificateException, + CRLException { + URL url = new URL(crlURL); + InputStream crlStream = url.openStream(); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(crlStream); + return crl; + } finally { + crlStream.close(); + } + } + + /** + * Extracts all CRL distribution point URLs from the "CRL Distribution Point" + * extension in a X.509 certificate. If CRL distribution point extension is + * unavailable, returns an empty list. + */ + public static List getCrlDistributionPoints( + X509Certificate cert) throws CertificateParsingException, IOException { + byte[] crldpExt = cert.getExtensionValue( + X509Extensions.CRLDistributionPoints.getId()); + if (crldpExt == null) { + List emptyList = new ArrayList(); + return emptyList; + } + ASN1InputStream oAsnInStream = new ASN1InputStream( + new ByteArrayInputStream(crldpExt)); + ASN1Primitive derObjCrlDP = oAsnInStream.readObject(); + DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP; + byte[] crldpExtOctets = dosCrlDP.getOctets(); + ASN1InputStream oAsnInStream2 = new ASN1InputStream( + new ByteArrayInputStream(crldpExtOctets)); + ASN1Primitive derObj2 = oAsnInStream2.readObject(); + CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2); + + oAsnInStream.close(); + oAsnInStream2.close(); + + List crlUrls = new ArrayList(); + for (DistributionPoint dp : distPoint.getDistributionPoints()) { + DistributionPointName dpn = dp.getDistributionPoint(); + // Look for URIs in fullName + if (dpn != null) { + if (dpn.getType() == DistributionPointName.FULL_NAME) { + GeneralName[] genNames = GeneralNames.getInstance( + dpn.getName()).getNames(); + // Look for an URI + for (int j = 0; j < genNames.length; j++) { + if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) { + String url = DERIA5String.getInstance( + genNames[j].getName()).getString(); + crlUrls.add(url); + } + } + } + } + } + return crlUrls; + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java index 2ab9ea95..86903b05 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -77,7 +77,7 @@ public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, // Check whether the certificate is revoked by the CRL // given in its CRL distribution point extension -// CRLVerifier.verifyCertificateCRLs(cert); //TODO + CRLVerifier.verifyCertificateCRLs(cert); // The chain is built and verified. Return it as a result return verifiedCertChain; diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index b9ec4b5e..19326de0 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -1,13 +1,41 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import java.time.format.ResolverStyle; +import java.util.Iterator; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; +import org.iot.dsa.node.DSIValue; import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSString; +import org.iot.dsa.node.DSValueType; +import org.iot.dsa.node.action.ActionInvocation; +import org.iot.dsa.node.action.ActionResult; +import org.iot.dsa.node.action.ActionSpec; +import org.iot.dsa.node.action.ActionSpec.ResultType; +import org.iot.dsa.node.action.ActionValues; +import org.iot.dsa.node.action.DSAbstractAction; +import org.iot.dsa.node.action.DSAction; +import org.iot.dsa.node.action.DSActionValues; import org.iot.dsa.security.DSPasswordAes128; +import org.iot.dsa.util.DSException; /** * Certificate management for the whole process. This is basically a stub for future @@ -28,6 +56,7 @@ public class SysCertManager extends DSNode { private static final String CERTFILE_TYPE = "Cert_File_Type"; private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; private static final String QUARANTINE = "Quarantine"; + private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; // Fields // ------ @@ -37,6 +66,7 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); + private DSInfo generateCSR = getInfo(GENERATE_CSR); private CertCollection localTruststore; private CertCollection quarantine; @@ -80,6 +110,24 @@ public void declareDefaults() { declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); declareDefault(QUARANTINE, new CertCollection()).setTransient(true); + declareDefault(GENERATE_CSR, getGenerateCSRAction()); + } + + private DSAbstractAction getGenerateCSRAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + return ((SysCertManager) info.getParent()).generateCSR(info); + } + }; + act.setResultType(ResultType.VALUES); + act.addValueResult("CSR", DSValueType.STRING); + return act; } private String getCertFilePass() { @@ -150,5 +198,44 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } + private ActionResult generateCSR(DSInfo actionInfo) { + KeyPairGenerator keyGen; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + DSException.throwRuntime(e); + return null; + } + keyGen.initialize(2048, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( + new X500Principal("CN=dslink-java-v2"), pair.getPublic()); + JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); + ContentSigner signer; + try { + signer = csBuilder.build(pair.getPrivate()); + } catch (OperatorCreationException e) { + DSException.throwRuntime(e); + return null; + } + PKCS10CertificationRequest csr = p10Builder.build(signer); + StringWriter str = new StringWriter(); + JcaPEMWriter pemWriter = new JcaPEMWriter(str); + try { + pemWriter.writeObject(csr); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } finally { + try { + pemWriter.close(); + str.close(); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } + } + return new DSActionValues(actionInfo.getAction()).addResult(DSString.valueOf(str)); + } } From 2bd98401d30e06fbb59dc1287912227d0d57e602 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Wed, 25 Jul 2018 17:06:32 -0700 Subject: [PATCH 05/22] add code that generates a self-signed certificate (without keytool) --- .../dsa/dslink/sys/cert/SysCertManager.java | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 19326de0..435ae05f 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -3,15 +3,26 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.time.format.ResolverStyle; -import java.util.Iterator; +import java.util.Calendar; +import java.util.Date; import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; @@ -20,7 +31,6 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; -import org.iot.dsa.node.DSIValue; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; @@ -28,11 +38,8 @@ import org.iot.dsa.node.DSValueType; import org.iot.dsa.node.action.ActionInvocation; import org.iot.dsa.node.action.ActionResult; -import org.iot.dsa.node.action.ActionSpec; import org.iot.dsa.node.action.ActionSpec.ResultType; -import org.iot.dsa.node.action.ActionValues; import org.iot.dsa.node.action.DSAbstractAction; -import org.iot.dsa.node.action.DSAction; import org.iot.dsa.node.action.DSActionValues; import org.iot.dsa.security.DSPasswordAes128; import org.iot.dsa.util.DSException; @@ -66,7 +73,6 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); - private DSInfo generateCSR = getInfo(GENERATE_CSR); private CertCollection localTruststore; private CertCollection quarantine; @@ -122,7 +128,8 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - return ((SysCertManager) info.getParent()).generateCSR(info); + String csr = ((SysCertManager) info.getParent()).generateCSR(); + return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); } }; act.setResultType(ResultType.VALUES); @@ -198,7 +205,7 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } - private ActionResult generateCSR(DSInfo actionInfo) { + private static String generateCSR() { KeyPairGenerator keyGen; try { keyGen = KeyPairGenerator.getInstance("RSA"); @@ -209,7 +216,7 @@ private ActionResult generateCSR(DSInfo actionInfo) { keyGen.initialize(2048, new SecureRandom()); KeyPair pair = keyGen.generateKeyPair(); PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( - new X500Principal("CN=dslink-java-v2"), pair.getPublic()); + new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); ContentSigner signer; try { @@ -235,7 +242,56 @@ private ActionResult generateCSR(DSInfo actionInfo) { return null; } } - return new DSActionValues(actionInfo.getAction()).addResult(DSString.valueOf(str)); + return str.toString(); + } + + private static X509Certificate generateSelfSigned() { + KeyPairGenerator keyGen; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + DSException.throwRuntime(e); + return null; + } + keyGen.initialize(2048, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + + Provider bcProvider = new BouncyCastleProvider(); + Security.addProvider(bcProvider); + + long now = System.currentTimeMillis(); + Date startDate = new Date(now); + + X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); + BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startDate); + calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity + Date endDate = calendar.getTime(); + + String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. + + try { + ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); + JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity + certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. + + return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); + } catch (OperatorCreationException e) { + DSException.throwRuntime(e); + return null; + } catch (CertIOException e) { + DSException.throwRuntime(e); + return null; + } catch (CertificateException e) { + DSException.throwRuntime(e); + return null; + } + + } } From 3195766227e025d73bc8a0fcbaec8924a24d8c04 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Fri, 27 Jul 2018 13:17:41 -0700 Subject: [PATCH 06/22] Refactor to not require bouncycastle, add actions for importing certs --- .../dslink/sys/cert/CertificateVerifier.java | 4 +- .../iot/dsa/dslink/sys/cert/KeyToolUtil.java | 93 ++++++ .../dsa/dslink/sys/cert/SysCertManager.java | 282 ++++++++++-------- 3 files changed, 256 insertions(+), 123 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java index 86903b05..17785013 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -77,7 +77,7 @@ public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, // Check whether the certificate is revoked by the CRL // given in its CRL distribution point extension - CRLVerifier.verifyCertificateCRLs(cert); +// CRLVerifier.verifyCertificateCRLs(cert); // The chain is built and verified. Return it as a result return verifiedCertChain; @@ -144,7 +144,7 @@ private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, new PKIXBuilderParameters(trustAnchors, selector); // Disable CRL checks (this is done manually as additional step) - pkixParams.setRevocationEnabled(false); + pkixParams.setRevocationEnabled(true); // Specify a list of intermediate certificates CertStore intermediateCertStore = CertStore.getInstance("Collection", diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java new file mode 100644 index 00000000..6e0628b9 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java @@ -0,0 +1,93 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.iot.dsa.logging.DSLogger; +import org.iot.dsa.time.DSTime; + +public class KeyToolUtil extends DSLogger { + + private static KeyToolUtil inst = new KeyToolUtil(); + private KeyToolUtil() { + + } + + private void executeCommand(String[] cmd) { + try { + ProcessBuilder builder = new ProcessBuilder(); + Process process = builder.command(cmd).start(); + process.waitFor(); + } catch (Exception e) { + error("", e); + } + } + + public static void generateSelfSigned(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-genkey", + "-keystore", keystore, + "-storepass", password, + "-keypass", password, + "-alias", "dsa", + "-keyalg", "RSA", + "-validity", "18000", + "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"" + }; + inst.executeCommand(cmd); + } + + public static String generateCSR(String keystore) throws IOException { + String filename = "dsa.csr"; + String[] cmd = new String[]{ + "keytool", + "-certreq", + "-keystore", keystore, + "-alias", "dsa", + "-keyalg", "RSA", + "-validity", "18000", + "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"", + "-file", filename + }; + inst.executeCommand(cmd); + return new String(Files.readAllBytes(Paths.get(filename))); + } + + public static void importCACert(String keystore, String certStr, String alias) throws IOException { + String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCACert")).toString(); + Files.write(Paths.get(filename), certStr.getBytes()); + String[] cmd = new String[]{ + "keytool", + "-import", + "-trustcacerts", + "-keystore", keystore, + "-alias", alias, + "-file", filename + }; + inst.executeCommand(cmd); + + new File(filename).delete(); + } + + public static void importPrimaryCert(String keystore, String certStr) throws IOException { + String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCert")).toString(); + Files.write(Paths.get(filename), certStr.getBytes()); + String[] cmd = new String[]{ + "keytool", + "-import", + "-trustcacerts", + "-keystore", keystore, + "-alias", "dsa", + "-file", filename + }; + inst.executeCommand(cmd); + + new File(filename).delete(); + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 435ae05f..1396acd1 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -16,20 +16,20 @@ import java.util.Calendar; import java.util.Date; import javax.security.auth.x500.X500Principal; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.cert.CertIOException; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; +//import org.bouncycastle.asn1.ASN1ObjectIdentifier; +//import org.bouncycastle.asn1.x500.X500Name; +//import org.bouncycastle.asn1.x509.BasicConstraints; +//import org.bouncycastle.cert.CertIOException; +//import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +//import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +//import org.bouncycastle.jce.provider.BouncyCastleProvider; +//import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +//import org.bouncycastle.operator.ContentSigner; +//import org.bouncycastle.operator.OperatorCreationException; +//import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +//import org.bouncycastle.pkcs.PKCS10CertificationRequest; +//import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +//import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap; @@ -64,6 +64,8 @@ public class SysCertManager extends DSNode { private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; private static final String QUARANTINE = "Quarantine"; private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; + private static final String IMPORT_CA_CERT = "Import CA Certificate"; + private static final String IMPORT_PRIMARY_CERT = "Import Primary Certificate"; // Fields // ------ @@ -117,6 +119,8 @@ public void declareDefaults() { declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); declareDefault(QUARANTINE, new CertCollection()).setTransient(true); declareDefault(GENERATE_CSR, getGenerateCSRAction()); + declareDefault(IMPORT_CA_CERT, getImportCACertAction()); + declareDefault(IMPORT_PRIMARY_CERT, getImportPrimaryCertAction()); } private DSAbstractAction getGenerateCSRAction() { @@ -128,7 +132,13 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - String csr = ((SysCertManager) info.getParent()).generateCSR(); + String csr; + try { + csr = KeyToolUtil.generateCSR(keystore.getElement().toString()); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); } }; @@ -136,6 +146,54 @@ public ActionResult invoke(DSInfo info, ActionInvocation invocation) { act.addValueResult("CSR", DSValueType.STRING); return act; } + + private DSAbstractAction getImportCACertAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + DSMap parameters = invocation.getParameters(); + String alias = parameters.getString("Alias"); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importCACert(keystore.getElement().toString(), certStr, alias); + } catch (IOException e) { + DSException.throwRuntime(e); + } + return null; + } + }; + act.addParameter("Alias", DSValueType.STRING, null); + act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); + return act; + } + + private DSAbstractAction getImportPrimaryCertAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + DSMap parameters = invocation.getParameters(); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importPrimaryCert(keystore.getElement().toString(), certStr); + } catch (IOException e) { + DSException.throwRuntime(e); + } + return null; + } + }; + act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); + return act; + } private String getCertFilePass() { DSPasswordAes128 pass = (DSPasswordAes128) keystorePass.getObject(); @@ -146,25 +204,7 @@ private String getCertFilePass() { * Executes the java keytool to generate a new self signed cert. */ private void keytoolGenkey() { - try { - String pass = getCertFilePass(); - String[] cmd = new String[]{ - "keytool", - "-genkey", - "-keystore", keystore.getElement().toString(), - "-storepass", pass, - "-keypass", pass, - "-alias", "dsa", - "-keyalg", "RSA", - "-validity", "18000", - "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"" - }; - ProcessBuilder builder = new ProcessBuilder(); - Process process = builder.command(cmd).start(); - process.waitFor(); - } catch (Exception x) { - error(getPath(), x); - } + KeyToolUtil.generateSelfSigned(keystore.getElement().toString(), getCertFilePass()); } @Override @@ -205,93 +245,93 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } - private static String generateCSR() { - KeyPairGenerator keyGen; - try { - keyGen = KeyPairGenerator.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - DSException.throwRuntime(e); - return null; - } - keyGen.initialize(2048, new SecureRandom()); - KeyPair pair = keyGen.generateKeyPair(); - PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( - new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); - JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); - ContentSigner signer; - try { - signer = csBuilder.build(pair.getPrivate()); - } catch (OperatorCreationException e) { - DSException.throwRuntime(e); - return null; - } - PKCS10CertificationRequest csr = p10Builder.build(signer); - StringWriter str = new StringWriter(); - JcaPEMWriter pemWriter = new JcaPEMWriter(str); - try { - pemWriter.writeObject(csr); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } finally { - try { - pemWriter.close(); - str.close(); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } - } - return str.toString(); - } +// private static String generateCSR() { +// KeyPairGenerator keyGen; +// try { +// keyGen = KeyPairGenerator.getInstance("RSA"); +// } catch (NoSuchAlgorithmException e) { +// DSException.throwRuntime(e); +// return null; +// } +// keyGen.initialize(2048, new SecureRandom()); +// KeyPair pair = keyGen.generateKeyPair(); +// PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( +// new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); +// JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); +// ContentSigner signer; +// try { +// signer = csBuilder.build(pair.getPrivate()); +// } catch (OperatorCreationException e) { +// DSException.throwRuntime(e); +// return null; +// } +// PKCS10CertificationRequest csr = p10Builder.build(signer); +// StringWriter str = new StringWriter(); +// JcaPEMWriter pemWriter = new JcaPEMWriter(str); +// try { +// pemWriter.writeObject(csr); +// } catch (IOException e) { +// DSException.throwRuntime(e); +// return null; +// } finally { +// try { +// pemWriter.close(); +// str.close(); +// } catch (IOException e) { +// DSException.throwRuntime(e); +// return null; +// } +// } +// return str.toString(); +// } - private static X509Certificate generateSelfSigned() { - KeyPairGenerator keyGen; - try { - keyGen = KeyPairGenerator.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - DSException.throwRuntime(e); - return null; - } - keyGen.initialize(2048, new SecureRandom()); - KeyPair pair = keyGen.generateKeyPair(); - - Provider bcProvider = new BouncyCastleProvider(); - Security.addProvider(bcProvider); - - long now = System.currentTimeMillis(); - Date startDate = new Date(now); - - X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); - BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(startDate); - calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity - Date endDate = calendar.getTime(); - - String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. - - try { - ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); - JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); - - BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity - certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. - - return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); - } catch (OperatorCreationException e) { - DSException.throwRuntime(e); - return null; - } catch (CertIOException e) { - DSException.throwRuntime(e); - return null; - } catch (CertificateException e) { - DSException.throwRuntime(e); - return null; - } - - - } +// private static X509Certificate generateSelfSigned() { +// KeyPairGenerator keyGen; +// try { +// keyGen = KeyPairGenerator.getInstance("RSA"); +// } catch (NoSuchAlgorithmException e) { +// DSException.throwRuntime(e); +// return null; +// } +// keyGen.initialize(2048, new SecureRandom()); +// KeyPair pair = keyGen.generateKeyPair(); +// +// Provider bcProvider = new BouncyCastleProvider(); +// Security.addProvider(bcProvider); +// +// long now = System.currentTimeMillis(); +// Date startDate = new Date(now); +// +// X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); +// BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number +// +// Calendar calendar = Calendar.getInstance(); +// calendar.setTime(startDate); +// calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity +// Date endDate = calendar.getTime(); +// +// String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. +// +// try { +// ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); +// JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); +// +// BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity +// certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. +// +// return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); +// } catch (OperatorCreationException e) { +// DSException.throwRuntime(e); +// return null; +// } catch (CertIOException e) { +// DSException.throwRuntime(e); +// return null; +// } catch (CertificateException e) { +// DSException.throwRuntime(e); +// return null; +// } +// +// +// } } From ce3ebbe8ce03d7e50ce7f423a60b8f1a43e47b43 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Fri, 27 Jul 2018 14:59:33 -0700 Subject: [PATCH 07/22] change certificate name to contain timestamp instead of hashcode --- .../com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java index 05b9781a..ec85c04a 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java @@ -6,6 +6,7 @@ import java.util.Base64.Encoder; import org.iot.dsa.node.DSIObject; import org.iot.dsa.node.DSNode; +import org.iot.dsa.time.DSTime; public class CertCollection extends DSNode { @@ -31,7 +32,7 @@ public boolean containsCertificate(X509Certificate cert) { } public static String certToName(X509Certificate cert) { - return cert.getIssuerX500Principal().getName() + "-" + Integer.toHexString(cert.hashCode()); + return DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder(cert.getIssuerX500Principal().getName())).toString(); } public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException { From 86fe455998e9f702bd7120d232a9c6244b053ee3 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Mon, 30 Jul 2018 16:29:20 -0700 Subject: [PATCH 08/22] fix bugs, add more keytool actions --- .../iot/dsa/dslink/sys/cert/KeyToolUtil.java | 45 +++++- .../dsa/dslink/sys/cert/SysCertManager.java | 142 +++++++++++++----- 2 files changed, 145 insertions(+), 42 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java index 6e0628b9..f5d78d23 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java @@ -2,8 +2,8 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Paths; @@ -17,13 +17,22 @@ private KeyToolUtil() { } - private void executeCommand(String[] cmd) { + private String executeCommand(String[] cmd) { try { ProcessBuilder builder = new ProcessBuilder(); Process process = builder.command(cmd).start(); process.waitFor(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line = null; + while ( (line = reader.readLine()) != null) { + sb.append(line); + sb.append(System.getProperty("line.separator")); + } + return sb.toString(); } catch (Exception e) { error("", e); + return ""; } } @@ -42,12 +51,13 @@ public static void generateSelfSigned(String keystore, String password) { inst.executeCommand(cmd); } - public static String generateCSR(String keystore) throws IOException { + public static String generateCSR(String keystore, String password) throws IOException { String filename = "dsa.csr"; String[] cmd = new String[]{ "keytool", "-certreq", "-keystore", keystore, + "-storepass", password, "-alias", "dsa", "-keyalg", "RSA", "-validity", "18000", @@ -58,7 +68,7 @@ public static String generateCSR(String keystore) throws IOException { return new String(Files.readAllBytes(Paths.get(filename))); } - public static void importCACert(String keystore, String certStr, String alias) throws IOException { + public static void importCACert(String keystore, String certStr, String alias, String password) throws IOException { String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCACert")).toString(); Files.write(Paths.get(filename), certStr.getBytes()); String[] cmd = new String[]{ @@ -66,6 +76,7 @@ public static void importCACert(String keystore, String certStr, String alias) t "-import", "-trustcacerts", "-keystore", keystore, + "-storepass", password, "-alias", alias, "-file", filename }; @@ -74,7 +85,7 @@ public static void importCACert(String keystore, String certStr, String alias) t new File(filename).delete(); } - public static void importPrimaryCert(String keystore, String certStr) throws IOException { + public static void importPrimaryCert(String keystore, String certStr, String password) throws IOException { String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCert")).toString(); Files.write(Paths.get(filename), certStr.getBytes()); String[] cmd = new String[]{ @@ -82,6 +93,7 @@ public static void importPrimaryCert(String keystore, String certStr) throws IOE "-import", "-trustcacerts", "-keystore", keystore, + "-storepass", password, "-alias", "dsa", "-file", filename }; @@ -89,5 +101,28 @@ public static void importPrimaryCert(String keystore, String certStr) throws IOE new File(filename).delete(); } + + public static String getEntry(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-list", + "-v", + "-keystore", keystore, + "-storepass", password, + "-alias", "dsa", + }; + return inst.executeCommand(cmd); + } + + public static void deleteEntry(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-delete", + "-keystore", keystore, + "-storepass", password, + "-alias", "dsa", + }; + inst.executeCommand(cmd); + } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 1396acd1..80202eef 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -2,20 +2,8 @@ import java.io.File; import java.io.IOException; -import java.io.StringWriter; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.Security; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.Calendar; -import java.util.Date; -import javax.security.auth.x500.X500Principal; //import org.bouncycastle.asn1.ASN1ObjectIdentifier; //import org.bouncycastle.asn1.x500.X500Name; //import org.bouncycastle.asn1.x509.BasicConstraints; @@ -50,6 +38,7 @@ * as accepts self signed (anonymous) certs from the broker. * * @author Aaron Hansen + * @author Daniel Shapiro */ public class SysCertManager extends DSNode { @@ -61,11 +50,14 @@ public class SysCertManager extends DSNode { private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; - private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; + private static final String LOCAL_TRUSTSTORE = "Local Truststore"; private static final String QUARANTINE = "Quarantine"; - private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; + private static final String GENERATE_CSR = "Generate Certificate Signing Request"; private static final String IMPORT_CA_CERT = "Import CA Certificate"; private static final String IMPORT_PRIMARY_CERT = "Import Primary Certificate"; + private static final String GENERATE_SELF_SIGNED = "Generate Self-Signed Certificate"; + private static final String DELETE_KS_ENTRY = "Delete Keystore Entry"; + private static final String GET_KS_ENTRY = "Get Keystore Entry"; // Fields // ------ @@ -121,6 +113,9 @@ public void declareDefaults() { declareDefault(GENERATE_CSR, getGenerateCSRAction()); declareDefault(IMPORT_CA_CERT, getImportCACertAction()); declareDefault(IMPORT_PRIMARY_CERT, getImportPrimaryCertAction()); + declareDefault(GENERATE_SELF_SIGNED, getGenerateSelfSignedAction()); + declareDefault(GET_KS_ENTRY, getGetKSEntryAction()); + declareDefault(DELETE_KS_ENTRY, getDeleteKSEntryAction()); } private DSAbstractAction getGenerateCSRAction() { @@ -132,21 +127,24 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - String csr; - try { - csr = KeyToolUtil.generateCSR(keystore.getElement().toString()); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } - return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); + String csr = ((SysCertManager) info.getParent()).generateCSR(); + return csr != null ? new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)) : null; } }; act.setResultType(ResultType.VALUES); - act.addValueResult("CSR", DSValueType.STRING); + act.addValueResult("CSR", DSValueType.STRING).setEditor("textarea"); return act; } + private String generateCSR() { + try { + return KeyToolUtil.generateCSR(getKeystorePath(), getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } + } + private DSAbstractAction getImportCACertAction() { DSAbstractAction act = new DSAbstractAction() { @@ -157,13 +155,7 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { DSMap parameters = invocation.getParameters(); - String alias = parameters.getString("Alias"); - String certStr = parameters.getString("Certificate"); - try { - KeyToolUtil.importCACert(keystore.getElement().toString(), certStr, alias); - } catch (IOException e) { - DSException.throwRuntime(e); - } + ((SysCertManager) info.getParent()).importCACert(parameters); return null; } }; @@ -172,6 +164,16 @@ public ActionResult invoke(DSInfo info, ActionInvocation invocation) { return act; } + private void importCACert(DSMap parameters) { + String alias = parameters.getString("Alias"); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importCACert(getKeystorePath(), certStr, alias, getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + } + } + private DSAbstractAction getImportPrimaryCertAction() { DSAbstractAction act = new DSAbstractAction() { @@ -182,29 +184,95 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { DSMap parameters = invocation.getParameters(); - String certStr = parameters.getString("Certificate"); - try { - KeyToolUtil.importPrimaryCert(keystore.getElement().toString(), certStr); - } catch (IOException e) { - DSException.throwRuntime(e); - } + ((SysCertManager) info.getParent()).importPrimaryCert(parameters); return null; } }; act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); return act; } + + private void importPrimaryCert(DSMap parameters) { + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importPrimaryCert(getKeystorePath(), certStr, getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + } + } + + private DSAbstractAction getGenerateSelfSignedAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + ((SysCertManager) info.getParent()).keytoolGenkey(); + return null; + } + }; + return act; + } + + private DSAbstractAction getGetKSEntryAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + String result = ((SysCertManager) info.getParent()).getKSEntry(); + return new DSActionValues(info.getAction()).addResult(DSString.valueOf(result)); + } + }; + act.setResultType(ResultType.VALUES); + act.addValueResult("Entry", DSValueType.STRING).setEditor("textarea"); + return act; + } + + private String getKSEntry() { + return KeyToolUtil.getEntry(getKeystorePath(), getCertFilePass()); + } + + private DSAbstractAction getDeleteKSEntryAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + ((SysCertManager) info.getParent()).deleteKSEntry(); + return null; + } + }; + return act; + } + + private void deleteKSEntry() { + KeyToolUtil.deleteEntry(getKeystorePath(), getCertFilePass()); + } private String getCertFilePass() { DSPasswordAes128 pass = (DSPasswordAes128) keystorePass.getObject(); return pass.decode(); } + + private String getKeystorePath() { + return keystore.getElement().toString(); + } /** * Executes the java keytool to generate a new self signed cert. */ private void keytoolGenkey() { - KeyToolUtil.generateSelfSigned(keystore.getElement().toString(), getCertFilePass()); + KeyToolUtil.generateSelfSigned(getKeystorePath(), getCertFilePass()); } @Override From ca18d0a0f709047b8f4431497569e9d61f1363ca Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 15:17:59 -0700 Subject: [PATCH 09/22] begin implementing basic quarantine/local trust store --- .../sys/cert/AnonymousTrustFactory.java | 20 +++++- .../dsa/dslink/sys/cert/CertCollection.java | 42 ++++++++++++ .../iot/dsa/dslink/sys/cert/CertNode.java | 67 +++++++++++++++++++ .../dsa/dslink/sys/cert/SysCertManager.java | 29 ++++++++ 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index 8072668a..76763af9 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -113,8 +113,16 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousClients()) { return; } + if (certManager.isInTrustStore(chain[0])) { + return; + } if (defaultX509Mgr != null) { - defaultX509Mgr.checkClientTrusted(chain, authType); + try { + defaultX509Mgr.checkClientTrusted(chain, authType); + } catch (CertificateException e) { + certManager.addToQuarantine(chain[0]); + throw e; + } } } @@ -124,8 +132,16 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousServers()) { return; } + if (certManager.isInTrustStore(chain[0])) { + return; + } if (defaultX509Mgr != null) { - defaultX509Mgr.checkServerTrusted(chain, authType); + try { + defaultX509Mgr.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + certManager.addToQuarantine(chain[0]); + throw e; + } } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java new file mode 100644 index 00000000..05b9781a --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java @@ -0,0 +1,42 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Base64.Encoder; +import org.iot.dsa.node.DSIObject; +import org.iot.dsa.node.DSNode; + +public class CertCollection extends DSNode { + + public void addCertificate(X509Certificate cert) throws CertificateEncodingException { + String name = certToName(cert); + addCertificate(name, encodeCertificate(cert)); + } + + public void addCertificate(String name, String cert) { + put(name, new CertNode().updateValue(cert)); + } + + public boolean containsCertificate(X509Certificate cert) { + DSIObject obj = get(certToName(cert)); + String certStr; + try { + certStr = encodeCertificate(cert); + } catch (CertificateEncodingException e) { + warn(e); + return false; + } + return obj != null && obj instanceof CertNode && certStr.equals(((CertNode) obj).toElement().toString()); + } + + public static String certToName(X509Certificate cert) { + return cert.getIssuerX500Principal().getName() + "-" + Integer.toHexString(cert.hashCode()); + } + + public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException { + Encoder encoder = Base64.getEncoder(); + return encoder.encodeToString(cert.getEncoded()); + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java new file mode 100644 index 00000000..1d579c33 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java @@ -0,0 +1,67 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSString; +import org.iot.dsa.node.DSValueNode; +import org.iot.dsa.node.action.ActionInvocation; +import org.iot.dsa.node.action.ActionResult; +import org.iot.dsa.node.action.DSAction; + +public class CertNode extends DSValueNode { + + private static final String VALUE = "value"; + private static final String ALLOW = "Allow"; + private static final String REMOVE = "Remove"; + + private DSInfo value = getInfo(VALUE); + private DSInfo allow = getInfo(ALLOW); + private DSInfo remove = getInfo(REMOVE); + + private SysCertManager certManager; + + @Override + protected void declareDefaults() { + super.declareDefaults(); + declareDefault(VALUE, DSString.valueOf("")).setHidden(true).setReadOnly(true); + declareDefault(ALLOW, DSAction.DEFAULT); + declareDefault(REMOVE, DSAction.DEFAULT); + } + + public CertNode updateValue(String newVal) { + put(VALUE, newVal); + return this; + } + + @Override + public DSInfo getValueChild() { + return value; + } + + @Override + public ActionResult onInvoke(DSInfo action, ActionInvocation invocation) { + if (action == remove) { + remove(); + } else if (action == allow) { + allow(); + } else { + super.onInvoke(action, invocation); + } + return null; + } + + private void remove() { + getParent().remove(getInfo()); + } + + private void allow() { + getCertManager().allow(getInfo()); + } + + public SysCertManager getCertManager() { + if (certManager == null) { + certManager = (SysCertManager) getAncestor(SysCertManager.class); + } + return certManager; + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 657cea24..35731636 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -1,6 +1,8 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.io.File; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSNode; @@ -24,6 +26,8 @@ public class SysCertManager extends DSNode { private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; + private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; + private static final String QUARANTINE = "Quarantine"; // Fields // ------ @@ -33,6 +37,8 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); + private CertCollection localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); + private CertCollection quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); // Methods // ------- @@ -58,6 +64,8 @@ public void declareDefaults() { declareDefault(CERTFILE, DSString.valueOf("dslink.jks")); declareDefault(CERTFILE_TYPE, DSString.valueOf("JKS")); declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); + declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); + declareDefault(QUARANTINE, new CertCollection()).setTransient(true); } private String getCertFilePass() { @@ -108,4 +116,25 @@ public void onStarted() { } } + public boolean isInTrustStore(X509Certificate cert) { + return localTruststore.containsCertificate(cert); + } + + public void addToQuarantine(X509Certificate cert) { + try { + quarantine.addCertificate(cert); + } catch (CertificateEncodingException e) { + error("", e); + } + } + + public void allow(DSInfo certInfo) { + String name = certInfo.getName(); + CertNode certNode = (CertNode) certInfo.getNode(); + String certStr = certNode.toElement().toString(); + quarantine.remove(certInfo); + localTruststore.addCertificate(name, certStr); + } + + } From 74176d96e5db6c1d2cf46b81ead5b96e5d94fce4 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 15:41:10 -0700 Subject: [PATCH 10/22] bugfix --- .../dsa/dslink/sys/cert/SysCertManager.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 35731636..b9ec4b5e 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -37,9 +37,23 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); - private CertCollection localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); - private CertCollection quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); + private CertCollection localTruststore; + private CertCollection quarantine; + private CertCollection getLocalTruststore() { + if (localTruststore == null) { + localTruststore = (CertCollection) getInfo(LOCAL_TRUSTSTORE).getObject(); + } + return localTruststore; + } + + private CertCollection getQuarantine() { + if (quarantine == null) { + quarantine = (CertCollection) getInfo(QUARANTINE).getObject(); + } + return quarantine; + } + // Methods // ------- @@ -117,12 +131,12 @@ public void onStarted() { } public boolean isInTrustStore(X509Certificate cert) { - return localTruststore.containsCertificate(cert); + return getLocalTruststore().containsCertificate(cert); } public void addToQuarantine(X509Certificate cert) { try { - quarantine.addCertificate(cert); + getQuarantine().addCertificate(cert); } catch (CertificateEncodingException e) { error("", e); } @@ -132,8 +146,8 @@ public void allow(DSInfo certInfo) { String name = certInfo.getName(); CertNode certNode = (CertNode) certInfo.getNode(); String certStr = certNode.toElement().toString(); - quarantine.remove(certInfo); - localTruststore.addCertificate(name, certStr); + getQuarantine().remove(certInfo); + getLocalTruststore().addCertificate(name, certStr); } From 9b4ea1dfdc27e12dd078ad7019a7afbbf5dddac1 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 24 Jul 2018 17:18:34 -0700 Subject: [PATCH 11/22] work on making certificate verification more correct --- .../sys/cert/AnonymousTrustFactory.java | 41 +++-- .../CertificateVerificationException.java | 19 +++ .../dslink/sys/cert/CertificateVerifier.java | 161 ++++++++++++++++++ 3 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index 76763af9..fad9a705 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -4,9 +4,14 @@ import java.security.Provider; import java.security.Security; import java.security.cert.CertificateException; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.net.ssl.*; /** @@ -113,17 +118,14 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousClients()) { return; } - if (certManager.isInTrustStore(chain[0])) { - return; - } if (defaultX509Mgr != null) { try { defaultX509Mgr.checkClientTrusted(chain, authType); + return; } catch (CertificateException e) { - certManager.addToQuarantine(chain[0]); - throw e; } } + checkLocally(chain, authType); } @Override @@ -132,17 +134,36 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) if (certManager.allowAnonymousServers()) { return; } - if (certManager.isInTrustStore(chain[0])) { - return; - } if (defaultX509Mgr != null) { try { defaultX509Mgr.checkServerTrusted(chain, authType); + return; } catch (CertificateException e) { - certManager.addToQuarantine(chain[0]); - throw e; } } + checkLocally(chain, authType); + } + + private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException { + Set chainAsSet = new HashSet(); + Collections.addAll(chainAsSet, chain); + try { + PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); + TrustAnchor anchor = result.getTrustAnchor(); + X509Certificate anchorCert = anchor.getTrustedCert(); + + if (anchorCert == null) { + throw new CertificateException(); + } + + if (!certManager.isInTrustStore(anchorCert)) { + certManager.addToQuarantine(anchorCert); + throw new CertificateException(); + } + + } catch (CertificateVerificationException e1) { + throw new CertificateException(); + } } @Override diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java new file mode 100644 index 00000000..8c51f3c0 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerificationException.java @@ -0,0 +1,19 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +/** + * This class wraps an exception that could be thrown during + * the certificate verification process. + * + * @author Svetlin Nakov + */ +public class CertificateVerificationException extends Exception { + private static final long serialVersionUID = 1L; + + public CertificateVerificationException(String message, Throwable cause) { + super(message, cause); + } + + public CertificateVerificationException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java new file mode 100644 index 00000000..2ab9ea95 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -0,0 +1,161 @@ +package com.acuity.iot.dsa.dslink.sys.cert; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertStore; +import java.security.cert.CertificateException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.Set; + +/** + * Class for building a certification chain for given certificate and verifying + * it. Relies on a set of root CA certificates and intermediate certificates + * that will be used for building the certification chain. The verification + * process assumes that all self-signed certificates in the set are trusted + * root CA certificates and all other certificates in the set are intermediate + * certificates. + * + * @author Svetlin Nakov + */ +public class CertificateVerifier { + + /** + * Attempts to build a certification chain for given certificate and to verify + * it. Relies on a set of root CA certificates and intermediate certificates + * that will be used for building the certification chain. The verification + * process assumes that all self-signed certificates in the set are trusted + * root CA certificates and all other certificates in the set are intermediate + * certificates. + * + * @param cert - certificate for validation + * @param additionalCerts - set of trusted root CA certificates that will be + * used as "trust anchors" and intermediate CA certificates that will be + * used as part of the certification chain. All self-signed certificates + * are considered to be trusted root CA certificates. All the rest are + * considered to be intermediate CA certificates. + * @return the certification chain (if verification is successful) + * @throws CertificateVerificationException - if the certification is not + * successful (e.g. certification path cannot be built or some + * certificate in the chain is expired or CRL checks are failed) + */ + public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, + Set additionalCerts) + throws CertificateVerificationException { + try { + // Check for self-signed certificate + if (isSelfSigned(cert)) { + throw new CertificateVerificationException( + "The certificate is self-signed."); + } + + // Prepare a set of trusted root CA certificates + // and a set of intermediate certificates + Set trustedRootCerts = new HashSet(); + Set intermediateCerts = new HashSet(); + for (X509Certificate additionalCert : additionalCerts) { + if (isSelfSigned(additionalCert)) { + trustedRootCerts.add(additionalCert); + } else { + intermediateCerts.add(additionalCert); + } + } + + // Attempt to build the certification chain and verify it + PKIXCertPathBuilderResult verifiedCertChain = + verifyCertificate(cert, trustedRootCerts, intermediateCerts); + + // Check whether the certificate is revoked by the CRL + // given in its CRL distribution point extension +// CRLVerifier.verifyCertificateCRLs(cert); //TODO + + // The chain is built and verified. Return it as a result + return verifiedCertChain; + } catch (CertPathBuilderException certPathEx) { + throw new CertificateVerificationException( + "Error building certification path: " + + cert.getSubjectX500Principal(), certPathEx); + } catch (CertificateVerificationException cvex) { + throw cvex; + } catch (Exception ex) { + throw new CertificateVerificationException( + "Error verifying the certificate: " + + cert.getSubjectX500Principal(), ex); + } + } + + /** + * Checks whether given X.509 certificate is self-signed. + */ + public static boolean isSelfSigned(X509Certificate cert) + throws CertificateException, NoSuchAlgorithmException, + NoSuchProviderException { + try { + // Try to verify certificate signature with its own public key + PublicKey key = cert.getPublicKey(); + cert.verify(key); + return true; + } catch (SignatureException sigEx) { + // Invalid signature --> not self-signed + return false; + } catch (InvalidKeyException keyEx) { + // Invalid key --> not self-signed + return false; + } + } + + /** + * Attempts to build a certification chain for given certificate and to verify + * it. Relies on a set of root CA certificates (trust anchors) and a set of + * intermediate certificates (to be used as part of the chain). + * @param cert - certificate for validation + * @param trustedRootCerts - set of trusted root CA certificates + * @param intermediateCerts - set of intermediate certificates + * @return the certification chain (if verification is successful) + * @throws GeneralSecurityException - if the verification is not successful + * (e.g. certification path cannot be built or some certificate in the + * chain is expired) + */ + private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set trustedRootCerts, + Set intermediateCerts) throws GeneralSecurityException { + + // Create the selector that specifies the starting certificate + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(cert); + + // Create the trust anchors (set of root CA certificates) + Set trustAnchors = new HashSet(); + for (X509Certificate trustedRootCert : trustedRootCerts) { + trustAnchors.add(new TrustAnchor(trustedRootCert, null)); + } + + // Configure the PKIX certificate builder algorithm parameters + PKIXBuilderParameters pkixParams = + new PKIXBuilderParameters(trustAnchors, selector); + + // Disable CRL checks (this is done manually as additional step) + pkixParams.setRevocationEnabled(false); + + // Specify a list of intermediate certificates + CertStore intermediateCertStore = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(intermediateCerts), "BC"); + pkixParams.addCertStore(intermediateCertStore); + + // Build and verify the certification chain + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); + PKIXCertPathBuilderResult result = + (PKIXCertPathBuilderResult) builder.build(pkixParams); + return result; + } + +} \ No newline at end of file From 6d077e655bd504a38fc2b5fbc5c3142b755ee5af Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Wed, 25 Jul 2018 16:14:21 -0700 Subject: [PATCH 12/22] check CRLs when verifying a cert; add action that generates a CSR --- dslink-v2/build.gradle | 2 + .../sys/cert/AnonymousTrustFactory.java | 17 +- .../iot/dsa/dslink/sys/cert/CRLVerifier.java | 189 ++++++++++++++++++ .../dslink/sys/cert/CertificateVerifier.java | 2 +- .../dsa/dslink/sys/cert/SysCertManager.java | 87 ++++++++ 5 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java diff --git a/dslink-v2/build.gradle b/dslink-v2/build.gradle index 07a12bc1..f40a6d72 100644 --- a/dslink-v2/build.gradle +++ b/dslink-v2/build.gradle @@ -4,6 +4,8 @@ artifacts { } dependencies { + compile 'org.bouncycastle:bcprov-jdk15on:+' + compile 'org.bouncycastle:bcpkix-jdk15on:+' testImplementation 'junit:junit:[4.12,)' } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index fad9a705..89811d2b 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -1,6 +1,8 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; import java.security.cert.CertificateException; @@ -147,10 +149,15 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException { Set chainAsSet = new HashSet(); Collections.addAll(chainAsSet, chain); + X509Certificate anchorCert; try { - PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); - TrustAnchor anchor = result.getTrustAnchor(); - X509Certificate anchorCert = anchor.getTrustedCert(); + if (CertificateVerifier.isSelfSigned(chain[0])) { + anchorCert = chain[0]; + } else { + PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet); + TrustAnchor anchor = result.getTrustAnchor(); + anchorCert = anchor.getTrustedCert(); + } if (anchorCert == null) { throw new CertificateException(); @@ -163,6 +170,10 @@ private void checkLocally(X509Certificate[] chain, String authType) throws Certi } catch (CertificateVerificationException e1) { throw new CertificateException(); + } catch (NoSuchAlgorithmException e) { + throw new CertificateException(); + } catch (NoSuchProviderException e) { + throw new CertificateException(); } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java new file mode 100644 index 00000000..97db1ca5 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java @@ -0,0 +1,189 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.X509Extensions; + +/** + * Class that verifies CRLs for given X509 certificate. Extracts the CRL + * distribution points from the certificate (if available) and checks the + * certificate revocation status against the CRLs coming from the + * distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. + * + * @author Svetlin Nakov + */ +public class CRLVerifier { + + /** + * Extracts the CRL distribution points from the certificate (if available) + * and checks the certificate revocation status against the CRLs coming from + * the distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. + * + * @param cert the certificate to be checked for revocation + * @throws CertificateVerificationException if the certificate is revoked + */ + public static void verifyCertificateCRLs(X509Certificate cert) + throws CertificateVerificationException { + try { + List crlDistPoints = getCrlDistributionPoints(cert); + for (String crlDP : crlDistPoints) { + X509CRL crl = downloadCRL(crlDP); + if (crl.isRevoked(cert)) { + throw new CertificateVerificationException( + "The certificate is revoked by CRL: " + crlDP); + } + } + } catch (Exception ex) { + if (ex instanceof CertificateVerificationException) { + throw (CertificateVerificationException) ex; + } else { + throw new CertificateVerificationException( + "Can not verify CRL for certificate: " + + cert.getSubjectX500Principal()); + } + } + } + + /** + * Downloads CRL from given URL. Supports http, https, ftp and ldap based URLs. + */ + private static X509CRL downloadCRL(String crlURL) throws IOException, + CertificateException, CRLException, + CertificateVerificationException, NamingException { + if (crlURL.startsWith("http://") || crlURL.startsWith("https://") + || crlURL.startsWith("ftp://")) { + X509CRL crl = downloadCRLFromWeb(crlURL); + return crl; + } else if (crlURL.startsWith("ldap://")) { + X509CRL crl = downloadCRLFromLDAP(crlURL); + return crl; + } else { + throw new CertificateVerificationException( + "Can not download CRL from certificate " + + "distribution point: " + crlURL); + } + } + + /** + * Downloads a CRL from given LDAP url, e.g. + * ldap://ldap.infonotary.com/dc=identity-ca,dc=infonotary,dc=com + */ + private static X509CRL downloadCRLFromLDAP(String ldapURL) + throws CertificateException, NamingException, CRLException, + CertificateVerificationException { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapURL); + + DirContext ctx = new InitialDirContext(env); + Attributes avals = ctx.getAttributes(""); + Attribute aval = avals.get("certificateRevocationList;binary"); + byte[] val = (byte[])aval.get(); + if ((val == null) || (val.length == 0)) { + throw new CertificateVerificationException( + "Can not download CRL from: " + ldapURL); + } else { + InputStream inStream = new ByteArrayInputStream(val); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL)cf.generateCRL(inStream); + return crl; + } + } + + /** + * Downloads a CRL from given HTTP/HTTPS/FTP URL, e.g. + * http://crl.infonotary.com/crl/identity-ca.crl + */ + private static X509CRL downloadCRLFromWeb(String crlURL) + throws MalformedURLException, IOException, CertificateException, + CRLException { + URL url = new URL(crlURL); + InputStream crlStream = url.openStream(); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(crlStream); + return crl; + } finally { + crlStream.close(); + } + } + + /** + * Extracts all CRL distribution point URLs from the "CRL Distribution Point" + * extension in a X.509 certificate. If CRL distribution point extension is + * unavailable, returns an empty list. + */ + public static List getCrlDistributionPoints( + X509Certificate cert) throws CertificateParsingException, IOException { + byte[] crldpExt = cert.getExtensionValue( + X509Extensions.CRLDistributionPoints.getId()); + if (crldpExt == null) { + List emptyList = new ArrayList(); + return emptyList; + } + ASN1InputStream oAsnInStream = new ASN1InputStream( + new ByteArrayInputStream(crldpExt)); + ASN1Primitive derObjCrlDP = oAsnInStream.readObject(); + DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP; + byte[] crldpExtOctets = dosCrlDP.getOctets(); + ASN1InputStream oAsnInStream2 = new ASN1InputStream( + new ByteArrayInputStream(crldpExtOctets)); + ASN1Primitive derObj2 = oAsnInStream2.readObject(); + CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2); + + oAsnInStream.close(); + oAsnInStream2.close(); + + List crlUrls = new ArrayList(); + for (DistributionPoint dp : distPoint.getDistributionPoints()) { + DistributionPointName dpn = dp.getDistributionPoint(); + // Look for URIs in fullName + if (dpn != null) { + if (dpn.getType() == DistributionPointName.FULL_NAME) { + GeneralName[] genNames = GeneralNames.getInstance( + dpn.getName()).getNames(); + // Look for an URI + for (int j = 0; j < genNames.length; j++) { + if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) { + String url = DERIA5String.getInstance( + genNames[j].getName()).getString(); + crlUrls.add(url); + } + } + } + } + } + return crlUrls; + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java index 2ab9ea95..86903b05 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -77,7 +77,7 @@ public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, // Check whether the certificate is revoked by the CRL // given in its CRL distribution point extension -// CRLVerifier.verifyCertificateCRLs(cert); //TODO + CRLVerifier.verifyCertificateCRLs(cert); // The chain is built and verified. Return it as a result return verifiedCertChain; diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index b9ec4b5e..19326de0 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -1,13 +1,41 @@ package com.acuity.iot.dsa.dslink.sys.cert; import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import java.time.format.ResolverStyle; +import java.util.Iterator; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; +import org.iot.dsa.node.DSIValue; import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSString; +import org.iot.dsa.node.DSValueType; +import org.iot.dsa.node.action.ActionInvocation; +import org.iot.dsa.node.action.ActionResult; +import org.iot.dsa.node.action.ActionSpec; +import org.iot.dsa.node.action.ActionSpec.ResultType; +import org.iot.dsa.node.action.ActionValues; +import org.iot.dsa.node.action.DSAbstractAction; +import org.iot.dsa.node.action.DSAction; +import org.iot.dsa.node.action.DSActionValues; import org.iot.dsa.security.DSPasswordAes128; +import org.iot.dsa.util.DSException; /** * Certificate management for the whole process. This is basically a stub for future @@ -28,6 +56,7 @@ public class SysCertManager extends DSNode { private static final String CERTFILE_TYPE = "Cert_File_Type"; private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; private static final String QUARANTINE = "Quarantine"; + private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; // Fields // ------ @@ -37,6 +66,7 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); + private DSInfo generateCSR = getInfo(GENERATE_CSR); private CertCollection localTruststore; private CertCollection quarantine; @@ -80,6 +110,24 @@ public void declareDefaults() { declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); declareDefault(QUARANTINE, new CertCollection()).setTransient(true); + declareDefault(GENERATE_CSR, getGenerateCSRAction()); + } + + private DSAbstractAction getGenerateCSRAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + return ((SysCertManager) info.getParent()).generateCSR(info); + } + }; + act.setResultType(ResultType.VALUES); + act.addValueResult("CSR", DSValueType.STRING); + return act; } private String getCertFilePass() { @@ -150,5 +198,44 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } + private ActionResult generateCSR(DSInfo actionInfo) { + KeyPairGenerator keyGen; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + DSException.throwRuntime(e); + return null; + } + keyGen.initialize(2048, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( + new X500Principal("CN=dslink-java-v2"), pair.getPublic()); + JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); + ContentSigner signer; + try { + signer = csBuilder.build(pair.getPrivate()); + } catch (OperatorCreationException e) { + DSException.throwRuntime(e); + return null; + } + PKCS10CertificationRequest csr = p10Builder.build(signer); + StringWriter str = new StringWriter(); + JcaPEMWriter pemWriter = new JcaPEMWriter(str); + try { + pemWriter.writeObject(csr); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } finally { + try { + pemWriter.close(); + str.close(); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } + } + return new DSActionValues(actionInfo.getAction()).addResult(DSString.valueOf(str)); + } } From cf5b5b421acd5de19b668e59422e472fb6f10866 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Wed, 25 Jul 2018 17:06:32 -0700 Subject: [PATCH 13/22] add code that generates a self-signed certificate (without keytool) --- .../dsa/dslink/sys/cert/SysCertManager.java | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 19326de0..435ae05f 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -3,15 +3,26 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.time.format.ResolverStyle; -import java.util.Iterator; +import java.util.Calendar; +import java.util.Date; import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; @@ -20,7 +31,6 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; -import org.iot.dsa.node.DSIValue; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; @@ -28,11 +38,8 @@ import org.iot.dsa.node.DSValueType; import org.iot.dsa.node.action.ActionInvocation; import org.iot.dsa.node.action.ActionResult; -import org.iot.dsa.node.action.ActionSpec; import org.iot.dsa.node.action.ActionSpec.ResultType; -import org.iot.dsa.node.action.ActionValues; import org.iot.dsa.node.action.DSAbstractAction; -import org.iot.dsa.node.action.DSAction; import org.iot.dsa.node.action.DSActionValues; import org.iot.dsa.security.DSPasswordAes128; import org.iot.dsa.util.DSException; @@ -66,7 +73,6 @@ public class SysCertManager extends DSNode { private DSInfo keystore = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); - private DSInfo generateCSR = getInfo(GENERATE_CSR); private CertCollection localTruststore; private CertCollection quarantine; @@ -122,7 +128,8 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - return ((SysCertManager) info.getParent()).generateCSR(info); + String csr = ((SysCertManager) info.getParent()).generateCSR(); + return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); } }; act.setResultType(ResultType.VALUES); @@ -198,7 +205,7 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } - private ActionResult generateCSR(DSInfo actionInfo) { + private static String generateCSR() { KeyPairGenerator keyGen; try { keyGen = KeyPairGenerator.getInstance("RSA"); @@ -209,7 +216,7 @@ private ActionResult generateCSR(DSInfo actionInfo) { keyGen.initialize(2048, new SecureRandom()); KeyPair pair = keyGen.generateKeyPair(); PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( - new X500Principal("CN=dslink-java-v2"), pair.getPublic()); + new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); ContentSigner signer; try { @@ -235,7 +242,56 @@ private ActionResult generateCSR(DSInfo actionInfo) { return null; } } - return new DSActionValues(actionInfo.getAction()).addResult(DSString.valueOf(str)); + return str.toString(); + } + + private static X509Certificate generateSelfSigned() { + KeyPairGenerator keyGen; + try { + keyGen = KeyPairGenerator.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + DSException.throwRuntime(e); + return null; + } + keyGen.initialize(2048, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + + Provider bcProvider = new BouncyCastleProvider(); + Security.addProvider(bcProvider); + + long now = System.currentTimeMillis(); + Date startDate = new Date(now); + + X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); + BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startDate); + calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity + Date endDate = calendar.getTime(); + + String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. + + try { + ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); + JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity + certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. + + return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); + } catch (OperatorCreationException e) { + DSException.throwRuntime(e); + return null; + } catch (CertIOException e) { + DSException.throwRuntime(e); + return null; + } catch (CertificateException e) { + DSException.throwRuntime(e); + return null; + } + + } } From 7035b29ff5e9bbe7d929e969a5dd101bdfd38b55 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Fri, 27 Jul 2018 13:17:41 -0700 Subject: [PATCH 14/22] Refactor to not require bouncycastle, add actions for importing certs --- .../dslink/sys/cert/CertificateVerifier.java | 4 +- .../iot/dsa/dslink/sys/cert/KeyToolUtil.java | 93 ++++++ .../dsa/dslink/sys/cert/SysCertManager.java | 282 ++++++++++-------- 3 files changed, 256 insertions(+), 123 deletions(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java index 86903b05..17785013 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertificateVerifier.java @@ -77,7 +77,7 @@ public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, // Check whether the certificate is revoked by the CRL // given in its CRL distribution point extension - CRLVerifier.verifyCertificateCRLs(cert); +// CRLVerifier.verifyCertificateCRLs(cert); // The chain is built and verified. Return it as a result return verifiedCertChain; @@ -144,7 +144,7 @@ private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, new PKIXBuilderParameters(trustAnchors, selector); // Disable CRL checks (this is done manually as additional step) - pkixParams.setRevocationEnabled(false); + pkixParams.setRevocationEnabled(true); // Specify a list of intermediate certificates CertStore intermediateCertStore = CertStore.getInstance("Collection", diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java new file mode 100644 index 00000000..6e0628b9 --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java @@ -0,0 +1,93 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.iot.dsa.logging.DSLogger; +import org.iot.dsa.time.DSTime; + +public class KeyToolUtil extends DSLogger { + + private static KeyToolUtil inst = new KeyToolUtil(); + private KeyToolUtil() { + + } + + private void executeCommand(String[] cmd) { + try { + ProcessBuilder builder = new ProcessBuilder(); + Process process = builder.command(cmd).start(); + process.waitFor(); + } catch (Exception e) { + error("", e); + } + } + + public static void generateSelfSigned(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-genkey", + "-keystore", keystore, + "-storepass", password, + "-keypass", password, + "-alias", "dsa", + "-keyalg", "RSA", + "-validity", "18000", + "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"" + }; + inst.executeCommand(cmd); + } + + public static String generateCSR(String keystore) throws IOException { + String filename = "dsa.csr"; + String[] cmd = new String[]{ + "keytool", + "-certreq", + "-keystore", keystore, + "-alias", "dsa", + "-keyalg", "RSA", + "-validity", "18000", + "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"", + "-file", filename + }; + inst.executeCommand(cmd); + return new String(Files.readAllBytes(Paths.get(filename))); + } + + public static void importCACert(String keystore, String certStr, String alias) throws IOException { + String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCACert")).toString(); + Files.write(Paths.get(filename), certStr.getBytes()); + String[] cmd = new String[]{ + "keytool", + "-import", + "-trustcacerts", + "-keystore", keystore, + "-alias", alias, + "-file", filename + }; + inst.executeCommand(cmd); + + new File(filename).delete(); + } + + public static void importPrimaryCert(String keystore, String certStr) throws IOException { + String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCert")).toString(); + Files.write(Paths.get(filename), certStr.getBytes()); + String[] cmd = new String[]{ + "keytool", + "-import", + "-trustcacerts", + "-keystore", keystore, + "-alias", "dsa", + "-file", filename + }; + inst.executeCommand(cmd); + + new File(filename).delete(); + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 435ae05f..1396acd1 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -16,20 +16,20 @@ import java.util.Calendar; import java.util.Date; import javax.security.auth.x500.X500Principal; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.cert.CertIOException; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; +//import org.bouncycastle.asn1.ASN1ObjectIdentifier; +//import org.bouncycastle.asn1.x500.X500Name; +//import org.bouncycastle.asn1.x509.BasicConstraints; +//import org.bouncycastle.cert.CertIOException; +//import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +//import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +//import org.bouncycastle.jce.provider.BouncyCastleProvider; +//import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +//import org.bouncycastle.operator.ContentSigner; +//import org.bouncycastle.operator.OperatorCreationException; +//import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +//import org.bouncycastle.pkcs.PKCS10CertificationRequest; +//import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +//import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap; @@ -64,6 +64,8 @@ public class SysCertManager extends DSNode { private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; private static final String QUARANTINE = "Quarantine"; private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; + private static final String IMPORT_CA_CERT = "Import CA Certificate"; + private static final String IMPORT_PRIMARY_CERT = "Import Primary Certificate"; // Fields // ------ @@ -117,6 +119,8 @@ public void declareDefaults() { declareDefault(LOCAL_TRUSTSTORE, new CertCollection()); declareDefault(QUARANTINE, new CertCollection()).setTransient(true); declareDefault(GENERATE_CSR, getGenerateCSRAction()); + declareDefault(IMPORT_CA_CERT, getImportCACertAction()); + declareDefault(IMPORT_PRIMARY_CERT, getImportPrimaryCertAction()); } private DSAbstractAction getGenerateCSRAction() { @@ -128,7 +132,13 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - String csr = ((SysCertManager) info.getParent()).generateCSR(); + String csr; + try { + csr = KeyToolUtil.generateCSR(keystore.getElement().toString()); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); } }; @@ -136,6 +146,54 @@ public ActionResult invoke(DSInfo info, ActionInvocation invocation) { act.addValueResult("CSR", DSValueType.STRING); return act; } + + private DSAbstractAction getImportCACertAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + DSMap parameters = invocation.getParameters(); + String alias = parameters.getString("Alias"); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importCACert(keystore.getElement().toString(), certStr, alias); + } catch (IOException e) { + DSException.throwRuntime(e); + } + return null; + } + }; + act.addParameter("Alias", DSValueType.STRING, null); + act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); + return act; + } + + private DSAbstractAction getImportPrimaryCertAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + DSMap parameters = invocation.getParameters(); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importPrimaryCert(keystore.getElement().toString(), certStr); + } catch (IOException e) { + DSException.throwRuntime(e); + } + return null; + } + }; + act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); + return act; + } private String getCertFilePass() { DSPasswordAes128 pass = (DSPasswordAes128) keystorePass.getObject(); @@ -146,25 +204,7 @@ private String getCertFilePass() { * Executes the java keytool to generate a new self signed cert. */ private void keytoolGenkey() { - try { - String pass = getCertFilePass(); - String[] cmd = new String[]{ - "keytool", - "-genkey", - "-keystore", keystore.getElement().toString(), - "-storepass", pass, - "-keypass", pass, - "-alias", "dsa", - "-keyalg", "RSA", - "-validity", "18000", - "-dname", "\"CN=dslink-java-v2, O=DSA, C=US\"" - }; - ProcessBuilder builder = new ProcessBuilder(); - Process process = builder.command(cmd).start(); - process.waitFor(); - } catch (Exception x) { - error(getPath(), x); - } + KeyToolUtil.generateSelfSigned(keystore.getElement().toString(), getCertFilePass()); } @Override @@ -205,93 +245,93 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } - private static String generateCSR() { - KeyPairGenerator keyGen; - try { - keyGen = KeyPairGenerator.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - DSException.throwRuntime(e); - return null; - } - keyGen.initialize(2048, new SecureRandom()); - KeyPair pair = keyGen.generateKeyPair(); - PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( - new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); - JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); - ContentSigner signer; - try { - signer = csBuilder.build(pair.getPrivate()); - } catch (OperatorCreationException e) { - DSException.throwRuntime(e); - return null; - } - PKCS10CertificationRequest csr = p10Builder.build(signer); - StringWriter str = new StringWriter(); - JcaPEMWriter pemWriter = new JcaPEMWriter(str); - try { - pemWriter.writeObject(csr); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } finally { - try { - pemWriter.close(); - str.close(); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } - } - return str.toString(); - } +// private static String generateCSR() { +// KeyPairGenerator keyGen; +// try { +// keyGen = KeyPairGenerator.getInstance("RSA"); +// } catch (NoSuchAlgorithmException e) { +// DSException.throwRuntime(e); +// return null; +// } +// keyGen.initialize(2048, new SecureRandom()); +// KeyPair pair = keyGen.generateKeyPair(); +// PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( +// new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); +// JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); +// ContentSigner signer; +// try { +// signer = csBuilder.build(pair.getPrivate()); +// } catch (OperatorCreationException e) { +// DSException.throwRuntime(e); +// return null; +// } +// PKCS10CertificationRequest csr = p10Builder.build(signer); +// StringWriter str = new StringWriter(); +// JcaPEMWriter pemWriter = new JcaPEMWriter(str); +// try { +// pemWriter.writeObject(csr); +// } catch (IOException e) { +// DSException.throwRuntime(e); +// return null; +// } finally { +// try { +// pemWriter.close(); +// str.close(); +// } catch (IOException e) { +// DSException.throwRuntime(e); +// return null; +// } +// } +// return str.toString(); +// } - private static X509Certificate generateSelfSigned() { - KeyPairGenerator keyGen; - try { - keyGen = KeyPairGenerator.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - DSException.throwRuntime(e); - return null; - } - keyGen.initialize(2048, new SecureRandom()); - KeyPair pair = keyGen.generateKeyPair(); - - Provider bcProvider = new BouncyCastleProvider(); - Security.addProvider(bcProvider); - - long now = System.currentTimeMillis(); - Date startDate = new Date(now); - - X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); - BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(startDate); - calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity - Date endDate = calendar.getTime(); - - String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. - - try { - ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); - JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); - - BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity - certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. - - return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); - } catch (OperatorCreationException e) { - DSException.throwRuntime(e); - return null; - } catch (CertIOException e) { - DSException.throwRuntime(e); - return null; - } catch (CertificateException e) { - DSException.throwRuntime(e); - return null; - } - - - } +// private static X509Certificate generateSelfSigned() { +// KeyPairGenerator keyGen; +// try { +// keyGen = KeyPairGenerator.getInstance("RSA"); +// } catch (NoSuchAlgorithmException e) { +// DSException.throwRuntime(e); +// return null; +// } +// keyGen.initialize(2048, new SecureRandom()); +// KeyPair pair = keyGen.generateKeyPair(); +// +// Provider bcProvider = new BouncyCastleProvider(); +// Security.addProvider(bcProvider); +// +// long now = System.currentTimeMillis(); +// Date startDate = new Date(now); +// +// X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); +// BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number +// +// Calendar calendar = Calendar.getInstance(); +// calendar.setTime(startDate); +// calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity +// Date endDate = calendar.getTime(); +// +// String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. +// +// try { +// ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); +// JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); +// +// BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity +// certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. +// +// return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); +// } catch (OperatorCreationException e) { +// DSException.throwRuntime(e); +// return null; +// } catch (CertIOException e) { +// DSException.throwRuntime(e); +// return null; +// } catch (CertificateException e) { +// DSException.throwRuntime(e); +// return null; +// } +// +// +// } } From 7b9052363848d5b38ed8bb67f8384944f9525c29 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Fri, 27 Jul 2018 14:59:33 -0700 Subject: [PATCH 15/22] change certificate name to contain timestamp instead of hashcode --- .../com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java index 05b9781a..ec85c04a 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java @@ -6,6 +6,7 @@ import java.util.Base64.Encoder; import org.iot.dsa.node.DSIObject; import org.iot.dsa.node.DSNode; +import org.iot.dsa.time.DSTime; public class CertCollection extends DSNode { @@ -31,7 +32,7 @@ public boolean containsCertificate(X509Certificate cert) { } public static String certToName(X509Certificate cert) { - return cert.getIssuerX500Principal().getName() + "-" + Integer.toHexString(cert.hashCode()); + return DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder(cert.getIssuerX500Principal().getName())).toString(); } public static String encodeCertificate(X509Certificate cert) throws CertificateEncodingException { From 69f729569e76a0814d239c8c27992ff9baea982c Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Mon, 30 Jul 2018 16:29:20 -0700 Subject: [PATCH 16/22] fix bugs, add more keytool actions --- .../iot/dsa/dslink/sys/cert/KeyToolUtil.java | 45 +++++- .../dsa/dslink/sys/cert/SysCertManager.java | 142 +++++++++++++----- 2 files changed, 145 insertions(+), 42 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java index 6e0628b9..f5d78d23 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java @@ -2,8 +2,8 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Paths; @@ -17,13 +17,22 @@ private KeyToolUtil() { } - private void executeCommand(String[] cmd) { + private String executeCommand(String[] cmd) { try { ProcessBuilder builder = new ProcessBuilder(); Process process = builder.command(cmd).start(); process.waitFor(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line = null; + while ( (line = reader.readLine()) != null) { + sb.append(line); + sb.append(System.getProperty("line.separator")); + } + return sb.toString(); } catch (Exception e) { error("", e); + return ""; } } @@ -42,12 +51,13 @@ public static void generateSelfSigned(String keystore, String password) { inst.executeCommand(cmd); } - public static String generateCSR(String keystore) throws IOException { + public static String generateCSR(String keystore, String password) throws IOException { String filename = "dsa.csr"; String[] cmd = new String[]{ "keytool", "-certreq", "-keystore", keystore, + "-storepass", password, "-alias", "dsa", "-keyalg", "RSA", "-validity", "18000", @@ -58,7 +68,7 @@ public static String generateCSR(String keystore) throws IOException { return new String(Files.readAllBytes(Paths.get(filename))); } - public static void importCACert(String keystore, String certStr, String alias) throws IOException { + public static void importCACert(String keystore, String certStr, String alias, String password) throws IOException { String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCACert")).toString(); Files.write(Paths.get(filename), certStr.getBytes()); String[] cmd = new String[]{ @@ -66,6 +76,7 @@ public static void importCACert(String keystore, String certStr, String alias) t "-import", "-trustcacerts", "-keystore", keystore, + "-storepass", password, "-alias", alias, "-file", filename }; @@ -74,7 +85,7 @@ public static void importCACert(String keystore, String certStr, String alias) t new File(filename).delete(); } - public static void importPrimaryCert(String keystore, String certStr) throws IOException { + public static void importPrimaryCert(String keystore, String certStr, String password) throws IOException { String filename = DSTime.encodeForFiles(DSTime.getCalendar(System.currentTimeMillis()), new StringBuilder("tempCert")).toString(); Files.write(Paths.get(filename), certStr.getBytes()); String[] cmd = new String[]{ @@ -82,6 +93,7 @@ public static void importPrimaryCert(String keystore, String certStr) throws IOE "-import", "-trustcacerts", "-keystore", keystore, + "-storepass", password, "-alias", "dsa", "-file", filename }; @@ -89,5 +101,28 @@ public static void importPrimaryCert(String keystore, String certStr) throws IOE new File(filename).delete(); } + + public static String getEntry(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-list", + "-v", + "-keystore", keystore, + "-storepass", password, + "-alias", "dsa", + }; + return inst.executeCommand(cmd); + } + + public static void deleteEntry(String keystore, String password) { + String[] cmd = new String[]{ + "keytool", + "-delete", + "-keystore", keystore, + "-storepass", password, + "-alias", "dsa", + }; + inst.executeCommand(cmd); + } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 1396acd1..80202eef 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -2,20 +2,8 @@ import java.io.File; import java.io.IOException; -import java.io.StringWriter; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.Security; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.Calendar; -import java.util.Date; -import javax.security.auth.x500.X500Principal; //import org.bouncycastle.asn1.ASN1ObjectIdentifier; //import org.bouncycastle.asn1.x500.X500Name; //import org.bouncycastle.asn1.x509.BasicConstraints; @@ -50,6 +38,7 @@ * as accepts self signed (anonymous) certs from the broker. * * @author Aaron Hansen + * @author Daniel Shapiro */ public class SysCertManager extends DSNode { @@ -61,11 +50,14 @@ public class SysCertManager extends DSNode { private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; - private static final String LOCAL_TRUSTSTORE = "Local_Truststore"; + private static final String LOCAL_TRUSTSTORE = "Local Truststore"; private static final String QUARANTINE = "Quarantine"; - private static final String GENERATE_CSR = "Generate_Certificate_Signing_Request"; + private static final String GENERATE_CSR = "Generate Certificate Signing Request"; private static final String IMPORT_CA_CERT = "Import CA Certificate"; private static final String IMPORT_PRIMARY_CERT = "Import Primary Certificate"; + private static final String GENERATE_SELF_SIGNED = "Generate Self-Signed Certificate"; + private static final String DELETE_KS_ENTRY = "Delete Keystore Entry"; + private static final String GET_KS_ENTRY = "Get Keystore Entry"; // Fields // ------ @@ -121,6 +113,9 @@ public void declareDefaults() { declareDefault(GENERATE_CSR, getGenerateCSRAction()); declareDefault(IMPORT_CA_CERT, getImportCACertAction()); declareDefault(IMPORT_PRIMARY_CERT, getImportPrimaryCertAction()); + declareDefault(GENERATE_SELF_SIGNED, getGenerateSelfSignedAction()); + declareDefault(GET_KS_ENTRY, getGetKSEntryAction()); + declareDefault(DELETE_KS_ENTRY, getDeleteKSEntryAction()); } private DSAbstractAction getGenerateCSRAction() { @@ -132,21 +127,24 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { - String csr; - try { - csr = KeyToolUtil.generateCSR(keystore.getElement().toString()); - } catch (IOException e) { - DSException.throwRuntime(e); - return null; - } - return new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)); + String csr = ((SysCertManager) info.getParent()).generateCSR(); + return csr != null ? new DSActionValues(info.getAction()).addResult(DSString.valueOf(csr)) : null; } }; act.setResultType(ResultType.VALUES); - act.addValueResult("CSR", DSValueType.STRING); + act.addValueResult("CSR", DSValueType.STRING).setEditor("textarea"); return act; } + private String generateCSR() { + try { + return KeyToolUtil.generateCSR(getKeystorePath(), getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + return null; + } + } + private DSAbstractAction getImportCACertAction() { DSAbstractAction act = new DSAbstractAction() { @@ -157,13 +155,7 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { DSMap parameters = invocation.getParameters(); - String alias = parameters.getString("Alias"); - String certStr = parameters.getString("Certificate"); - try { - KeyToolUtil.importCACert(keystore.getElement().toString(), certStr, alias); - } catch (IOException e) { - DSException.throwRuntime(e); - } + ((SysCertManager) info.getParent()).importCACert(parameters); return null; } }; @@ -172,6 +164,16 @@ public ActionResult invoke(DSInfo info, ActionInvocation invocation) { return act; } + private void importCACert(DSMap parameters) { + String alias = parameters.getString("Alias"); + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importCACert(getKeystorePath(), certStr, alias, getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + } + } + private DSAbstractAction getImportPrimaryCertAction() { DSAbstractAction act = new DSAbstractAction() { @@ -182,29 +184,95 @@ public void prepareParameter(DSInfo info, DSMap parameter) { @Override public ActionResult invoke(DSInfo info, ActionInvocation invocation) { DSMap parameters = invocation.getParameters(); - String certStr = parameters.getString("Certificate"); - try { - KeyToolUtil.importPrimaryCert(keystore.getElement().toString(), certStr); - } catch (IOException e) { - DSException.throwRuntime(e); - } + ((SysCertManager) info.getParent()).importPrimaryCert(parameters); return null; } }; act.addParameter("Certificate", DSValueType.STRING, null).setEditor("textarea"); return act; } + + private void importPrimaryCert(DSMap parameters) { + String certStr = parameters.getString("Certificate"); + try { + KeyToolUtil.importPrimaryCert(getKeystorePath(), certStr, getCertFilePass()); + } catch (IOException e) { + DSException.throwRuntime(e); + } + } + + private DSAbstractAction getGenerateSelfSignedAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + ((SysCertManager) info.getParent()).keytoolGenkey(); + return null; + } + }; + return act; + } + + private DSAbstractAction getGetKSEntryAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + String result = ((SysCertManager) info.getParent()).getKSEntry(); + return new DSActionValues(info.getAction()).addResult(DSString.valueOf(result)); + } + }; + act.setResultType(ResultType.VALUES); + act.addValueResult("Entry", DSValueType.STRING).setEditor("textarea"); + return act; + } + + private String getKSEntry() { + return KeyToolUtil.getEntry(getKeystorePath(), getCertFilePass()); + } + + private DSAbstractAction getDeleteKSEntryAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + ((SysCertManager) info.getParent()).deleteKSEntry(); + return null; + } + }; + return act; + } + + private void deleteKSEntry() { + KeyToolUtil.deleteEntry(getKeystorePath(), getCertFilePass()); + } private String getCertFilePass() { DSPasswordAes128 pass = (DSPasswordAes128) keystorePass.getObject(); return pass.decode(); } + + private String getKeystorePath() { + return keystore.getElement().toString(); + } /** * Executes the java keytool to generate a new self signed cert. */ private void keytoolGenkey() { - KeyToolUtil.generateSelfSigned(keystore.getElement().toString(), getCertFilePass()); + KeyToolUtil.generateSelfSigned(getKeystorePath(), getCertFilePass()); } @Override From 54066f38e736ae40073888df9e54063c93c84b57 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 7 Aug 2018 16:15:12 -0700 Subject: [PATCH 17/22] add option to enable or disable hostname verification --- .../dslink/websocket/WsBinaryTransport.java | 7 +++ .../dsa/dslink/websocket/WsTextTransport.java | 7 +++ .../dsa/dslink/sys/cert/SysCertManager.java | 45 +++++++++++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java index 07623754..14fdd6b5 100644 --- a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java +++ b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java @@ -1,5 +1,6 @@ package org.iot.dsa.dslink.websocket; +import com.acuity.iot.dsa.dslink.sys.cert.SysCertManager; import com.acuity.iot.dsa.dslink.transport.BufferedBinaryTransport; import com.acuity.iot.dsa.dslink.transport.DSTransport; import java.io.IOException; @@ -7,6 +8,9 @@ import java.nio.ByteBuffer; import javax.websocket.*; import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.ClientProperties; +import org.glassfish.tyrus.client.SslContextConfigurator; +import org.glassfish.tyrus.client.SslEngineConfigurator; import org.iot.dsa.util.DSException; /** @@ -97,6 +101,9 @@ public DSTransport open() { } client.setDefaultMaxBinaryMessageBufferSize(64 * 1024); client.setDefaultMaxTextMessageBufferSize(64 * 1024); + SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); + sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); client.connectToServer(this, new URI(getConnectionUrl())); debug(debug() ? "Transport open" : null); } catch (Exception x) { diff --git a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java index 937c04d8..2a7cff55 100644 --- a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java +++ b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java @@ -2,6 +2,7 @@ import com.acuity.iot.dsa.dslink.io.DSCharBuffer; import com.acuity.iot.dsa.dslink.io.DSIoException; +import com.acuity.iot.dsa.dslink.sys.cert.SysCertManager; import com.acuity.iot.dsa.dslink.transport.DSTextTransport; import com.acuity.iot.dsa.dslink.transport.DSTransport; import java.io.IOException; @@ -18,6 +19,9 @@ import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.ClientProperties; +import org.glassfish.tyrus.client.SslContextConfigurator; +import org.glassfish.tyrus.client.SslEngineConfigurator; import org.iot.dsa.util.DSException; /** @@ -149,6 +153,9 @@ public DSTransport open() { } client.setDefaultMaxBinaryMessageBufferSize(64 * 1024); client.setDefaultMaxTextMessageBufferSize(64 * 1024); + SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); + sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); client.connectToServer(this, new URI(getConnectionUrl())); } catch (Exception x) { DSException.throwRuntime(x); diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 80202eef..46105610 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -4,6 +4,9 @@ import java.io.IOException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; //import org.bouncycastle.asn1.ASN1ObjectIdentifier; //import org.bouncycastle.asn1.x500.X500Name; //import org.bouncycastle.asn1.x509.BasicConstraints; @@ -47,6 +50,7 @@ public class SysCertManager extends DSNode { private static final String ALLOW_CLIENTS = "Allow_Anonymous_Clients"; private static final String ALLOW_SERVERS = "Allow_Anonymous_Servers"; + private static final String VERIFY_HOSTNAMES = "Enable Hostname Verification"; private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; @@ -64,11 +68,27 @@ public class SysCertManager extends DSNode { private DSInfo allowClients = getInfo(ALLOW_CLIENTS); private DSInfo allowServers = getInfo(ALLOW_SERVERS); - private DSInfo keystore = getInfo(CERTFILE); + private DSInfo verifyHostnames = getInfo(VERIFY_HOSTNAMES); + private DSInfo keystorePath = getInfo(CERTFILE); private DSInfo keystorePass = getInfo(CERTFILE_PASS); private DSInfo keystoreType = getInfo(CERTFILE_TYPE); private CertCollection localTruststore; private CertCollection quarantine; + private static SysCertManager inst; + + private static HostnameVerifier oldHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); + private HostnameVerifier hostnameVerifier = new SysHostnameVerifier(); + + public SysCertManager() { + } + + public static SysCertManager getInstance() { + return inst; + } + + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } private CertCollection getLocalTruststore() { if (localTruststore == null) { @@ -100,11 +120,16 @@ public boolean allowAnonymousClients() { public boolean allowAnonymousServers() { return allowServers.getElement().toBoolean(); } + + public boolean hostnameVerificationEnabled() { + return verifyHostnames.getElement().toBoolean(); + } @Override public void declareDefaults() { declareDefault(ALLOW_CLIENTS, DSBool.FALSE); declareDefault(ALLOW_SERVERS, DSBool.TRUE); + declareDefault(VERIFY_HOSTNAMES, DSBool.TRUE); declareDefault(CERTFILE, DSString.valueOf("dslink.jks")); declareDefault(CERTFILE_TYPE, DSString.valueOf("JKS")); declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); @@ -265,7 +290,7 @@ private String getCertFilePass() { } private String getKeystorePath() { - return keystore.getElement().toString(); + return keystorePath.getElement().toString(); } /** @@ -277,8 +302,9 @@ private void keytoolGenkey() { @Override public void onStarted() { + inst = this; AnonymousTrustFactory.init(this); - String keystore = this.keystore.getElement().toString(); + String keystore = this.keystorePath.getElement().toString(); File f = new File(keystore); if (!f.exists()) { keytoolGenkey(); @@ -291,6 +317,7 @@ public void onStarted() { } catch (Exception x) { error(getParent(), x); } + HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); } public boolean isInTrustStore(X509Certificate cert) { @@ -313,6 +340,18 @@ public void allow(DSInfo certInfo) { getLocalTruststore().addCertificate(name, certStr); } + private class SysHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + if (hostnameVerificationEnabled()) { + //TODO implement whitelist + return oldHostnameVerifier.verify(hostname, session); + } else { + return true; + } + } + } + // private static String generateCSR() { // KeyPairGenerator keyGen; // try { From 529101d999ce24842193f2aa9a732843eae9c985 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Wed, 8 Aug 2018 17:58:51 -0700 Subject: [PATCH 18/22] only configure WebSocket hostname verifier for wss urls --- .../iot/dsa/dslink/websocket/WsBinaryTransport.java | 11 +++++++---- .../org/iot/dsa/dslink/websocket/WsTextTransport.java | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java index 14fdd6b5..3b1f1e2b 100644 --- a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java +++ b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsBinaryTransport.java @@ -101,10 +101,13 @@ public DSTransport open() { } client.setDefaultMaxBinaryMessageBufferSize(64 * 1024); client.setDefaultMaxTextMessageBufferSize(64 * 1024); - SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); - sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); - client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); - client.connectToServer(this, new URI(getConnectionUrl())); + URI connUri = new URI(getConnectionUrl()); + if ("wss".equalsIgnoreCase(connUri.getScheme())) { + SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); + sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); + } + client.connectToServer(this, connUri); debug(debug() ? "Transport open" : null); } catch (Exception x) { DSException.throwRuntime(x); diff --git a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java index 1cb0bbae..f3d016c2 100644 --- a/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java +++ b/dslink-v2-websocket/src/main/java/org/iot/dsa/dslink/websocket/WsTextTransport.java @@ -153,10 +153,13 @@ public DSTransport open() { } client.setDefaultMaxBinaryMessageBufferSize(64 * 1024); client.setDefaultMaxTextMessageBufferSize(64 * 1024); - SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); - sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); - client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); - client.connectToServer(this, new URI(getConnectionUrl())); + URI connUri = new URI(getConnectionUrl()); + if ("wss".equalsIgnoreCase(connUri.getScheme())) { + SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(new SslContextConfigurator()); + sslEngineConfigurator.setHostnameVerifier(SysCertManager.getInstance().getHostnameVerifier()); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); + } + client.connectToServer(this, connUri); } catch (Exception x) { DSException.throwRuntime(x); } From 9936ace76939eb1f2095d9eb6cc6456975cb9077 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Fri, 10 Aug 2018 17:39:21 -0700 Subject: [PATCH 19/22] implement hostname whitelist and blacklist --- .../dslink/sys/cert/HostnameWhitelist.java | 74 +++++++++++++++++++ .../dsa/dslink/sys/cert/SysCertManager.java | 24 +++++- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java new file mode 100644 index 00000000..7e37c15c --- /dev/null +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java @@ -0,0 +1,74 @@ +package com.acuity.iot.dsa.dslink.sys.cert; + +import org.iot.dsa.node.DSBool; +import org.iot.dsa.node.DSElement; +import org.iot.dsa.node.DSIValue; +import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSJavaEnum; +import org.iot.dsa.node.DSMap; +import org.iot.dsa.node.DSNode; +import org.iot.dsa.node.DSValueType; +import org.iot.dsa.node.action.ActionInvocation; +import org.iot.dsa.node.action.ActionResult; +import org.iot.dsa.node.action.DSAbstractAction; + +public class HostnameWhitelist extends DSNode { + + public static enum WhitelistValue { + ALLOWED, FORBIDDEN; + } + + private static final String ENABLED = "Enabled"; + private static final String ADD_HOSTNAME = "Add Hostname"; + + private DSInfo enabled = getInfo(ENABLED); + + @Override + protected void declareDefaults() { + super.declareDefaults(); + declareDefault(ENABLED, DSBool.FALSE); + declareDefault(ADD_HOSTNAME, getAddHostnameAction()); + } + + public boolean isEnabled() { + return enabled.getElement().toBoolean(); + } + + private DSAbstractAction getAddHostnameAction() { + DSAbstractAction act = new DSAbstractAction() { + + @Override + public void prepareParameter(DSInfo info, DSMap parameter) { + } + + @Override + public ActionResult invoke(DSInfo info, ActionInvocation invocation) { + ((HostnameWhitelist) info.getParent()).addHostname(invocation.getParameters()); + return null; + } + }; + act.addParameter("Hostname", DSValueType.STRING, null); + act.addParameter("Status", DSJavaEnum.valueOf(WhitelistValue.ALLOWED), "Whether this hostname should be whitelisted or blacklisted"); + return act; + } + + private void addHostname(DSMap parameters) { + String hostname = parameters.getString("Hostname"); + DSElement status = parameters.get("Status"); + put(hostname, status).setRemovable(true); + } + + public WhitelistValue checkHostname(String hostname) { + DSIValue value = getValue(hostname); + String str = null; + if (value != null) { + str = value.toElement().toString(); + } + try { + return WhitelistValue.valueOf(str); + } catch (Exception e) { + return null; + } + } + +} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 46105610..30c7c730 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -23,6 +23,7 @@ //import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; +import org.iot.dsa.node.DSList; import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSString; @@ -34,6 +35,7 @@ import org.iot.dsa.node.action.DSActionValues; import org.iot.dsa.security.DSPasswordAes128; import org.iot.dsa.util.DSException; +import com.acuity.iot.dsa.dslink.sys.cert.HostnameWhitelist.WhitelistValue; /** * Certificate management for the whole process. This is basically a stub for future @@ -51,6 +53,7 @@ public class SysCertManager extends DSNode { private static final String ALLOW_CLIENTS = "Allow_Anonymous_Clients"; private static final String ALLOW_SERVERS = "Allow_Anonymous_Servers"; private static final String VERIFY_HOSTNAMES = "Enable Hostname Verification"; + private static final String HOSTNAME_WHITELIST = "Hostname Whitelist"; private static final String CERTFILE = "Cert_File"; private static final String CERTFILE_PASS = "Cert_File_Pass"; private static final String CERTFILE_TYPE = "Cert_File_Type"; @@ -74,6 +77,7 @@ public class SysCertManager extends DSNode { private DSInfo keystoreType = getInfo(CERTFILE_TYPE); private CertCollection localTruststore; private CertCollection quarantine; + private HostnameWhitelist whitelist; private static SysCertManager inst; private static HostnameVerifier oldHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); @@ -104,6 +108,13 @@ private CertCollection getQuarantine() { return quarantine; } + private HostnameWhitelist getHostnameWhitelist() { + if (whitelist == null) { + whitelist = (HostnameWhitelist) getInfo(HOSTNAME_WHITELIST).getObject(); + } + return whitelist; + } + // Methods // ------- @@ -130,6 +141,7 @@ public void declareDefaults() { declareDefault(ALLOW_CLIENTS, DSBool.FALSE); declareDefault(ALLOW_SERVERS, DSBool.TRUE); declareDefault(VERIFY_HOSTNAMES, DSBool.TRUE); + declareDefault(HOSTNAME_WHITELIST, new HostnameWhitelist()); declareDefault(CERTFILE, DSString.valueOf("dslink.jks")); declareDefault(CERTFILE_TYPE, DSString.valueOf("JKS")); declareDefault(CERTFILE_PASS, DSPasswordAes128.valueOf("dsarocks")); @@ -343,8 +355,18 @@ public void allow(DSInfo certInfo) { private class SysHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { + if (getHostnameWhitelist().isEnabled()) { + WhitelistValue wlval = getHostnameWhitelist().checkHostname(hostname); + if (wlval != null) { + switch (wlval) { + case ALLOWED: + return true; + case FORBIDDEN: + return false; + } + } + } if (hostnameVerificationEnabled()) { - //TODO implement whitelist return oldHostnameVerifier.verify(hostname, session); } else { return true; From 1f911da5bd3d27c6d2f2cd0989ac4a5a51fb8938 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Mon, 13 Aug 2018 16:15:18 -0700 Subject: [PATCH 20/22] implement removal of hostname from whitelist via enum option --- .../dslink/sys/cert/HostnameWhitelist.java | 20 ++++++++++++++++--- .../dsa/dslink/sys/cert/SysCertManager.java | 1 - 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java index 7e37c15c..3a1f5457 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java @@ -1,7 +1,6 @@ package com.acuity.iot.dsa.dslink.sys.cert; import org.iot.dsa.node.DSBool; -import org.iot.dsa.node.DSElement; import org.iot.dsa.node.DSIValue; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSJavaEnum; @@ -18,6 +17,10 @@ public static enum WhitelistValue { ALLOWED, FORBIDDEN; } + public static enum WhitelistOption { + ALLOWED, FORBIDDEN, REMOVE; + } + private static final String ENABLED = "Enabled"; private static final String ADD_HOSTNAME = "Add Hostname"; @@ -54,8 +57,9 @@ public ActionResult invoke(DSInfo info, ActionInvocation invocation) { private void addHostname(DSMap parameters) { String hostname = parameters.getString("Hostname"); - DSElement status = parameters.get("Status"); - put(hostname, status).setRemovable(true); + String statusStr = parameters.getString("Status"); + WhitelistOption option = WhitelistOption.valueOf(statusStr); + put(hostname, DSJavaEnum.valueOf(option)); } public WhitelistValue checkHostname(String hostname) { @@ -70,5 +74,15 @@ public WhitelistValue checkHostname(String hostname) { return null; } } + + protected void onChildChanged(DSInfo info) { + if (info.isValue()) { + String val = info.getValue().toElement().toString(); + if (WhitelistOption.REMOVE.name().equals(val)) { + remove(info); + } + } + } + } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 30c7c730..36b2bf75 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -23,7 +23,6 @@ //import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; -import org.iot.dsa.node.DSList; import org.iot.dsa.node.DSMap; import org.iot.dsa.node.DSNode; import org.iot.dsa.node.DSString; From 67b28b6cf5ec7b605b3bdfc73919e36c390930d0 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 14 Aug 2018 13:35:35 -0700 Subject: [PATCH 21/22] cleanup --- dslink-core/.gitignore | 1 + dslink-v2/build.gradle | 2 - .../dslink/sys/backup/SysBackupService.java | 4 + .../sys/cert/AnonymousTrustFactory.java | 1 + .../iot/dsa/dslink/sys/cert/CRLVerifier.java | 189 ------------------ .../dsa/dslink/sys/cert/CertCollection.java | 3 + .../iot/dsa/dslink/sys/cert/CertNode.java | 3 + .../dslink/sys/cert/HostnameWhitelist.java | 3 + .../iot/dsa/dslink/sys/cert/KeyToolUtil.java | 4 + .../dsa/dslink/sys/cert/SysCertManager.java | 101 +--------- .../dsa/dslink/sys/logging/LoggerNode.java | 3 + .../dslink/sys/logging/StreamableLogNode.java | 3 + .../dsa/dslink/sys/logging/SysLogService.java | 3 + 13 files changed, 34 insertions(+), 286 deletions(-) create mode 100644 dslink-core/.gitignore delete mode 100644 dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java diff --git a/dslink-core/.gitignore b/dslink-core/.gitignore new file mode 100644 index 00000000..ae3c1726 --- /dev/null +++ b/dslink-core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/dslink-v2/build.gradle b/dslink-v2/build.gradle index f40a6d72..07a12bc1 100644 --- a/dslink-v2/build.gradle +++ b/dslink-v2/build.gradle @@ -4,8 +4,6 @@ artifacts { } dependencies { - compile 'org.bouncycastle:bcprov-jdk15on:+' - compile 'org.bouncycastle:bcpkix-jdk15on:+' testImplementation 'junit:junit:[4.12,)' } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/backup/SysBackupService.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/backup/SysBackupService.java index 8789af35..df41eb68 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/backup/SysBackupService.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/backup/SysBackupService.java @@ -25,6 +25,10 @@ import org.iot.dsa.node.action.DSAction; import org.iot.dsa.time.DSTime; +/** + * @author Daniel Shapiro + * @author Aaron Hansen + */ public class SysBackupService extends DSNode implements Runnable { static final String ENABLED = "Enabled"; diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java index 89811d2b..152c90d0 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/AnonymousTrustFactory.java @@ -21,6 +21,7 @@ * falls back to the default Java trust manager. * * @author Aaron Hansen + * @author Daniel Shapiro */ public class AnonymousTrustFactory extends TrustManagerFactorySpi { diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java deleted file mode 100644 index 97db1ca5..00000000 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CRLVerifier.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.acuity.iot.dsa.dslink.sys.cert; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.cert.CRLException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509CRL; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.DERIA5String; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.x509.CRLDistPoint; -import org.bouncycastle.asn1.x509.DistributionPoint; -import org.bouncycastle.asn1.x509.DistributionPointName; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.asn1.x509.X509Extensions; - -/** - * Class that verifies CRLs for given X509 certificate. Extracts the CRL - * distribution points from the certificate (if available) and checks the - * certificate revocation status against the CRLs coming from the - * distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. - * - * @author Svetlin Nakov - */ -public class CRLVerifier { - - /** - * Extracts the CRL distribution points from the certificate (if available) - * and checks the certificate revocation status against the CRLs coming from - * the distribution points. Supports HTTP, HTTPS, FTP and LDAP based URLs. - * - * @param cert the certificate to be checked for revocation - * @throws CertificateVerificationException if the certificate is revoked - */ - public static void verifyCertificateCRLs(X509Certificate cert) - throws CertificateVerificationException { - try { - List crlDistPoints = getCrlDistributionPoints(cert); - for (String crlDP : crlDistPoints) { - X509CRL crl = downloadCRL(crlDP); - if (crl.isRevoked(cert)) { - throw new CertificateVerificationException( - "The certificate is revoked by CRL: " + crlDP); - } - } - } catch (Exception ex) { - if (ex instanceof CertificateVerificationException) { - throw (CertificateVerificationException) ex; - } else { - throw new CertificateVerificationException( - "Can not verify CRL for certificate: " + - cert.getSubjectX500Principal()); - } - } - } - - /** - * Downloads CRL from given URL. Supports http, https, ftp and ldap based URLs. - */ - private static X509CRL downloadCRL(String crlURL) throws IOException, - CertificateException, CRLException, - CertificateVerificationException, NamingException { - if (crlURL.startsWith("http://") || crlURL.startsWith("https://") - || crlURL.startsWith("ftp://")) { - X509CRL crl = downloadCRLFromWeb(crlURL); - return crl; - } else if (crlURL.startsWith("ldap://")) { - X509CRL crl = downloadCRLFromLDAP(crlURL); - return crl; - } else { - throw new CertificateVerificationException( - "Can not download CRL from certificate " + - "distribution point: " + crlURL); - } - } - - /** - * Downloads a CRL from given LDAP url, e.g. - * ldap://ldap.infonotary.com/dc=identity-ca,dc=infonotary,dc=com - */ - private static X509CRL downloadCRLFromLDAP(String ldapURL) - throws CertificateException, NamingException, CRLException, - CertificateVerificationException { - Hashtable env = new Hashtable(); - env.put(Context.INITIAL_CONTEXT_FACTORY, - "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, ldapURL); - - DirContext ctx = new InitialDirContext(env); - Attributes avals = ctx.getAttributes(""); - Attribute aval = avals.get("certificateRevocationList;binary"); - byte[] val = (byte[])aval.get(); - if ((val == null) || (val.length == 0)) { - throw new CertificateVerificationException( - "Can not download CRL from: " + ldapURL); - } else { - InputStream inStream = new ByteArrayInputStream(val); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL)cf.generateCRL(inStream); - return crl; - } - } - - /** - * Downloads a CRL from given HTTP/HTTPS/FTP URL, e.g. - * http://crl.infonotary.com/crl/identity-ca.crl - */ - private static X509CRL downloadCRLFromWeb(String crlURL) - throws MalformedURLException, IOException, CertificateException, - CRLException { - URL url = new URL(crlURL); - InputStream crlStream = url.openStream(); - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) cf.generateCRL(crlStream); - return crl; - } finally { - crlStream.close(); - } - } - - /** - * Extracts all CRL distribution point URLs from the "CRL Distribution Point" - * extension in a X.509 certificate. If CRL distribution point extension is - * unavailable, returns an empty list. - */ - public static List getCrlDistributionPoints( - X509Certificate cert) throws CertificateParsingException, IOException { - byte[] crldpExt = cert.getExtensionValue( - X509Extensions.CRLDistributionPoints.getId()); - if (crldpExt == null) { - List emptyList = new ArrayList(); - return emptyList; - } - ASN1InputStream oAsnInStream = new ASN1InputStream( - new ByteArrayInputStream(crldpExt)); - ASN1Primitive derObjCrlDP = oAsnInStream.readObject(); - DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP; - byte[] crldpExtOctets = dosCrlDP.getOctets(); - ASN1InputStream oAsnInStream2 = new ASN1InputStream( - new ByteArrayInputStream(crldpExtOctets)); - ASN1Primitive derObj2 = oAsnInStream2.readObject(); - CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2); - - oAsnInStream.close(); - oAsnInStream2.close(); - - List crlUrls = new ArrayList(); - for (DistributionPoint dp : distPoint.getDistributionPoints()) { - DistributionPointName dpn = dp.getDistributionPoint(); - // Look for URIs in fullName - if (dpn != null) { - if (dpn.getType() == DistributionPointName.FULL_NAME) { - GeneralName[] genNames = GeneralNames.getInstance( - dpn.getName()).getNames(); - // Look for an URI - for (int j = 0; j < genNames.length; j++) { - if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) { - String url = DERIA5String.getInstance( - genNames[j].getName()).getString(); - crlUrls.add(url); - } - } - } - } - } - return crlUrls; - } - -} diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java index ec85c04a..70f8c51a 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertCollection.java @@ -8,6 +8,9 @@ import org.iot.dsa.node.DSNode; import org.iot.dsa.time.DSTime; +/** + * @author Daniel Shapiro + */ public class CertCollection extends DSNode { public void addCertificate(X509Certificate cert) throws CertificateEncodingException { diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java index 1d579c33..23a10ead 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/CertNode.java @@ -7,6 +7,9 @@ import org.iot.dsa.node.action.ActionResult; import org.iot.dsa.node.action.DSAction; +/** + * @author Daniel Shapiro + */ public class CertNode extends DSValueNode { private static final String VALUE = "value"; diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java index 3a1f5457..5cad988f 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/HostnameWhitelist.java @@ -11,6 +11,9 @@ import org.iot.dsa.node.action.ActionResult; import org.iot.dsa.node.action.DSAbstractAction; +/** + * @author Daniel Shapiro + */ public class HostnameWhitelist extends DSNode { public static enum WhitelistValue { diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java index f5d78d23..4b8bf6f2 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/KeyToolUtil.java @@ -10,6 +10,10 @@ import org.iot.dsa.logging.DSLogger; import org.iot.dsa.time.DSTime; + +/** + * @author Daniel Shapiro + */ public class KeyToolUtil extends DSLogger { private static KeyToolUtil inst = new KeyToolUtil(); diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index 36b2bf75..f8e5d8eb 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -49,13 +49,13 @@ public class SysCertManager extends DSNode { // Constants // --------- - private static final String ALLOW_CLIENTS = "Allow_Anonymous_Clients"; - private static final String ALLOW_SERVERS = "Allow_Anonymous_Servers"; - private static final String VERIFY_HOSTNAMES = "Enable Hostname Verification"; + private static final String ALLOW_CLIENTS = "Allow Anonymous Clients"; + private static final String ALLOW_SERVERS = "Allow Anonymous Servers"; + private static final String VERIFY_HOSTNAMES = "Enable Hostname-Certificate Verification"; private static final String HOSTNAME_WHITELIST = "Hostname Whitelist"; - private static final String CERTFILE = "Cert_File"; - private static final String CERTFILE_PASS = "Cert_File_Pass"; - private static final String CERTFILE_TYPE = "Cert_File_Type"; + private static final String CERTFILE = "Cert File"; + private static final String CERTFILE_PASS = "Cert File Pass"; + private static final String CERTFILE_TYPE = "Cert File Type"; private static final String LOCAL_TRUSTSTORE = "Local Truststore"; private static final String QUARANTINE = "Quarantine"; private static final String GENERATE_CSR = "Generate Certificate Signing Request"; @@ -372,94 +372,5 @@ public boolean verify(String hostname, SSLSession session) { } } } - -// private static String generateCSR() { -// KeyPairGenerator keyGen; -// try { -// keyGen = KeyPairGenerator.getInstance("RSA"); -// } catch (NoSuchAlgorithmException e) { -// DSException.throwRuntime(e); -// return null; -// } -// keyGen.initialize(2048, new SecureRandom()); -// KeyPair pair = keyGen.generateKeyPair(); -// PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( -// new X500Principal("CN=dslink-java-v2, O=DSA, C=US"), pair.getPublic()); -// JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA"); -// ContentSigner signer; -// try { -// signer = csBuilder.build(pair.getPrivate()); -// } catch (OperatorCreationException e) { -// DSException.throwRuntime(e); -// return null; -// } -// PKCS10CertificationRequest csr = p10Builder.build(signer); -// StringWriter str = new StringWriter(); -// JcaPEMWriter pemWriter = new JcaPEMWriter(str); -// try { -// pemWriter.writeObject(csr); -// } catch (IOException e) { -// DSException.throwRuntime(e); -// return null; -// } finally { -// try { -// pemWriter.close(); -// str.close(); -// } catch (IOException e) { -// DSException.throwRuntime(e); -// return null; -// } -// } -// return str.toString(); -// } - -// private static X509Certificate generateSelfSigned() { -// KeyPairGenerator keyGen; -// try { -// keyGen = KeyPairGenerator.getInstance("RSA"); -// } catch (NoSuchAlgorithmException e) { -// DSException.throwRuntime(e); -// return null; -// } -// keyGen.initialize(2048, new SecureRandom()); -// KeyPair pair = keyGen.generateKeyPair(); -// -// Provider bcProvider = new BouncyCastleProvider(); -// Security.addProvider(bcProvider); -// -// long now = System.currentTimeMillis(); -// Date startDate = new Date(now); -// -// X500Name dname = new X500Name("CN=dslink-java-v2, O=DSA, C=US"); -// BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number -// -// Calendar calendar = Calendar.getInstance(); -// calendar.setTime(startDate); -// calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity -// Date endDate = calendar.getTime(); -// -// String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm. -// -// try { -// ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(pair.getPrivate()); -// JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dname, certSerialNumber, startDate, endDate, dname, pair.getPublic()); -// -// BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity -// certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); // Basic Constraints is usually marked as critical. -// -// return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); -// } catch (OperatorCreationException e) { -// DSException.throwRuntime(e); -// return null; -// } catch (CertIOException e) { -// DSException.throwRuntime(e); -// return null; -// } catch (CertificateException e) { -// DSException.throwRuntime(e); -// return null; -// } -// -// -// } } diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/LoggerNode.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/LoggerNode.java index 65e78274..20058abc 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/LoggerNode.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/LoggerNode.java @@ -8,6 +8,9 @@ import org.iot.dsa.node.action.ActionResult; import org.iot.dsa.node.action.DSAbstractAction; +/** + * @author Daniel Shapiro + */ public class LoggerNode extends StreamableLogNode { private DSInfo levelInfo = getInfo("Log Level"); diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/StreamableLogNode.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/StreamableLogNode.java index ebfcece1..1717d7c0 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/StreamableLogNode.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/StreamableLogNode.java @@ -20,6 +20,9 @@ import org.iot.dsa.node.action.ActionTable; import org.iot.dsa.node.action.DSAbstractAction; +/** + * @author Daniel Shapiro + */ public abstract class StreamableLogNode extends DSNode { private static DSList levelRange; diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/SysLogService.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/SysLogService.java index c8524cd3..f0b0c70b 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/SysLogService.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/logging/SysLogService.java @@ -16,6 +16,9 @@ import org.iot.dsa.node.action.ActionResult; import org.iot.dsa.node.action.DSAbstractAction; +/** + * @author Daniel Shapiro + */ public class SysLogService extends StreamableLogNode { private DSInfo levelInfo = getInfo("Default Log Level"); From 3e4833c89d93d7f6786481c98b581cf7b10d80f8 Mon Sep 17 00:00:00 2001 From: Daniel Shapiro Date: Tue, 28 Aug 2018 11:46:05 -0700 Subject: [PATCH 22/22] remove commented-out imports --- .../iot/dsa/dslink/sys/cert/SysCertManager.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java index f8e5d8eb..bc10f1f6 100644 --- a/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java +++ b/dslink-v2/src/main/java/com/acuity/iot/dsa/dslink/sys/cert/SysCertManager.java @@ -7,20 +7,6 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; -//import org.bouncycastle.asn1.ASN1ObjectIdentifier; -//import org.bouncycastle.asn1.x500.X500Name; -//import org.bouncycastle.asn1.x509.BasicConstraints; -//import org.bouncycastle.cert.CertIOException; -//import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -//import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -//import org.bouncycastle.jce.provider.BouncyCastleProvider; -//import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -//import org.bouncycastle.operator.ContentSigner; -//import org.bouncycastle.operator.OperatorCreationException; -//import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -//import org.bouncycastle.pkcs.PKCS10CertificationRequest; -//import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; -//import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.iot.dsa.node.DSBool; import org.iot.dsa.node.DSInfo; import org.iot.dsa.node.DSMap;