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