KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > security > CodeSource


1 /*
2  * @(#)CodeSource.java 1.38 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7  
8 package java.security;
9
10
11 import java.net.URL JavaDoc;
12 import java.net.SocketPermission JavaDoc;
13 import java.util.ArrayList JavaDoc;
14 import java.util.List JavaDoc;
15 import java.util.Hashtable JavaDoc;
16 import java.io.ByteArrayInputStream JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.security.cert.*;
19
20 /**
21  *
22  * <p>This class extends the concept of a codebase to
23  * encapsulate not only the location (URL) but also the certificate chains
24  * that were used to verify signed code originating from that location.
25  *
26  * @version 1.38, 12/19/03
27  * @author Li Gong
28  * @author Roland Schemers
29  */

30
31 public class CodeSource implements java.io.Serializable JavaDoc {
32
33     private static final long serialVersionUID = 4977541819976013951L;
34
35     /**
36      * The code location.
37      *
38      * @serial
39      */

40     private URL JavaDoc location;
41
42     /*
43      * The code signers.
44      */

45     private transient CodeSigner JavaDoc[] signers = null;
46
47     /*
48      * The code signers. Certificate chains are concatenated.
49      */

50     private transient java.security.cert.Certificate JavaDoc certs[] = null;
51
52     // cached SocketPermission used for matchLocation
53
private transient SocketPermission JavaDoc sp;
54
55     // for generating cert paths
56
private transient CertificateFactory factory = null;
57
58     /**
59      * Constructs a CodeSource and associates it with the specified
60      * location and set of certificates.
61      *
62      * @param url the location (URL).
63      *
64      * @param certs the certificate(s). It may be null. The contents of the
65      * array are copied to protect against subsequent modification.
66      */

67     public CodeSource(URL JavaDoc url, java.security.cert.Certificate JavaDoc certs[]) {
68     this.location = url;
69
70     // Copy the supplied certs
71
if (certs != null) {
72         this.certs = (java.security.cert.Certificate JavaDoc[]) certs.clone();
73     }
74     }
75
76     /**
77      * Constructs a CodeSource and associates it with the specified
78      * location and set of code signers.
79      *
80      * @param url the location (URL).
81      * @param signers the code signers. It may be null. The contents of the
82      * array are copied to protect against subsequent modification.
83      *
84      * @since 1.5
85      */

86     public CodeSource(URL JavaDoc url, CodeSigner JavaDoc[] signers) {
87     this.location = url;
88
89     // Copy the supplied signers
90
if (signers != null) {
91         this.signers = (CodeSigner JavaDoc[])signers.clone();
92     }
93     }
94
95     /**
96      * Returns the hash code value for this object.
97      *
98      * @return a hash code value for this object.
99      */

100
101     public int hashCode() {
102     if (location != null)
103         return location.hashCode();
104     else
105         return 0;
106     }
107
108     /**
109      * Tests for equality between the specified object and this
110      * object. Two CodeSource objects are considered equal if their
111      * locations are of identical value and if their signer certificate
112      * chains are of identical value. It is not required that
113      * the certificate chains be in the same order.
114      *
115      * @param obj the object to test for equality with this object.
116      *
117      * @return true if the objects are considered equal, false otherwise.
118      */

119     public boolean equals(Object JavaDoc obj) {
120     if (obj == this)
121         return true;
122
123     // objects types must be equal
124
if (!(obj instanceof CodeSource JavaDoc))
125         return false;
126
127     CodeSource JavaDoc cs = (CodeSource JavaDoc) obj;
128
129     // URLs must match
130
if (location == null) {
131         // if location is null, then cs.location must be null as well
132
if (cs.location != null) return false;
133     } else {
134         // if location is not null, then it must equal cs.location
135
if (!location.equals(cs.location)) return false;
136     }
137
138     // certs must match
139
return matchCerts(cs, true);
140     }
141
142     /**
143      * Returns the location associated with this CodeSource.
144      *
145      * @return the location (URL).
146      */

147     public final URL JavaDoc getLocation() {
148     /* since URL is practically immutable, returning itself is not
149            a security problem */

150     return this.location;
151     }
152
153     /**
154      * Returns the certificates associated with this CodeSource.
155      * <p>
156      * If this CodeSource object was created using the
157      * {@link #CodeSource(URL url, CodeSigner[] signers)}
158      * constructor then its certificate chains are extracted and used to
159      * create an array of Certificate objects. Each signer certificate is
160      * followed by its supporting certificate chain (which may be empty).
161      * Each signer certificate and its supporting certificate chain is ordered
162      * bottom-to-top (i.e., with the signer certificate first and the (root)
163      * certificate authority last).
164      *
165      * @return A copy of the certificates array, or null if there is none.
166      */

167     public final java.security.cert.Certificate JavaDoc[] getCertificates() {
168     if (certs != null) {
169         return (java.security.cert.Certificate JavaDoc[]) certs.clone();
170
171     } else if (signers != null) {
172         // Convert the code signers to certs
173
ArrayList JavaDoc certChains = new ArrayList JavaDoc();
174         for (int i = 0; i < signers.length; i++) {
175         certChains.addAll(
176             signers[i].getSignerCertPath().getCertificates());
177         }
178         certs = (java.security.cert.Certificate JavaDoc[])
179         certChains.toArray(
180             new java.security.cert.Certificate JavaDoc[certChains.size()]);
181         return (java.security.cert.Certificate JavaDoc[]) certs.clone();
182
183     } else {
184         return null;
185     }
186     }
187
188     /**
189      * Returns the code signers associated with this CodeSource.
190      * <p>
191      * If this CodeSource object was created using the
192      * {@link #CodeSource(URL url, Certificate[] certs)}
193      * constructor then its certificate chains are extracted and used to
194      * create an array of CodeSigner objects. Note that only X.509 certificates
195      * are examined - all other certificate types are ignored.
196      *
197      * @return A copy of the code signer array, or null if there is none.
198      *
199      * @since 1.5
200      */

201     public final CodeSigner JavaDoc[] getCodeSigners() {
202     if (signers != null) {
203         return (CodeSigner JavaDoc[]) signers.clone();
204
205     } else if (certs != null) {
206         // Convert the certs to code signers
207
signers = convertCertArrayToSignerArray(certs);
208         return (CodeSigner JavaDoc[]) signers.clone();
209         
210     } else {
211         return null;
212     }
213     }
214
215     /**
216      * Returns true if this CodeSource object "implies" the specified CodeSource.
217      * <P>
218      * More specifically, this method makes the following checks, in order.
219      * If any fail, it returns false. If they all succeed, it returns true.<p>
220      * <ol>
221      * <li> <i>codesource</i> must not be null.
222      * <li> If this object's certificates are not null, then all
223      * of this object's certificates must be present in <i>codesource</i>'s
224      * certificates.
225      * <li> If this object's location (getLocation()) is not null, then the
226      * following checks are made against this object's location and
227      * <i>codesource</i>'s:<p>
228      * <ol>
229      * <li> <i>codesource</i>'s location must not be null.
230      *
231      * <li> If this object's location
232      * equals <i>codesource</i>'s location, then return true.
233      *
234      * <li> This object's protocol (getLocation().getProtocol()) must be
235      * equal to <i>codesource</i>'s protocol.
236      *
237      * <li> If this object's host (getLocation().getHost()) is not null,
238      * then the SocketPermission
239      * constructed with this object's host must imply the
240      * SocketPermission constructed with <i>codesource</i>'s host.
241      *
242      * <li> If this object's port (getLocation().getPort()) is not
243      * equal to -1 (that is, if a port is specified), it must equal
244      * <i>codesource</i>'s port.
245      *
246      * <li> If this object's file (getLocation().getFile()) doesn't equal
247      * <i>codesource</i>'s file, then the following checks are made:
248      * If this object's file ends with "/-",
249      * then <i>codesource</i>'s file must start with this object's
250      * file (exclusive the trailing "-").
251      * If this object's file ends with a "/*",
252      * then <i>codesource</i>'s file must start with this object's
253      * file and must not have any further "/" separators.
254      * If this object's file doesn't end with a "/",
255      * then <i>codesource</i>'s file must match this object's
256      * file with a '/' appended.
257      *
258      * <li> If this object's reference (getLocation().getRef()) is
259      * not null, it must equal <i>codesource</i>'s reference.
260      *
261      * </ol>
262      * </ol>
263      * <p>
264      * For example, the codesource objects with the following locations
265      * and null certificates all imply
266      * the codesource with the location "http://java.sun.com/classes/foo.jar"
267      * and null certificates:
268      * <pre>
269      * http:
270      * http://*.sun.com/classes/*
271      * http://java.sun.com/classes/-
272      * http://java.sun.com/classes/foo.jar
273      * </pre>
274      *
275      * Note that if this CodeSource has a null location and a null
276      * certificate chain, then it implies every other CodeSource.
277      *
278      * @param codesource CodeSource to compare against.
279      *
280      * @return true if the specified codesource is implied by this codesource,
281      * false if not.
282      */

283  
284     public boolean implies(CodeSource JavaDoc codesource)
285     {
286     if (codesource == null)
287         return false;
288
289     return matchCerts(codesource, false) && matchLocation(codesource);
290     }
291
292     /**
293      * Returns true if all the certs in this
294      * CodeSource are also in <i>that</i>.
295      *
296      * @param that the CodeSource to check against.
297      * @param strict If true then a strict equality match is performed.
298      * Otherwise a subset match is performed.
299      */

300     private boolean matchCerts(CodeSource JavaDoc that, boolean strict)
301     {
302     // match any key
303
if (certs == null && signers == null)
304         return true;
305
306     // match no key
307
if (that.certs == null && that.signers == null)
308         return false;
309
310     boolean match;
311     // both have signers
312
if (signers != null && that.signers != null) {
313         if (strict && signers.length != that.signers.length) {
314         return false;
315         }
316         for (int i = 0; i < signers.length; i++) {
317         match = false;
318         for (int j = 0; j < that.signers.length; j++) {
319             if (signers[i].equals(that.signers[j])) {
320             match = true;
321             break;
322             }
323         }
324         if (!match) return false;
325         }
326         return true;
327
328     // both have certs
329
} else {
330         if (strict && certs.length != that.certs.length) {
331         return false;
332         }
333         for (int i = 0; i < certs.length; i++) {
334         match = false;
335         for (int j = 0; j < that.certs.length; j++) {
336             if (certs[i].equals(that.certs[j])) {
337             match = true;
338             break;
339             }
340         }
341         if (!match) return false;
342         }
343         return true;
344     }
345     }
346
347
348     /**
349      * Returns true if two CodeSource's have the "same" location.
350      *
351      * @param that CodeSource to compare against
352      */

353     private boolean matchLocation(CodeSource JavaDoc that)
354     {
355         if (location == null) {
356         return true;
357         }
358
359         if ((that == null) || (that.location == null))
360         return false;
361
362         if (location.equals(that.location))
363         return true;
364
365         if (!location.getProtocol().equals(that.location.getProtocol()))
366         return false;
367
368         String JavaDoc thisHost = location.getHost();
369         String JavaDoc thatHost = that.location.getHost();
370
371         if (thisHost != null) {
372         if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
373             ("".equals(thatHost) || "localhost".equals(thatHost))) {
374             // ok
375
} else if (!thisHost.equals(thatHost)) {
376             if (thatHost == null) {
377             return false;
378             }
379             if (this.sp == null) {
380             this.sp = new SocketPermission JavaDoc(thisHost, "resolve");
381             }
382             if (that.sp == null) {
383             that.sp = new SocketPermission JavaDoc(thatHost, "resolve");
384             }
385             if (!this.sp.implies(that.sp)) {
386             return false;
387             }
388         }
389         }
390
391         if (location.getPort() != -1) {
392         if (location.getPort() != that.location.getPort())
393             return false;
394         }
395
396         if (location.getFile().endsWith("/-")) {
397         // Matches the directory and (recursively) all files
398
// and subdirectories contained in that directory.
399
// For example, "/a/b/-" implies anything that starts with
400
// "/a/b/"
401
String JavaDoc thisPath = location.getFile().substring(0,
402                                                 location.getFile().length()-1);
403         if (!that.location.getFile().startsWith(thisPath))
404             return false;
405         } else if (location.getFile().endsWith("/*")) {
406         // Matches the directory and all the files contained in that
407
// directory.
408
// For example, "/a/b/*" implies anything that starts with
409
// "/a/b/" but has no further slashes
410
int last = that.location.getFile().lastIndexOf('/');
411         if (last == -1)
412             return false;
413         String JavaDoc thisPath = location.getFile().substring(0,
414                                                 location.getFile().length()-1);
415         String JavaDoc thatPath = that.location.getFile().substring(0, last+1);
416         if (!thatPath.equals(thisPath))
417             return false;
418         } else {
419         // Exact matches only.
420
// For example, "/a/b" and "/a/b/" both imply "/a/b/"
421
if ((!that.location.getFile().equals(location.getFile()))
422         && (!that.location.getFile().equals(location.getFile()+"/"))) {
423             return false;
424         }
425         }
426
427         if (location.getRef() == null)
428         return true;
429         else
430         return location.getRef().equals(that.location.getRef());
431     }
432
433     /**
434      * Returns a string describing this CodeSource, telling its
435      * URL and certificates.
436      *
437      * @return information about this CodeSource.
438      */

