Source code: jsdsi/Certificate.java
1 /*
2 * Copyright 2002 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this program for any
5 * purpose and without fee is hereby granted, provided that this
6 * copyright and permission notice appear on all copies and supporting
7 * documentation, the name of M.I.T. not be used in advertising or
8 * publicity pertaining to distribution of the program without specific
9 * prior permission, and notice be given in supporting documentation that
10 * copying and distribution is by permission of M.I.T. M.I.T. makes no
11 * representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 */
14 package jsdsi;
15
16 import java.security.MessageDigest;
17 import java.security.PublicKey;
18 import java.security.NoSuchAlgorithmException;
19 import java.security.NoSuchProviderException;
20 import java.security.InvalidKeyException;
21 import java.security.SignatureException;
22 import java.security.cert.CertificateException;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Arrays;
26 import java.util.ArrayList;
27
28 /**
29 * A Cert along with its validators (verification path, signature, and
30 * online test results). Whereas a Cert is simply an unauthenticated
31 * statement, a Certificate is self-validating and thus can be
32 * considered authentic if verify() succeeds. A Certificate is
33 * serialized as a SPKI/SDSI Sequence.
34 *
35 * @see Cert
36 * @see Signature
37 * @see Sequence
38 *
39 * @author Sameer Ajmani
40 * @version $Revision: 1.2 $ $Date: 2003/04/22 21:37:44 $
41 */
42 public class Certificate extends java.security.cert.Certificate {
43 /**
44 * Issuer of this <code>Certificate</code>.
45 */
46 private PublicKey issuer; // if null, cert.getIssuer() is a key
47
48 /**
49 * <code>Cert</code> of this certificate.
50 */
51 private Cert cert;
52
53 // TODO: VerificationPath ...
54
55 /**
56 * <code>Signature</code> of this <code>Certificate</code>.
57 */
58 private Signature sig;
59
60 // TODO: OnlineTestResult[] ...
61
62 /**
63 * Creates a new <code>Certificate</code> from a given public key,
64 * <code>Cert</code>, and signature.
65 *
66 * @param k public key of the issuer (principal).
67 * @param c <code>Cert</code> of this <code>Certificate</code>.
68 * @param s <code>Signature</code> of this <code>Certificate</code>.
69 * @throws CertificateException if <code>k</code> is <code>null</code> or
70 * not a <code>PublicKey</code>.
71 */
72 public Certificate(PublicKey k, Cert c, Signature s)
73 throws CertificateException {
74 super("SPKI");
75 assert(c != null) : "null cert";
76 assert(s != null) : "null signature";
77 issuer = k;
78 cert = c;
79 sig = s;
80 if ((issuer == null) && !(c.getIssuer() instanceof PublicKey)) {
81 throw new CertificateException("issuer must be a PublicKey");
82 }
83 }
84
85 /**
86 * Creates a new <code>Certificate</code> from a given <code>Cert</code>
87 * and <code>Signature</code>.
88 *
89 * @param c <code>Cert</code> to create a new <code>Certificate</code>
90 * from.
91 * @param s <code>Signature</code> to create a new
92 * <code>Certificate</code> from.
93 * @throws CertificateException
94 */
95 public Certificate(Cert c, Signature s) throws CertificateException {
96 this(null, c, s);
97 }
98
99 /**
100 * @see java.security.cert.Certificate#getPublicKey()
101 */
102 public java.security.PublicKey getPublicKey() {
103 if (issuer == null) {
104 return (PublicKey) cert.getIssuer();
105 } else {
106 return issuer;
107 }
108 }
109
110 /**
111 * @see java.security.cert.Certificate#verify(PublicKey)
112 */
113 public void verify(java.security.PublicKey key)
114 throws
115 CertificateException,
116 NoSuchAlgorithmException,
117 InvalidKeyException,
118 NoSuchProviderException,
119 SignatureException {
120 verify(key, null);
121 }
122
123 /**
124 * @see java.security.cert.Certificate#verify(PublicKey, String)
125 */
126 public void verify(java.security.PublicKey key, String sigProvider)
127 throws
128 CertificateException,
129 NoSuchAlgorithmException,
130 InvalidKeyException,
131 NoSuchProviderException,
132 SignatureException {
133 assert(key != null) : "null key";
134 if (!cert.getIssuer().samePrincipalAs(sig.getSigner())) {
135 throw new CertificateException("issuer does not match signer");
136 }
137 if (!(key instanceof Principal)
138 || !cert.getIssuer().samePrincipalAs((Principal) key)) {
139 throw new CertificateException("verification key does not match issuer");
140 }
141 if (!sig.verify(key, cert, sigProvider)) {
142 throw new SignatureException("signature verification failed");
143 }
144 // TODO: check verification path, online test results, etc.
145 }
146
147 /**
148 * Returns the <code>Cert</code> of this <code>Certificate</code>.
149 *
150 * @return the <code>Cert</code> of this <code>Certificate</code>.
151 */
152 public Cert getCert() {
153 return cert;
154 }
155
156 /**
157 * Checks if a given <code>Iterator</code> has more elements.
158 *
159 * @param elems <code>Iterator</code> to check for if it has more
160 * elements.
161 * @throws CertificateException if <code>elems</code> does not have
162 * any more elements.
163 */
164 private static void check(Iterator elems) throws CertificateException {
165 if (!elems.hasNext()) {
166 throw new CertificateException("Not enough elements in sequence");
167 }
168 }
169
170 /**
171 * Factory method for creating a new <code>Certificate</code> from an
172 * iterator that holds a <code>Cert</code> and a <code>Signature</code>.
173 *
174 * @param elems <code>Iterator</code> holding a <code>Cert</code> and a
175 * <code>Signature</code> (in this order).
176 * @return the certificate created from the <code>Cert</code> and the
177 * <code>Signature</code> in <code>elems</code>.
178 * @throws CertificateException if <code>elems</code> does not contain
179 * the expected values.
180 */
181 public static Certificate fromElements(Iterator elems)
182 throws CertificateException {
183 PublicKey issuer = null;
184 check(elems);
185 Element e = (Element) elems.next();
186 if (e instanceof PublicKey) {
187 issuer = (PublicKey) e;
188 check(elems);
189 e = (Element) elems.next();
190 }
191 if (!(e instanceof Cert)) {
192 throw new CertificateException("Expected cert");
193 }
194 Cert cert = (Cert) e;
195 check(elems);
196 e = (Element) elems.next();
197 if (!(e instanceof Signature)) {
198 throw new CertificateException("Expected signature");
199 }
200 Signature sig = (Signature) e;
201 // TODO: include verification path, online test results, etc.
202 return new Certificate(issuer, cert, sig);
203 }
204
205 /**
206 * Adds the issuer (if not <code>null</code>), the <code>Cert</code>,
207 * and the <code>Signture</code> of this <code>Certificate</code> to
208 * the given List.
209 *
210 * @param elems <code>List</code> to add the issuer (if not
211 * <code>null</code>), the <code>Cert</code>, and the
212 * <code>Signture</code> of this <code>Certificate</code> to.
213 */
214 public void toElements(List elems) {
215 if (issuer != null) {
216 elems.add(issuer);
217 // TODO: if key is hashed in cert or sig, include hash ops
218 }
219 elems.add(cert);
220 elems.add(sig);
221 // TODO: include verification path, online test results, etc.
222 }
223
224 /**
225 * Factory method that creates a new <code>Certificate</code> from a given
226 * <code>Sequence</code> that contains a <code>Cert</code> and a
227 * <code>Signature</code> (in this order).
228 *
229 * @param seq <code>Sequence</code> holding the <code>Cert</code> and the
230 * <code>Signature</code> to create the <code>Certificate</code>
231 * from.
232 * @return the new <code>Certificate</code>.
233 * @throws CertificateException if the creation of the
234 * <code>Certificate</code> failed.
235 */
236 public static Certificate fromSequence(Sequence seq)
237 throws CertificateException {
238 return fromElements(Arrays.asList(seq.getElements()).iterator());
239 }
240
241 /**
242 * Returns a <code>Sequence</code> containing the issuer (if not
243 * <code>null</code>), the <code>Cert</code>, and the
244 * <code>Signature</code> of this <code>Certificate</code>.
245 * @return Sequence
246 */
247 public Sequence toSequence() {
248 ArrayList elems = new ArrayList();
249 toElements(elems);
250 return new Sequence((Element[]) elems.toArray(new Element[0]));
251 }
252
253 /**
254 * @see java.lang.Object#toString()
255 */
256 public String toString() {
257 return toSequence().toString();
258 }
259
260 /**
261 * @see java.security.cert.Certificate#getEncoded()
262 */
263 public byte[] getEncoded() {
264 return toSequence().toByteArray();
265 }
266
267 /**
268 * Returns the format of this <code>Certificate</code>, namely
269 * <code>"SEXP"</code>.
270 *
271 * @return the format of this <code>Certificate</code>
272 * (<code>"SEXP"</code>).
273 */
274 public String getFormat() {
275 return "SEXP";
276 }
277 }