1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.security; 27 28 29 import java.net.URL; 30 import java.net.SocketPermission; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Hashtable; 34 import java.io.ByteArrayInputStream; 35 import java.io.IOException; 36 import java.security.cert.*; 37 38 /** 39 * 40 * <p>This class extends the concept of a codebase to 41 * encapsulate not only the location (URL) but also the certificate chains 42 * that were used to verify signed code originating from that location. 43 * 44 * @author Li Gong 45 * @author Roland Schemers 46 */ 47 48 public class CodeSource implements java.io.Serializable { 49 50 private static final long serialVersionUID = 4977541819976013951L; 51 52 /** 53 * The code location. 54 * 55 * @serial 56 */ 57 private URL location; 58 59 /* 60 * The code signers. 61 */ 62 private transient CodeSigner[] signers = null; 63 64 /* 65 * The code signers. Certificate chains are concatenated. 66 */ 67 private transient java.security.cert.Certificate certs[] = null; 68 69 // cached SocketPermission used for matchLocation 70 private transient SocketPermission sp; 71 72 // for generating cert paths 73 private transient CertificateFactory factory = null; 74 75 /** 76 * Constructs a CodeSource and associates it with the specified 77 * location and set of certificates. 78 * 79 * @param url the location (URL). 80 * 81 * @param certs the certificate(s). It may be null. The contents of the 82 * array are copied to protect against subsequent modification. 83 */ 84 public CodeSource(URL url, java.security.cert.Certificate certs[]) { 85 this.location = url; 86 87 // Copy the supplied certs 88 if (certs != null) { 89 this.certs = certs.clone(); 90 } 91 } 92 93 /** 94 * Constructs a CodeSource and associates it with the specified 95 * location and set of code signers. 96 * 97 * @param url the location (URL). 98 * @param signers the code signers. It may be null. The contents of the 99 * array are copied to protect against subsequent modification. 100 * 101 * @since 1.5 102 */ 103 public CodeSource(URL url, CodeSigner[] signers) { 104 this.location = url; 105 106 // Copy the supplied signers 107 if (signers != null) { 108 this.signers = signers.clone(); 109 } 110 } 111 112 /** 113 * Returns the hash code value for this object. 114 * 115 * @return a hash code value for this object. 116 */ 117 @Override 118 public int hashCode() { 119 if (location != null) 120 return location.hashCode(); 121 else 122 return 0; 123 } 124 125 /** 126 * Tests for equality between the specified object and this 127 * object. Two CodeSource objects are considered equal if their 128 * locations are of identical value and if their signer certificate 129 * chains are of identical value. It is not required that 130 * the certificate chains be in the same order. 131 * 132 * @param obj the object to test for equality with this object. 133 * 134 * @return true if the objects are considered equal, false otherwise. 135 */ 136 @Override 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) return false; 151 } else { 152 // if location is not null, then it must equal cs.location 153 if (!location.equals(cs.location)) return false; 154 } 155 156 // certs must match 157 return matchCerts(cs, true); 158 } 159 160 /** 161 * Returns the location associated with this CodeSource. 162 * 163 * @return the location (URL). 164 */ 165 public final URL getLocation() { 166 /* since URL is practically immutable, returning itself is not 167 a security problem */ 168 return this.location; 169 } 170 171 /** 172 * Returns the certificates associated with this CodeSource. 173 * <p> 174 * If this CodeSource object was created using the 175 * {@link #CodeSource(URL url, CodeSigner[] signers)} 176 * constructor then its certificate chains are extracted and used to 177 * create an array of Certificate objects. Each signer certificate is 178 * followed by its supporting certificate chain (which may be empty). 179 * Each signer certificate and its supporting certificate chain is ordered 180 * bottom-to-top (i.e., with the signer certificate first and the (root) 181 * certificate authority last). 182 * 183 * @return A copy of the certificates array, or null if there is none. 184 */ 185 public final java.security.cert.Certificate[] getCertificates() { 186 if (certs != null) { 187 return certs.clone(); 188 189 } else if (signers != null) { 190 // Convert the code signers to certs 191 ArrayList<java.security.cert.Certificate> certChains = 192 new ArrayList<>(); 193 for (int i = 0; i < signers.length; i++) { 194 certChains.addAll( 195 signers[i].getSignerCertPath().getCertificates()); 196 } 197 certs = certChains.toArray( 198 new java.security.cert.Certificate[certChains.size()]); 199 return certs.clone(); 200 201 } else { 202 return null; 203 } 204 } 205 206 /** 207 * Returns the code signers associated with this CodeSource. 208 * <p> 209 * If this CodeSource object was created using the 210 * {@link #CodeSource(URL url, java.security.cert.Certificate[] certs)} 211 * constructor then its certificate chains are extracted and used to 212 * create an array of CodeSigner objects. Note that only X.509 certificates 213 * are examined - all other certificate types are ignored. 214 * 215 * @return A copy of the code signer array, or null if there is none. 216 * 217 * @since 1.5 218 */ 219 public final CodeSigner[] getCodeSigners() { 220 if (signers != null) { 221 return signers.clone(); 222 223 } else if (certs != null) { 224 // Convert the certs to code signers 225 signers = convertCertArrayToSignerArray(certs); 226 return signers.clone(); 227 228 } else { 229 return null; 230 } 231 } 232 233 /** 234 * Returns true if this CodeSource object "implies" the specified CodeSource. 235 * <p> 236 * More specifically, this method makes the following checks. 237 * If any fail, it returns false. If they all succeed, it returns true. 238 * <ul> 239 * <li> <i>codesource</i> must not be null. 240 * <li> If this object's certificates are not null, then all 241 * of this object's certificates must be present in <i>codesource</i>'s 242 * certificates. 243 * <li> If this object's location (getLocation()) is not null, then the 244 * following checks are made against this object's location and 245 * <i>codesource</i>'s: 246 * <ul> 247 * <li> <i>codesource</i>'s location must not be null. 248 * 249 * <li> If this object's location 250 * equals <i>codesource</i>'s location, then return true. 251 * 252 * <li> This object's protocol (getLocation().getProtocol()) must be 253 * equal to <i>codesource</i>'s protocol, ignoring case. 254 * 255 * <li> If this object's host (getLocation().getHost()) is not null, 256 * then the SocketPermission 257 * constructed with this object's host must imply the 258 * SocketPermission constructed with <i>codesource</i>'s host. 259 * 260 * <li> If this object's port (getLocation().getPort()) is not 261 * equal to -1 (that is, if a port is specified), it must equal 262 * <i>codesource</i>'s port or default port 263 * (codesource.getLocation().getDefaultPort()). 264 * 265 * <li> If this object's file (getLocation().getFile()) doesn't equal 266 * <i>codesource</i>'s file, then the following checks are made: 267 * If this object's file ends with "/-", 268 * then <i>codesource</i>'s file must start with this object's 269 * file (exclusive the trailing "-"). 270 * If this object's file ends with a "/*", 271 * then <i>codesource</i>'s file must start with this object's 272 * file and must not have any further "/" separators. 273 * If this object's file doesn't end with a "/", 274 * then <i>codesource</i>'s file must match this object's 275 * file with a '/' appended. 276 * 277 * <li> If this object's reference (getLocation().getRef()) is 278 * not null, it must equal <i>codesource</i>'s reference. 279 * 280 * </ul> 281 * </ul> 282 * <p> 283 * For example, the codesource objects with the following locations 284 * and null certificates all imply 285 * the codesource with the location "http://java.sun.com/classes/foo.jar" 286 * and null certificates: 287 * <pre> 288 * http: 289 * http://*.sun.com/classes/* 290 * http://java.sun.com/classes/- 291 * http://java.sun.com/classes/foo.jar 292 * </pre> 293 * 294 * Note that if this CodeSource has a null location and a null 295 * certificate chain, then it implies every other CodeSource. 296 * 297 * @param codesource CodeSource to compare against. 298 * 299 * @return true if the specified codesource is implied by this codesource, 300 * false if not. 301 */ 302 303 public boolean implies(CodeSource codesource) 304 { 305 if (codesource == null) 306 return false; 307 308 return matchCerts(codesource, false) && matchLocation(codesource); 309 } 310 311 /** 312 * Returns true if all the certs in this 313 * CodeSource are also in <i>that</i>. 314 * 315 * @param that the CodeSource to check against. 316 * @param strict If true then a strict equality match is performed. 317 * Otherwise a subset match is performed. 318 */ 319 private boolean matchCerts(CodeSource that, boolean strict) 320 { 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) return false; 344 } 345 return true; 346 347 // both have certs 348 } else if (certs != null && that.certs != null) { 349 if (strict && certs.length != that.certs.length) { 350 return false; 351 } 352 for (int i = 0; i < certs.length; i++) { 353 match = false; 354 for (int j = 0; j < that.certs.length; j++) { 355 if (certs[i].equals(that.certs[j])) { 356 match = true; 357 break; 358 } 359 } 360 if (!match) return false; 361 } 362 return true; 363 } 364 365 return false; 366 } 367 368 369 /** 370 * Returns true if two CodeSource's have the "same" location. 371 * 372 * @param that CodeSource to compare against 373 */ 374 private boolean matchLocation(CodeSource that) { 375 if (location == null) 376 return true; 377 378 if ((that == null) || (that.location == null)) 379 return false; 380 381 if (location.equals(that.location)) 382 return true; 383 384 if (!location.getProtocol().equalsIgnoreCase(that.location.getProtocol())) 385 return false; 386 387 int thisPort = location.getPort(); 388 if (thisPort != -1) { 389 int thatPort = that.location.getPort(); 390 int port = thatPort != -1 ? thatPort 391 : that.location.getDefaultPort(); 392 if (thisPort != port) 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 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 thisPath = location.getFile().substring(0, 414 location.getFile().length()-1); 415 String 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 && !location.getRef().equals(that.location.getRef())) { 429 return false; 430 } 431 432 String thisHost = location.getHost(); 433 String thatHost = that.location.getHost(); 434 if (thisHost != null) { 435 if (("".equals(thisHost) || "localhost".equals(thisHost)) && 436 ("".equals(thatHost) || "localhost".equals(thatHost))) { 437 // ok 438 } else if (!thisHost.equals(thatHost)) { 439 if (thatHost == null) { 440 return false; 441 } 442 if (this.sp == null) { 443 this.sp = new SocketPermission(thisHost, "resolve"); 444 } 445 if (that.sp == null) { 446 that.sp = new SocketPermission(thatHost, "resolve"); 447 } 448 if (!this.sp.implies(that.sp)) { 449 return false; 450 } 451 } 452 } 453 // everything matches 454 return true; 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 @Override 464 public String toString() { 465 StringBuilder sb = new StringBuilder(); 466 sb.append("("); 467 sb.append(this.location); 468 469 if (this.certs != null && this.certs.length > 0) { 470 for (int i = 0; i < this.certs.length; i++) { 471 sb.append(' ').append(this.certs[i]); 472 } 473 474 } else if (this.signers != null && this.signers.length > 0) { 475 for (int i = 0; i < this.signers.length; i++) { 476 sb.append(' ').append(this.signers[i]); 477 } 478 } else { 479 sb.append(" <no signer certificates>"); 480 } 481 sb.append(")"); 482 return sb.toString(); 483 } 484 485 /** 486 * Writes this object out to a stream (i.e., serializes it). 487 * 488 * @serialData An initial {@code URL} is followed by an 489 * {@code int} indicating the number of certificates to follow 490 * (a value of "zero" denotes that there are no certificates associated 491 * with this object). 492 * Each certificate is written out starting with a {@code String} 493 * denoting the certificate type, followed by an 494 * {@code int} specifying the length of the certificate encoding, 495 * followed by the certificate encoding itself which is written out as an 496 * array of bytes. Finally, if any code signers are present then the array 497 * of code signers is serialized and written out too. 498 */ 499 private void writeObject(java.io.ObjectOutputStream oos) 500 throws IOException 501 { 502 oos.defaultWriteObject(); // location 503 504 // Serialize the array of certs 505 if (certs == null || certs.length == 0) { 506 oos.writeInt(0); 507 } else { 508 // write out the total number of certs 509 oos.writeInt(certs.length); 510 // write out each cert, including its type 511 for (int i = 0; i < certs.length; i++) { 512 java.security.cert.Certificate cert = certs[i]; 513 try { 514 oos.writeUTF(cert.getType()); 515 byte[] encoded = cert.getEncoded(); 516 oos.writeInt(encoded.length); 517 oos.write(encoded); 518 } catch (CertificateEncodingException cee) { 519 throw new IOException(cee.getMessage()); 520 } 521 } 522 } 523 524 // Serialize the array of code signers (if any) 525 if (signers != null && signers.length > 0) { 526 oos.writeObject(signers); 527 } 528 } 529 530 /** 531 * Restores this object from a stream (i.e., deserializes it). 532 */ 533 private void readObject(java.io.ObjectInputStream ois) 534 throws IOException, ClassNotFoundException 535 { 536 CertificateFactory cf; 537 Hashtable<String, CertificateFactory> cfs = null; 538 539 ois.defaultReadObject(); // location 540 541 // process any new-style certs in the stream (if present) 542 int size = ois.readInt(); 543 if (size > 0) { 544 // we know of 3 different cert types: X.509, PGP, SDSI, which 545 // could all be present in the stream at the same time 546 cfs = new Hashtable<String, CertificateFactory>(3); 547 this.certs = new java.security.cert.Certificate[size]; 548 } 549 550 for (int i = 0; i < size; i++) { 551 // read the certificate type, and instantiate a certificate 552 // factory of that type (reuse existing factory if possible) 553 String certType = ois.readUTF(); 554 if (cfs.containsKey(certType)) { 555 // reuse certificate factory 556 cf = cfs.get(certType); 557 } else { 558 // create new certificate factory 559 try { 560 cf = CertificateFactory.getInstance(certType); 561 } catch (CertificateException ce) { 562 throw new ClassNotFoundException 563 ("Certificate factory for " + certType + " not found"); 564 } 565 // store the certificate factory so we can reuse it later 566 cfs.put(certType, cf); 567 } 568 // parse the certificate 569 byte[] encoded = null; 570 try { 571 encoded = new byte[ois.readInt()]; 572 } catch (OutOfMemoryError oome) { 573 throw new IOException("Certificate too big"); 574 } 575 ois.readFully(encoded); 576 ByteArrayInputStream bais = new ByteArrayInputStream(encoded); 577 try { 578 this.certs[i] = cf.generateCertificate(bais); 579 } catch (CertificateException ce) { 580 throw new IOException(ce.getMessage()); 581 } 582 bais.close(); 583 } 584 585 // Deserialize array of code signers (if any) 586 try { 587 this.signers = ((CodeSigner[])ois.readObject()).clone(); 588 } catch (IOException ioe) { 589 // no signers present 590 } 591 } 592 593 /* 594 * Convert an array of certificates to an array of code signers. 595 * The array of certificates is a concatenation of certificate chains 596 * where the initial certificate in each chain is the end-entity cert. 597 * 598 * @return An array of code signers or null if none are generated. 599 */ 600 private CodeSigner[] convertCertArrayToSignerArray( 601 java.security.cert.Certificate[] certs) { 602 603 if (certs == null) { 604 return null; 605 } 606 607 try { 608 // Initialize certificate factory 609 if (factory == null) { 610 factory = CertificateFactory.getInstance("X.509"); 611 } 612 613 // Iterate through all the certificates 614 int i = 0; 615 List<CodeSigner> signers = new ArrayList<>(); 616 while (i < certs.length) { 617 List<java.security.cert.Certificate> certChain = 618 new ArrayList<>(); 619 certChain.add(certs[i++]); // first cert is an end-entity cert 620 int j = i; 621 622 // Extract chain of certificates 623 // (loop while certs are not end-entity certs) 624 while (j < certs.length && 625 certs[j] instanceof X509Certificate && 626 ((X509Certificate)certs[j]).getBasicConstraints() != -1) { 627 certChain.add(certs[j]); 628 j++; 629 } 630 i = j; 631 CertPath certPath = factory.generateCertPath(certChain); 632 signers.add(new CodeSigner(certPath, null)); 633 } 634 635 if (signers.isEmpty()) { 636 return null; 637 } else { 638 return signers.toArray(new CodeSigner[signers.size()]); 639 } 640 641 } catch (CertificateException e) { 642 return null; //TODO - may be better to throw an ex. here 643 } 644 } 645 }