439     public String JavaDoc toString() {
440     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
441     sb.append("(");
442     sb.append(this.location);
443
444     if (this.certs != null && this.certs.length > 0) {
445         for (int i = 0; i < this.certs.length; i++) {
446         sb.append( " " + this.certs[i]);
447         }
448
449     } else if (this.signers != null && this.signers.length > 0) {
450         for (int i = 0; i < this.signers.length; i++) {
451         sb.append( " " + this.signers[i]);
452         }
453     } else {
454         sb.append(" <no signer certificates>");
455     }
456     sb.append(")");
457     return sb.toString();
458     }
459
460     /**
461      * Writes this object out to a stream (i.e., serializes it).
462      *
463      * @serialData An initial <code>URL</code> is followed by an
464      * <code>int</code> indicating the number of certificates to follow
465      * (a value of "zero" denotes that there are no certificates associated
466      * with this object).
467      * Each certificate is written out starting with a <code>String</code>
468      * denoting the certificate type, followed by an
469      * <code>int</code> specifying the length of the certificate encoding,
470      * followed by the certificate encoding itself which is written out as an
471      * array of bytes. Finally, if any code signers are present then the array
472      * of code signers is serialized and written out too.
473      */

474     private synchronized void writeObject(java.io.ObjectOutputStream JavaDoc oos)
475         throws IOException JavaDoc
476     {
477     oos.defaultWriteObject(); // location
478

479     // Serialize the array of certs
480
if (certs == null || certs.length == 0) {
481         oos.writeInt(0);
482     } else {
483         // write out the total number of certs
484
oos.writeInt(certs.length);
485         // write out each cert, including its type
486
for (int i = 0; i < certs.length; i++) {
487         java.security.cert.Certificate JavaDoc cert = certs[i];
488         try {
489             oos.writeUTF(cert.getType());
490             byte[] encoded = cert.getEncoded();
491             oos.writeInt(encoded.length);
492             oos.write(encoded);
493         } catch (CertificateEncodingException cee) {
494             throw new IOException JavaDoc(cee.getMessage());
495         }
496         }
497     }
498
499     // Serialize the array of code signers (if any)
500
if (signers != null && signers.length > 0) {
501         oos.writeObject(signers);
502     }
503     }
504
505     /**
506      * Restores this object from a stream (i.e., deserializes it).
507      */

