001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.security;
027
028 import java.net.URL;
029 import java.net.SocketPermission;
030 import java.util.ArrayList;
031 import java.util.List;
032 import java.util.Hashtable;
033 import java.io.ByteArrayInputStream;
034 import java.io.IOException;
035 import java.security.cert.*;
036
037 /**
038 *
039 * <p>This class extends the concept of a codebase to
040 * encapsulate not only the location (URL) but also the certificate chains
041 * that were used to verify signed code originating from that location.
042 *
043 * @version 1.48, 05/05/07
044 * @author Li Gong
045 * @author Roland Schemers
046 */
047
048 public class CodeSource implements java.io.Serializable {
049
050 private static final long serialVersionUID = 4977541819976013951L;
051
052 /**
053 * The code location.
054 *
055 * @serial
056 */
057 private URL location;
058
059 /*
060 * The code signers.
061 */
062 private transient CodeSigner[] signers = null;
063
064 /*
065 * The code signers. Certificate chains are concatenated.
066 */
067 private transient java.security.cert.Certificate certs[] = null;
068
069 // cached SocketPermission used for matchLocation
070 private transient SocketPermission sp;
071
072 // for generating cert paths
073 private transient CertificateFactory factory = null;
074
075 /**
076 * Constructs a CodeSource and associates it with the specified
077 * location and set of certificates.
078 *
079 * @param url the location (URL).
080 *
081 * @param certs the certificate(s). It may be null. The contents of the
082 * array are copied to protect against subsequent modification.
083 */
084 public CodeSource(URL url, java.security.cert.Certificate certs[]) {
085 this .location = url;
086
087 // Copy the supplied certs
088 if (certs != null) {
089 this .certs = (java.security.cert.Certificate[]) certs
090 .clone();
091 }
092 }
093
094 /**
095 * Constructs a CodeSource and associates it with the specified
096 * location and set of code signers.
097 *
098 * @param url the location (URL).
099 * @param signers the code signers. It may be null. The contents of the
100 * array are copied to protect against subsequent modification.
101 *
102 * @since 1.5
103 */
104 public CodeSource(URL url, CodeSigner[] signers) {
105 this .location = url;
106
107 // Copy the supplied signers
108 if (signers != null) {
109 this .signers = (CodeSigner[]) signers.clone();
110 }
111 }
112
113 /**
114 * Returns the hash code value for this object.
115 *
116 * @return a hash code value for this object.
117 */
118
119 public int hashCode() {
120 if (location != null)
121 return location.hashCode();
122 else
123 return 0;
124 }
125
126 /**
127 * Tests for equality between the specified object and this
128 * object. Two CodeSource objects are considered equal if their
129 * locations are of identical value and if their signer certificate
130 * chains are of identical value. It is not required that
131 * the certificate chains be in the same order.
132 *
133 * @param obj the object to test for equality with this object.
134 *
135 * @return true if the objects are considered equal, false otherwise.
136 */
137 public boolean equals(Object obj) {
138 if (obj == this )
139 return true;
140
141 // objects types must be equal
142 if (!(obj instanceof CodeSource))
143 return false;
144
145 CodeSource cs = (CodeSource) obj;
146
147 // URLs must match
148 if (location == null) {
149 // if location is null, then cs.location must be null as well
150 if (cs.location != null)
151 return false;
152 } else {
153 // if location is not null, then it must equal cs.location
154 if (!location.equals(cs.location))
155 return false;
156 }
157
158 // certs must match
159 return matchCerts(cs, true);
160 }
161
162 /**
163 * Returns the location associated with this CodeSource.
164 *
165 * @return the location (URL).
166 */
167 public final URL getLocation() {
168 /* since URL is practically immutable, returning itself is not
169 a security problem */
170 return this .location;
171 }
172
173 /**
174 * Returns the certificates associated with this CodeSource.
175 * <p>
176 * If this CodeSource object was created using the
177 * {@link #CodeSource(URL url, CodeSigner[] signers)}
178 * constructor then its certificate chains are extracted and used to
179 * create an array of Certificate objects. Each signer certificate is
180 * followed by its supporting certificate chain (which may be empty).
181 * Each signer certificate and its supporting certificate chain is ordered
182 * bottom-to-top (i.e., with the signer certificate first and the (root)
183 * certificate authority last).
184 *
185 * @return A copy of the certificates array, or null if there is none.
186 */
187 public final java.security.cert.Certificate[] getCertificates() {
188 if (certs != null) {
189 return (java.security.cert.Certificate[]) certs.clone();
190
191 } else if (signers != null) {
192 // Convert the code signers to certs
193 ArrayList<java.security.cert.Certificate> certChains = new ArrayList<java.security.cert.Certificate>();
194 for (int i = 0; i < signers.length; i++) {
195 certChains.addAll(signers[i].getSignerCertPath()
196 .getCertificates());
197 }
198 certs = certChains
199 .toArray(new java.security.cert.Certificate[certChains
200 .size()]);
201 return certs.clone();
202
203 } else {
204 return null;
205 }
206 }
207
208 /**
209 * Returns the code signers associated with this CodeSource.
210 * <p>
211 * If this CodeSource object was created using the
212 * {@link #CodeSource(URL url, Certificate[] certs)}
213 * constructor then its certificate chains are extracted and used to
214 * create an array of CodeSigner objects. Note that only X.509 certificates
215 * are examined - all other certificate types are ignored.
216 *
217 * @return A copy of the code signer array, or null if there is none.
218 *
219 * @since 1.5
220 */
221 public final CodeSigner[] getCodeSigners() {
222 if (signers != null) {
223 return (CodeSigner[]) signers.clone();
224
225 } else if (certs != null) {
226 // Convert the certs to code signers
227 signers = convertCertArrayToSignerArray(certs);
228 return (CodeSigner[]) signers.clone();
229
230 } else {
231 return null;
232 }
233 }
234
235 /**
236 * Returns true if this CodeSource object "implies" the specified CodeSource.
237 * <P>
238 * More specifically, this method makes the following checks, in order.
239 * If any fail, it returns false. If they all succeed, it returns true.<p>
240 * <ol>
241 * <li> <i>codesource</i> must not be null.
242 * <li> If this object's certificates are not null, then all
243 * of this object's certificates must be present in <i>codesource</i>'s
244 * certificates.
245 * <li> If this object's location (getLocation()) is not null, then the
246 * following checks are made against this object's location and
247 * <i>codesource</i>'s:<p>
248 * <ol>
249 * <li> <i>codesource</i>'s location must not be null.
250 *
251 * <li> If this object's location
252 * equals <i>codesource</i>'s location, then return true.
253 *
254 * <li> This object's protocol (getLocation().getProtocol()) must be
255 * equal to <i>codesource</i>'s protocol.
256 *
257 * <li> If this object's host (getLocation().getHost()) is not null,
258 * then the SocketPermission
259 * constructed with this object's host must imply the
260 * SocketPermission constructed with <i>codesource</i>'s host.
261 *
262 * <li> If this object's port (getLocation().getPort()) is not
263 * equal to -1 (that is, if a port is specified), it must equal
264 * <i>codesource</i>'s port.
265 *
266 * <li> If this object's file (getLocation().getFile()) doesn't equal
267 * <i>codesource</i>'s file, then the following checks are made:
268 * If this object's file ends with "/-",
269 * then <i>codesource</i>'s file must start with this object's
270 * file (exclusive the trailing "-").
271 * If this object's file ends with a "/*",
272 * then <i>codesource</i>'s file must start with this object's
273 * file and must not have any further "/" separators.
274 * If this object's file doesn't end with a "/",
275 * then <i>codesource</i>'s file must match this object's
276 * file with a '/' appended.
277 *
278 * <li> If this object's reference (getLocation().getRef()) is
279 * not null, it must equal <i>codesource</i>'s reference.
280 *
281 * </ol>
282 * </ol>
283 * <p>
284 * For example, the codesource objects with the following locations
285 * and null certificates all imply
286 * the codesource with the location "http://java.sun.com/classes/foo.jar"
287 * and null certificates:
288 * <pre>
289 * http:
290 * http://*.sun.com/classes/*
291 * http://java.sun.com/classes/-
292 * http://java.sun.com/classes/foo.jar
293 * </pre>
294 *
295 * Note that if this CodeSource has a null location and a null
296 * certificate chain, then it implies every other CodeSource.
297 *
298 * @param codesource CodeSource to compare against.
299 *
300 * @return true if the specified codesource is implied by this codesource,
301 * false if not.
302 */
303
304 public boolean implies(CodeSource codesource) {
305 if (codesource == null)
306 return false;
307
308 return matchCerts(codesource, false)
309 && matchLocation(codesource);
310 }
311
312 /**
313 * Returns true if all the certs in this
314 * CodeSource are also in <i>that</i>.
315 *
316 * @param that the CodeSource to check against.
317 * @param strict If true then a strict equality match is performed.
318 * Otherwise a subset match is performed.
319 */
320 private boolean matchCerts(CodeSource that, boolean strict) {
321 boolean match;
322
323 // match any key
324 if (certs == null && signers == null) {
325 if (strict) {
326 return (that.certs == null && that.signers == null);
327 } else {
328 return true;
329 }
330 // both have signers
331 } else if (signers != null && that.signers != null) {
332 if (strict && signers.length != that.signers.length) {
333 return false;
334 }
335 for (int i = 0; i < signers.length; i++) {
336 match = false;
337 for (int j = 0; j < that.signers.length; j++) {
338 if (signers[i].equals(that.signers[j])) {
339 match = true;
340 break;
341 }
342 }
343 if (!match)
344 return false;
345 }
346 return true;
347
348 // both have certs
349 } else if (certs != null && that.certs != null) {
350 if (strict && certs.length != that.certs.length) {
351 return false;
352 }
353 for (int i = 0; i < certs.length; i++) {
354 match = false;
355 for (int j = 0; j < that.certs.length; j++) {
356 if (certs[i].equals(that.certs[j])) {
357 match = true;
358 break;
359 }
360 }
361 if (!match)
362 return false;
363 }
364 return true;
365 }
366
367 return false;
368 }
369
370 /**
371 * Returns true if two CodeSource's have the "same" location.
372 *
373 * @param that CodeSource to compare against
374 */
375 private boolean matchLocation(CodeSource that) {
376 if (location == null) {
377 return true;
378 }
379
380 if ((that == null) || (that.location == null))
381 return false;
382
383 if (location.equals(that.location))
384 return true;
385
386 if (!location.getProtocol().equals(that.location.getProtocol()))
387 return false;
388
389 String this Host = location.getHost();
390 String thatHost = that.location.getHost();
391
392 if (this Host != null) {
393 if (("".equals(this Host) || "localhost".equals(this Host))
394 && ("".equals(thatHost) || "localhost"
395 .equals(thatHost))) {
396 // ok
397 } else if (!this Host.equals(thatHost)) {
398 if (thatHost == null) {
399 return false;
400 }
401 if (this .sp == null) {
402 this .sp = new SocketPermission(this Host, "resolve");
403 }
404 if (that.sp == null) {
405 that.sp = new SocketPermission(thatHost, "resolve");
406 }
407 if (!this .sp.implies(that.sp)) {
408 return false;
409 }
410 }
411 }
412
413 if (location.getPort() != -1) {
414 if (location.getPort() != that.location.getPort())
415 return false;
416 }
417
418 if (location.getFile().endsWith("/-")) {
419 // Matches the directory and (recursively) all files
420 // and subdirectories contained in that directory.
421 // For example, "/a/b/-" implies anything that starts with
422 // "/a/b/"
423 String this Path = location.getFile().substring(0,
424 location.getFile().length() - 1);
425 if (!that.location.getFile().startsWith(this Path))
426 return false;
427 } else if (location.getFile().endsWith("/*")) {
428 // Matches the directory and all the files contained in that
429 // directory.
430 // For example, "/a/b/*" implies anything that starts with
431 // "/a/b/" but has no further slashes
432 int last = that.location.getFile().lastIndexOf('/');
433 if (last == -1)
434 return false;
435 String this Path = location.getFile().substring(0,
436 location.getFile().length() - 1);
437 String thatPath = that.location.getFile().substring(0,
438 last + 1);
439 if (!thatPath.equals(this Path))
440 return false;
441 } else {
442 // Exact matches only.
443 // For example, "/a/b" and "/a/b/" both imply "/a/b/"
444 if ((!that.location.getFile().equals(location.getFile()))
445 && (!that.location.getFile().equals(
446 location.getFile() + "/"))) {
447 return false;
448 }
449 }
450
451 if (location.getRef() == null)
452 return true;
453 else
454 return location.getRef().equals(that.location.getRef());
455 }
456
457 /**
458 * Returns a string describing this CodeSource, telling its
459 * URL and certificates.
460 *
461 * @return information about this CodeSource.
462 */
463 public String toString() {
464 StringBuilder sb = new StringBuilder();
465 sb.append("(");
466 sb.append(this .location);
467
468 if (this .certs != null && this .certs.length > 0) {
469 for (int i = 0; i < this .certs.length; i++) {
470 sb.append(" " + this .certs[i]);
471 }
472
473 } else if (this .signers != null && this .signers.length > 0) {
474 for (int i = 0; i < this .signers.length; i++) {
475 sb.append(" " + this .signers[i]);
476 }
477 } else {
478 sb.append(" <no signer certificates>");
479 }
480 sb.append(")");
481 return sb.toString();
482 }
483
484 /**
485 * Writes this object out to a stream (i.e., serializes it).
486 *
487 * @serialData An initial <code>URL</code> is followed by an
488 * <code>int</code> indicating the number of certificates to follow
489 * (a value of "zero" denotes that there are no certificates associated
490 * with this object).
491 * Each certificate is written out starting with a <code>String</code>
492 * denoting the certificate type, followed by an
493 * <code>int</code> specifying the length of the certificate encoding,
494 * followed by the certificate encoding itself which is written out as an
495 * array of bytes. Finally, if any code signers are present then the array
496 * of code signers is serialized and written out too.
497 */
498 private void writeObject(java.io.ObjectOutputStream oos)
499 throws IOException {
500 oos.defaultWriteObject(); // location
501
502 // Serialize the array of certs
503 if (certs == null || certs.length == 0) {
504 oos.writeInt(0);
505 } else {
506 // write out the total number of certs
507 oos.writeInt(certs.length);
508 // write out each cert, including its type
509 for (int i = 0; i < certs.length; i++) {
510 java.security.cert.Certificate cert = certs[i];
511 try {
512 oos.writeUTF(cert.getType());
513 byte[] encoded = cert.getEncoded();
514 oos.writeInt(encoded.length);
515 oos.write(encoded);
516 } catch (CertificateEncodingException cee) {
517 throw new IOException(cee.getMessage());
518 }
519 }
520 }
521
522 // Serialize the array of code signers (if any)
523 if (signers != null && signers.length > 0) {
524 oos.writeObject(signers);
525 }
526 }
527
528 /**
529 * Restores this object from a stream (i.e., deserializes it).
530 */
531 private void readObject(java.io.ObjectInputStream ois)
532 throws IOException, ClassNotFoundException {
533 CertificateFactory cf;
534 Hashtable<String, CertificateFactory> cfs = null;
535
536 ois.defaultReadObject(); // location
537
538 // process any new-style certs in the stream (if present)
539 int size = ois.readInt();
540 if (size > 0) {
541 // we know of 3 different cert types: X.509, PGP, SDSI, which
542 // could all be present in the stream at the same time
543 cfs = new Hashtable<String, CertificateFactory>(3);
544 this .certs = new java.security.cert.Certificate[size];
545 }
546
547 for (int i = 0; i < size; i++) {
548 // read the certificate type, and instantiate a certificate
549 // factory of that type (reuse existing factory if possible)
550 String certType = ois.readUTF();
551 if (cfs.containsKey(certType)) {
552 // reuse certificate factory
553 cf = cfs.get(certType);
554 } else {
555 // create new certificate factory
556 try {
557 cf = CertificateFactory.getInstance(certType);
558 } catch (CertificateException ce) {
559 throw new ClassNotFoundException(
560 "Certificate factory for " + certType
561 + " not found");
562 }
563 // store the certificate factory so we can reuse it later
564 cfs.put(certType, cf);
565 }
566 // parse the certificate
567 byte[] encoded = null;
568 try {
569 encoded = new byte[ois.readInt()];
570 } catch (OutOfMemoryError oome) {
571 throw new IOException("Certificate too big");
572 }
573 ois.readFully(encoded);
574 ByteArrayInputStream bais = new ByteArrayInputStream(
575 encoded);
576 try {
577 this .certs[i] = cf.generateCertificate(bais);
578 } catch (CertificateException ce) {
579 throw new IOException(ce.getMessage());
580 }
581 bais.close();
582 }
583
584 // Deserialize array of code signers (if any)
585 try {
586 this .signers = (CodeSigner[]) ois.readObject();
587 } catch (IOException ioe) {
588 // no signers present
589 }
590 }
591
592 /*
593 * Convert an array of certificates to an array of code signers.
594 * The array of certificates is a concatenation of certificate chains
595 * where the initial certificate in each chain is the end-entity cert.
596 *
597 * @return An array of code signers or null if none are generated.
598 */
599 private CodeSigner[] convertCertArrayToSignerArray(
600 java.security.cert.Certificate[] certs) {
601
602 if (certs == null) {
603 return null;
604 }
605
606 try {
607 // Initialize certificate factory
608 if (factory == null) {
609 factory = CertificateFactory.getInstance("X.509");
610 }
611
612 // Iterate through all the certificates
613 int i = 0;
614 List<CodeSigner> signers = new ArrayList<CodeSigner>();
615 while (i < certs.length) {
616 List<java.security.cert.Certificate> certChain = new ArrayList<java.security.cert.Certificate>();
617 certChain.add(certs[i++]); // first cert is an end-entity cert
618 int j = i;
619
620 // Extract chain of certificates
621 // (loop while certs are not end-entity certs)
622 while (j < certs.length
623 && certs[j] instanceof X509Certificate
624 && ((X509Certificate) certs[j])
625 .getBasicConstraints() != -1) {
626 certChain.add(certs[j]);
627 j++;
628 }
629 i = j;
630 CertPath certPath = factory.generateCertPath(certChain);
631 signers.add(new CodeSigner(certPath, null));
632 }
633
634 if (signers.isEmpty()) {
635 return null;
636 } else {
637 return signers.toArray(new CodeSigner[signers.size()]);
638 }
639
640 } catch (CertificateException e) {
641 return null; //TODO - may be better to throw an ex. here
642 }
643 }
644 }
|