Skip to content

Commit 9b4ea1d

Browse files
committed
work on making certificate verification more correct
1 parent 74176d9 commit 9b4ea1d

File tree

3 files changed

+211
-10
lines changed

3 files changed

+211
-10
lines changed

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

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import java.security.Provider;
55
import java.security.Security;
66
import java.security.cert.CertificateException;
7+
import java.security.cert.PKIXCertPathBuilderResult;
8+
import java.security.cert.TrustAnchor;
79
import java.security.cert.X509Certificate;
810
import java.util.Arrays;
11+
import java.util.Collections;
12+
import java.util.HashSet;
913
import java.util.List;
14+
import java.util.Set;
1015
import javax.net.ssl.*;
1116

1217
/**
@@ -113,17 +118,14 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
113118
if (certManager.allowAnonymousClients()) {
114119
return;
115120
}
116-
if (certManager.isInTrustStore(chain[0])) {
117-
return;
118-
}
119121
if (defaultX509Mgr != null) {
120122
try {
121123
defaultX509Mgr.checkClientTrusted(chain, authType);
124+
return;
122125
} catch (CertificateException e) {
123-
certManager.addToQuarantine(chain[0]);
124-
throw e;
125126
}
126127
}
128+
checkLocally(chain, authType);
127129
}
128130

129131
@Override
@@ -132,17 +134,36 @@ public void checkServerTrusted(X509Certificate[] chain, String authType)
132134
if (certManager.allowAnonymousServers()) {
133135
return;
134136
}
135-
if (certManager.isInTrustStore(chain[0])) {
136-
return;
137-
}
138137
if (defaultX509Mgr != null) {
139138
try {
140139
defaultX509Mgr.checkServerTrusted(chain, authType);
140+
return;
141141
} catch (CertificateException e) {
142-
certManager.addToQuarantine(chain[0]);
143-
throw e;
144142
}
145143
}
144+
checkLocally(chain, authType);
145+
}
146+
147+
private void checkLocally(X509Certificate[] chain, String authType) throws CertificateException {
148+
Set<X509Certificate> chainAsSet = new HashSet<X509Certificate>();
149+
Collections.addAll(chainAsSet, chain);
150+
try {
151+
PKIXCertPathBuilderResult result = CertificateVerifier.verifyCertificate(chain[0], chainAsSet);
152+
TrustAnchor anchor = result.getTrustAnchor();
153+
X509Certificate anchorCert = anchor.getTrustedCert();
154+
155+
if (anchorCert == null) {
156+
throw new CertificateException();
157+
}
158+
159+
if (!certManager.isInTrustStore(anchorCert)) {
160+
certManager.addToQuarantine(anchorCert);
161+
throw new CertificateException();
162+
}
163+
164+
} catch (CertificateVerificationException e1) {
165+
throw new CertificateException();
166+
}
146167
}
147168

148169
@Override
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.acuity.iot.dsa.dslink.sys.cert;
2+
3+
/**
4+
* This class wraps an exception that could be thrown during
5+
* the certificate verification process.
6+
*
7+
* @author Svetlin Nakov
8+
*/
9+
public class CertificateVerificationException extends Exception {
10+
private static final long serialVersionUID = 1L;
11+
12+
public CertificateVerificationException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
16+
public CertificateVerificationException(String message) {
17+
super(message);
18+
}
19+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.acuity.iot.dsa.dslink.sys.cert;
2+
import java.security.GeneralSecurityException;
3+
import java.security.InvalidKeyException;
4+
import java.security.NoSuchAlgorithmException;
5+
import java.security.NoSuchProviderException;
6+
import java.security.PublicKey;
7+
import java.security.SignatureException;
8+
import java.security.cert.CertPathBuilder;
9+
import java.security.cert.CertPathBuilderException;
10+
import java.security.cert.CertStore;
11+
import java.security.cert.CertificateException;
12+
import java.security.cert.CollectionCertStoreParameters;
13+
import java.security.cert.PKIXBuilderParameters;
14+
import java.security.cert.PKIXCertPathBuilderResult;
15+
import java.security.cert.TrustAnchor;
16+
import java.security.cert.X509CertSelector;
17+
import java.security.cert.X509Certificate;
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
21+
/**
22+
* Class for building a certification chain for given certificate and verifying
23+
* it. Relies on a set of root CA certificates and intermediate certificates
24+
* that will be used for building the certification chain. The verification
25+
* process assumes that all self-signed certificates in the set are trusted
26+
* root CA certificates and all other certificates in the set are intermediate
27+
* certificates.
28+
*
29+
* @author Svetlin Nakov
30+
*/
31+
public class CertificateVerifier {
32+
33+
/**
34+
* Attempts to build a certification chain for given certificate and to verify
35+
* it. Relies on a set of root CA certificates and intermediate certificates
36+
* that will be used for building the certification chain. The verification
37+
* process assumes that all self-signed certificates in the set are trusted
38+
* root CA certificates and all other certificates in the set are intermediate
39+
* certificates.
40+
*
41+
* @param cert - certificate for validation
42+
* @param additionalCerts - set of trusted root CA certificates that will be
43+
* used as "trust anchors" and intermediate CA certificates that will be
44+
* used as part of the certification chain. All self-signed certificates
45+
* are considered to be trusted root CA certificates. All the rest are
46+
* considered to be intermediate CA certificates.
47+
* @return the certification chain (if verification is successful)
48+
* @throws CertificateVerificationException - if the certification is not
49+
* successful (e.g. certification path cannot be built or some
50+
* certificate in the chain is expired or CRL checks are failed)
51+
*/
52+
public static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert,
53+
Set<X509Certificate> additionalCerts)
54+
throws CertificateVerificationException {
55+
try {
56+
// Check for self-signed certificate
57+
if (isSelfSigned(cert)) {
58+
throw new CertificateVerificationException(
59+
"The certificate is self-signed.");
60+
}
61+
62+
// Prepare a set of trusted root CA certificates
63+
// and a set of intermediate certificates
64+
Set<X509Certificate> trustedRootCerts = new HashSet<X509Certificate>();
65+
Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
66+
for (X509Certificate additionalCert : additionalCerts) {
67+
if (isSelfSigned(additionalCert)) {
68+
trustedRootCerts.add(additionalCert);
69+
} else {
70+
intermediateCerts.add(additionalCert);
71+
}
72+
}
73+
74+
// Attempt to build the certification chain and verify it
75+
PKIXCertPathBuilderResult verifiedCertChain =
76+
verifyCertificate(cert, trustedRootCerts, intermediateCerts);
77+
78+
// Check whether the certificate is revoked by the CRL
79+
// given in its CRL distribution point extension
80+
// CRLVerifier.verifyCertificateCRLs(cert); //TODO
81+
82+
// The chain is built and verified. Return it as a result
83+
return verifiedCertChain;
84+
} catch (CertPathBuilderException certPathEx) {
85+
throw new CertificateVerificationException(
86+
"Error building certification path: " +
87+
cert.getSubjectX500Principal(), certPathEx);
88+
} catch (CertificateVerificationException cvex) {
89+
throw cvex;
90+
} catch (Exception ex) {
91+
throw new CertificateVerificationException(
92+
"Error verifying the certificate: " +
93+
cert.getSubjectX500Principal(), ex);
94+
}
95+
}
96+
97+
/**
98+
* Checks whether given X.509 certificate is self-signed.
99+
*/
100+
public static boolean isSelfSigned(X509Certificate cert)
101+
throws CertificateException, NoSuchAlgorithmException,
102+
NoSuchProviderException {
103+
try {
104+
// Try to verify certificate signature with its own public key
105+
PublicKey key = cert.getPublicKey();
106+
cert.verify(key);
107+
return true;
108+
} catch (SignatureException sigEx) {
109+
// Invalid signature --> not self-signed
110+
return false;
111+
} catch (InvalidKeyException keyEx) {
112+
// Invalid key --> not self-signed
113+
return false;
114+
}
115+
}
116+
117+
/**
118+
* Attempts to build a certification chain for given certificate and to verify
119+
* it. Relies on a set of root CA certificates (trust anchors) and a set of
120+
* intermediate certificates (to be used as part of the chain).
121+
* @param cert - certificate for validation
122+
* @param trustedRootCerts - set of trusted root CA certificates
123+
* @param intermediateCerts - set of intermediate certificates
124+
* @return the certification chain (if verification is successful)
125+
* @throws GeneralSecurityException - if the verification is not successful
126+
* (e.g. certification path cannot be built or some certificate in the
127+
* chain is expired)
128+
*/
129+
private static PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> trustedRootCerts,
130+
Set<X509Certificate> intermediateCerts) throws GeneralSecurityException {
131+
132+
// Create the selector that specifies the starting certificate
133+
X509CertSelector selector = new X509CertSelector();
134+
selector.setCertificate(cert);
135+
136+
// Create the trust anchors (set of root CA certificates)
137+
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
138+
for (X509Certificate trustedRootCert : trustedRootCerts) {
139+
trustAnchors.add(new TrustAnchor(trustedRootCert, null));
140+
}
141+
142+
// Configure the PKIX certificate builder algorithm parameters
143+
PKIXBuilderParameters pkixParams =
144+
new PKIXBuilderParameters(trustAnchors, selector);
145+
146+
// Disable CRL checks (this is done manually as additional step)
147+
pkixParams.setRevocationEnabled(false);
148+
149+
// Specify a list of intermediate certificates
150+
CertStore intermediateCertStore = CertStore.getInstance("Collection",
151+
new CollectionCertStoreParameters(intermediateCerts), "BC");
152+
pkixParams.addCertStore(intermediateCertStore);
153+
154+
// Build and verify the certification chain
155+
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
156+
PKIXCertPathBuilderResult result =
157+
(PKIXCertPathBuilderResult) builder.build(pkixParams);
158+
return result;
159+
}
160+
161+
}

0 commit comments

Comments
 (0)