508     private synchronized void readObject(java.io.ObjectInputStream JavaDoc ois)
509     throws IOException JavaDoc, ClassNotFoundException JavaDoc
510     {
511     CertificateFactory cf;
512     Hashtable JavaDoc cfs = null;
513
514     ois.defaultReadObject(); // location
515

516     // process any new-style certs in the stream (if present)
517
int size = ois.readInt();
518     if (size > 0) {
519         // we know of 3 different cert types: X.509, PGP, SDSI, which
520
// could all be present in the stream at the same time
521
cfs = new Hashtable JavaDoc(3);
522         this.certs = new java.security.cert.Certificate JavaDoc[size];
523     }
524
525     for (int i = 0; i < size; i++) {
526         // read the certificate type, and instantiate a certificate
527
// factory of that type (reuse existing factory if possible)
528
String JavaDoc certType = ois.readUTF();
529         if (cfs.containsKey(certType)) {
530         // reuse certificate factory
531
cf = (CertificateFactory)cfs.get(certType);
532         } else {
533         // create new certificate factory
534
try {
535             cf = CertificateFactory.getInstance(certType);
536         } catch (CertificateException ce) {
537             throw new ClassNotFoundException JavaDoc
538             ("Certificate factory for " + certType + " not found");
539         }
540         // store the certificate factory so we can reuse it later
541
cfs.put(certType, cf);
542         }
543         // parse the certificate
544
byte[] encoded = null;
545         try {
546         encoded = new byte[ois.readInt()];
547         } catch (OutOfMemoryError JavaDoc oome) {
548         throw new IOException JavaDoc("Certificate too big");
549         }
550         ois.readFully(encoded);
551         ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(encoded);
552         try {
553         this.certs[i] = cf.generateCertificate(bais);
554         } catch (CertificateException ce) {
555         throw new IOException JavaDoc(ce.getMessage());
556         }
557         bais.close();
558     }
559
560     // Deserialize array of code signers (if any)
561
try {
562         this.signers = (CodeSigner JavaDoc[])ois.readObject();
563     } catch (IOException JavaDoc ioe) {
564         // no signers present
565
}
566     }
567
568     /*
569      * Convert an array of certificates to an array of code signers.
570      * The array of certificates is a concatenation of certificate chains
571      * where the initial certificate in each chain is the end-entity cert.
572      *
573      * @return An array of code signers or null if none are generated.
574      */

