View Javadoc

1   package org.astrogrid.security.rfc3820;
2   
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.CertificateException;
9   import java.security.cert.X509Certificate;
10  import javax.security.auth.x500.X500Principal;
11  import org.globus.gsi.TrustedCertificates;
12  import org.globus.gsi.proxy.ProxyPathValidator;
13  import org.globus.gsi.proxy.ProxyPathValidatorException;
14  
15  /***
16   * A validator for certificate chains formed according to RFC3820.
17   * These chains are the kind that can include proxy certificates.
18   *
19   * Most of the checking is done in the superclass,
20   * org.globus.gsi.proxy.ProxyPathValidator; see that class for details.
21   * This current class adds checks on the signatures of the certificates.
22   *
23   * @author Guy Rixon
24   */
25  public class CertificateChainValidator extends ProxyPathValidator {
26  
27    /***
28     * Constructs a new instance of CertificateChainValidator.
29     */
30    public CertificateChainValidator() {
31      super();
32    }
33  
34    /***
35     * Validates a certificate chain conforming to RFC3820. Such a chain
36     * may include proxy certificates. The equivalent method in the
37     * Globus superclass is called to check the subject-issuer relationships
38     * and the use of the ProxyCert and BasicConstraints extensions. The
39     * signatures on the certificates are then checked. This method assumes that
40     * the given chain is complete and includes the trust anchor in the last
41     * element of the array.
42     *
43     * @param chain The chain to be checked, with the trust anchor in the last place.
44     * @throws ProxyPathException - if the use of proxy certificates is invalid.
45     * @throws NoSuchAlgorithmException - on unsupported signature algorithms.
46     * @throws InvalidKeyException - on incorrect key.
47     * @throws NoSuchProviderException - if there's no default provider for the JCE.
48     * @throws SignatureException - on signature errors.
49     * @throws CertificateException - on encoding errors.
50     */
51    public void validateChain(X509Certificate[] chain)
52        throws CertificateException,
53               InvalidKeyException,
54               NoSuchAlgorithmException,
55               NoSuchProviderException,
56               ProxyPathValidatorException,
57               SignatureException {
58  
59      // Validate the subject-issuer relationships and the contraints
60      // but not the signatures. (The authors of Globus seem to treat
61      // signatures separately.)
62      super.validate(chain);
63  
64      // Validate the signatures; i.e. for certificate i putatively issued by
65      // certificate i+1, check that i was signed by the private key matching
66      // the public key in i+1. For the last certificate in the chain, check the
67      // signature against the key in the trust anchor.
68      for (int i = 0; i < chain.length - 1; i++) {
69        PublicKey key = chain[i+1].getPublicKey();
70        chain[i].verify(key);
71      }
72    }
73  
74    /***
75     * Validates a certificate chain conforming to RFC3820. Such as chain
76     * may include proxy certificates. The equivalent method in the
77     * Globus superclass is called to check the subject-issuer relationships
78     * and the use of the ProxyCert and BasicConstraints extensions. The
79     * signatures on the certificates are then checked. This method assumes that
80     * the given chain lacks a trust anchor and that a set of possible
81     * trust-anchors is passed separately.
82     *
83     * @param chain The chain to be checked.
84     * @param trustAnchors The set of trusted certificates.
85     * @throws ProxyPathException - if the use of proxy certificates is invalid.
86     * @throws NoSuchAlgorithmException - on unsupported signature algorithms.
87     * @throws InvalidKeyException - on incorrect key.
88     * @throws NoSuchProviderException - if there's no default provider for the JCE.
89     * @throws SignatureException - on signature errors.
90     * @throws CertificateException - on encoding errors.
91     */
92    public void validateChain(X509Certificate[] chain,
93                              TrustedCertificates trustAnchors)
94        throws CertificateException,
95               InvalidKeyException,
96               NoSuchAlgorithmException,
97               NoSuchProviderException,
98               ProxyPathValidatorException,
99               SignatureException {
100 
101     // Validate the subject-issuer relationships and the contraints
102     // but not the signatures. (The authors of Globus seem to treat
103     // signatures separately.)
104     super.validate(chain, trustAnchors);
105 
106     // Validate the signatures; i.e. for certificate i putatively issued by
107     // certificate i+1, check that i was signed by the private key matching
108     // the public key in i+1.
109     for (int i = 0; i < chain.length - 1; i++) {
110       X509Certificate signatory = chain[i+1];
111       chain[i].verify(signatory.getPublicKey());
112     }
113 
114     // Validate the signature on the last certificate in the chain against the
115     // appropriate trust-anchor.
116     X509Certificate lastInGivenChain = chain[chain.length-1];
117     X500Principal issuer = lastInGivenChain.getIssuerX500Principal();
118     X509Certificate[] anchors = trustAnchors.getCertificates();
119     for (int j = 0; j < anchors.length; j++) {
120       X509Certificate anchor = anchors[j];
121       //System.out.println("\nHave: " + anchor.getIssuerX500Principal().getName(X500Principal.CANONICAL));
122       //System.out.println("Have: " + anchor.getIssuerX500Principal().getName(X500Principal.RFC1779));
123       //System.out.println("Have: " + anchor.getIssuerX500Principal().getName(X500Principal.RFC2253));
124       //System.out.println("Need: " + issuer.getName(X500Principal.CANONICAL));
125       if(issuer.equals(anchor.getSubjectX500Principal())) {
126         lastInGivenChain.verify(anchor.getPublicKey());
127         return;
128       }
129     }
130     throw new ProxyPathValidatorException(ProxyPathValidatorException.UNKNOWN_CA,
131                                           lastInGivenChain,
132                                           "No trusted certificate with subject " +
133                                           issuer.getName() +
134                                           " is available.");
135   }
136 
137 }