575     private CodeSigner JavaDoc[] convertCertArrayToSignerArray(
576     java.security.cert.Certificate JavaDoc[] certs) {
577
578     if (certs == null) {
579         return null;
580     }
581
582     try {
583         // Initialize certificate factory
584
if (factory == null) {
585         factory = CertificateFactory.getInstance("X.509");
586         }
587
588         // Iterate through all the certificates
589
int i = 0;
590         List JavaDoc signers = new ArrayList JavaDoc();
591         while (i < certs.length) {
592         List JavaDoc certChain = new ArrayList JavaDoc();
593         certChain.add(certs[i++]); // first cert is an end-entity cert
594
int j = i;
595
596         // Extract chain of certificates
597
// (loop while certs are not end-entity certs)
598
while (j < certs.length &&
599             certs[j] instanceof X509Certificate &&
600             ((X509Certificate)certs[j]).getBasicConstraints() != -1) {
601             certChain.add(certs[j]);
602             j++;
603         }
604         i = j;
605         CertPath certPath = factory.generateCertPath(certChain);
606         signers.add(new CodeSigner JavaDoc(certPath, null));
607         }
608
609         if (signers.isEmpty()) {
610         return null;
611         } else {
612         return (CodeSigner JavaDoc[])
613             signers.toArray(new CodeSigner JavaDoc[signers.size()]);
614         }
615
616     } catch (CertificateException e) {
617         return null; //TODO - may be better to throw an ex. here
618
}
619     }
620 }
621
622
Popular Tags