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. package java.security;
  8. import java.net.URL;
  9. import java.net.SocketPermission;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import java.util.Hashtable;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.IOException;
  15. import java.security.cert.*;
  16. /**
  17. *
  18. * <p>This class extends the concept of a codebase to
  19. * encapsulate not only the location (URL) but also the certificate chains
  20. * that were used to verify signed code originating from that location.
  21. *
  22. * @version 1.38, 12/19/03
  23. * @author Li Gong
  24. * @author Roland Schemers
  25. */
  26. public class CodeSource implements java.io.Serializable {
  27. private static final long serialVersionUID = 4977541819976013951L;
  28. /**
  29. * The code location.
  30. *
  31. * @serial
  32. */
  33. private URL location;
  34. /*
  35. * The code signers.
  36. */
  37. private transient CodeSigner[] signers = null;
  38. /*
  39. * The code signers. Certificate chains are concatenated.
  40. */
  41. private transient java.security.cert.Certificate certs[] = null;
  42. // cached SocketPermission used for matchLocation
  43. private transient SocketPermission sp;
  44. // for generating cert paths
  45. private transient CertificateFactory factory = null;
  46. /**
  47. * Constructs a CodeSource and associates it with the specified
  48. * location and set of certificates.
  49. *
  50. * @param url the location (URL).
  51. *
  52. * @param certs the certificate(s). It may be null. The contents of the
  53. * array are copied to protect against subsequent modification.
  54. */
  55. public CodeSource(URL url, java.security.cert.Certificate certs[]) {
  56. this.location = url;
  57. // Copy the supplied certs
  58. if (certs != null) {
  59. this.certs = (java.security.cert.Certificate[]) certs.clone();
  60. }
  61. }
  62. /**
  63. * Constructs a CodeSource and associates it with the specified
  64. * location and set of code signers.
  65. *
  66. * @param url the location (URL).
  67. * @param signers the code signers. It may be null. The contents of the
  68. * array are copied to protect against subsequent modification.
  69. *
  70. * @since 1.5
  71. */
  72. public CodeSource(URL url, CodeSigner[] signers) {
  73. this.location = url;
  74. // Copy the supplied signers
  75. if (signers != null) {
  76. this.signers = (CodeSigner[])signers.clone();
  77. }
  78. }
  79. /**
  80. * Returns the hash code value for this object.
  81. *
  82. * @return a hash code value for this object.
  83. */
  84. public int hashCode() {
  85. if (location != null)
  86. return location.hashCode();
  87. else
  88. return 0;
  89. }
  90. /**
  91. * Tests for equality between the specified object and this
  92. * object. Two CodeSource objects are considered equal if their
  93. * locations are of identical value and if their signer certificate
  94. * chains are of identical value. It is not required that
  95. * the certificate chains be in the same order.
  96. *
  97. * @param obj the object to test for equality with this object.
  98. *
  99. * @return true if the objects are considered equal, false otherwise.
  100. */
  101. public boolean equals(Object obj) {
  102. if (obj == this)
  103. return true;
  104. // objects types must be equal
  105. if (!(obj instanceof CodeSource))
  106. return false;
  107. CodeSource cs = (CodeSource) obj;
  108. // URLs must match
  109. if (location == null) {
  110. // if location is null, then cs.location must be null as well
  111. if (cs.location != null) return false;
  112. } else {
  113. // if location is not null, then it must equal cs.location
  114. if (!location.equals(cs.location)) return false;
  115. }
  116. // certs must match
  117. return matchCerts(cs, true);
  118. }
  119. /**
  120. * Returns the location associated with this CodeSource.
  121. *
  122. * @return the location (URL).
  123. */
  124. public final URL getLocation() {
  125. /* since URL is practically immutable, returning itself is not
  126. a security problem */
  127. return this.location;
  128. }
  129. /**
  130. * Returns the certificates associated with this CodeSource.
  131. * <p>
  132. * If this CodeSource object was created using the
  133. * {@link #CodeSource(URL url, CodeSigner[] signers)}
  134. * constructor then its certificate chains are extracted and used to
  135. * create an array of Certificate objects. Each signer certificate is
  136. * followed by its supporting certificate chain (which may be empty).
  137. * Each signer certificate and its supporting certificate chain is ordered
  138. * bottom-to-top (i.e., with the signer certificate first and the (root)
  139. * certificate authority last).
  140. *
  141. * @return A copy of the certificates array, or null if there is none.
  142. */
  143. public final java.security.cert.Certificate[] getCertificates() {
  144. if (certs != null) {
  145. return (java.security.cert.Certificate[]) certs.clone();
  146. } else if (signers != null) {
  147. // Convert the code signers to certs
  148. ArrayList certChains = new ArrayList();
  149. for (int i = 0; i < signers.length; i++) {
  150. certChains.addAll(
  151. signers[i].getSignerCertPath().getCertificates());
  152. }
  153. certs = (java.security.cert.Certificate[])
  154. certChains.toArray(
  155. new java.security.cert.Certificate[certChains.size()]);
  156. return (java.security.cert.Certificate[]) certs.clone();
  157. } else {
  158. return null;
  159. }
  160. }
  161. /**
  162. * Returns the code signers associated with this CodeSource.
  163. * <p>
  164. * If this CodeSource object was created using the
  165. * {@link #CodeSource(URL url, Certificate[] certs)}
  166. * constructor then its certificate chains are extracted and used to
  167. * create an array of CodeSigner objects. Note that only X.509 certificates
  168. * are examined - all other certificate types are ignored.
  169. *
  170. * @return A copy of the code signer array, or null if there is none.
  171. *
  172. * @since 1.5
  173. */
  174. public final CodeSigner[] getCodeSigners() {
  175. if (signers != null) {
  176. return (CodeSigner[]) signers.clone();
  177. } else if (certs != null) {
  178. // Convert the certs to code signers
  179. signers = convertCertArrayToSignerArray(certs);
  180. return (CodeSigner[]) signers.clone();
  181. } else {
  182. return null;
  183. }
  184. }
  185. /**
  186. * Returns true if this CodeSource object "implies" the specified CodeSource.
  187. * <P>
  188. * More specifically, this method makes the following checks, in order.
  189. * If any fail, it returns false. If they all succeed, it returns true.<p>
  190. * <ol>
  191. * <li> <i>codesource</i> must not be null.
  192. * <li> If this object's certificates are not null, then all
  193. * of this object's certificates must be present in <i>codesource</i>'s
  194. * certificates.
  195. * <li> If this object's location (getLocation()) is not null, then the
  196. * following checks are made against this object's location and
  197. * <i>codesource</i>'s:<p>
  198. * <ol>
  199. * <li> <i>codesource</i>'s location must not be null.
  200. *
  201. * <li> If this object's location
  202. * equals <i>codesource</i>'s location, then return true.
  203. *
  204. * <li> This object's protocol (getLocation().getProtocol()) must be
  205. * equal to <i>codesource</i>'s protocol.
  206. *
  207. * <li> If this object's host (getLocation().getHost()) is not null,
  208. * then the SocketPermission
  209. * constructed with this object's host must imply the
  210. * SocketPermission constructed with <i>codesource</i>'s host.
  211. *
  212. * <li> If this object's port (getLocation().getPort()) is not
  213. * equal to -1 (that is, if a port is specified), it must equal
  214. * <i>codesource</i>'s port.
  215. *
  216. * <li> If this object's file (getLocation().getFile()) doesn't equal
  217. * <i>codesource</i>'s file, then the following checks are made:
  218. * If this object's file ends with "/-",
  219. * then <i>codesource</i>'s file must start with this object's
  220. * file (exclusive the trailing "-").
  221. * If this object's file ends with a "/*",
  222. * then <i>codesource</i>'s file must start with this object's
  223. * file and must not have any further "/" separators.
  224. * If this object's file doesn't end with a "/",
  225. * then <i>codesource</i>'s file must match this object's
  226. * file with a '/' appended.
  227. *
  228. * <li> If this object's reference (getLocation().getRef()) is
  229. * not null, it must equal <i>codesource</i>'s reference.
  230. *
  231. * </ol>
  232. * </ol>
  233. * <p>
  234. * For example, the codesource objects with the following locations
  235. * and null certificates all imply
  236. * the codesource with the location "http://java.sun.com/classes/foo.jar"
  237. * and null certificates:
  238. * <pre>
  239. * http:
  240. * http://*.sun.com/classes/*
  241. * http://java.sun.com/classes/-
  242. * http://java.sun.com/classes/foo.jar
  243. * </pre>
  244. *
  245. * Note that if this CodeSource has a null location and a null
  246. * certificate chain, then it implies every other CodeSource.
  247. *
  248. * @param codesource CodeSource to compare against.
  249. *
  250. * @return true if the specified codesource is implied by this codesource,
  251. * false if not.
  252. */
  253. public boolean implies(CodeSource codesource)
  254. {
  255. if (codesource == null)
  256. return false;
  257. return matchCerts(codesource, false) && matchLocation(codesource);
  258. }
  259. /**
  260. * Returns true if all the certs in this
  261. * CodeSource are also in <i>that</i>.
  262. *
  263. * @param that the CodeSource to check against.
  264. * @param strict If true then a strict equality match is performed.
  265. * Otherwise a subset match is performed.
  266. */
  267. private boolean matchCerts(CodeSource that, boolean strict)
  268. {
  269. // match any key
  270. if (certs == null && signers == null)
  271. return true;
  272. // match no key
  273. if (that.certs == null && that.signers == null)
  274. return false;
  275. boolean match;
  276. // both have signers
  277. if (signers != null && that.signers != null) {
  278. if (strict && signers.length != that.signers.length) {
  279. return false;
  280. }
  281. for (int i = 0; i < signers.length; i++) {
  282. match = false;
  283. for (int j = 0; j < that.signers.length; j++) {
  284. if (signers[i].equals(that.signers[j])) {
  285. match = true;
  286. break;
  287. }
  288. }
  289. if (!match) return false;
  290. }
  291. return true;
  292. // both have certs
  293. } else {
  294. if (strict && certs.length != that.certs.length) {
  295. return false;
  296. }
  297. for (int i = 0; i < certs.length; i++) {
  298. match = false;
  299. for (int j = 0; j < that.certs.length; j++) {
  300. if (certs[i].equals(that.certs[j])) {
  301. match = true;
  302. break;
  303. }
  304. }
  305. if (!match) return false;
  306. }
  307. return true;
  308. }
  309. }
  310. /**
  311. * Returns true if two CodeSource's have the "same" location.
  312. *
  313. * @param that CodeSource to compare against
  314. */
  315. private boolean matchLocation(CodeSource that)
  316. {
  317. if (location == null) {
  318. return true;
  319. }
  320. if ((that == null) || (that.location == null))
  321. return false;
  322. if (location.equals(that.location))
  323. return true;
  324. if (!location.getProtocol().equals(that.location.getProtocol()))
  325. return false;
  326. String thisHost = location.getHost();
  327. String thatHost = that.location.getHost();
  328. if (thisHost != null) {
  329. if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
  330. ("".equals(thatHost) || "localhost".equals(thatHost))) {
  331. // ok
  332. } else if (!thisHost.equals(thatHost)) {
  333. if (thatHost == null) {
  334. return false;
  335. }
  336. if (this.sp == null) {
  337. this.sp = new SocketPermission(thisHost, "resolve");
  338. }
  339. if (that.sp == null) {
  340. that.sp = new SocketPermission(thatHost, "resolve");
  341. }
  342. if (!this.sp.implies(that.sp)) {
  343. return false;
  344. }
  345. }
  346. }
  347. if (location.getPort() != -1) {
  348. if (location.getPort() != that.location.getPort())
  349. return false;
  350. }
  351. if (location.getFile().endsWith("/-")) {
  352. // Matches the directory and (recursively) all files
  353. // and subdirectories contained in that directory.
  354. // For example, "/a/b/-" implies anything that starts with
  355. // "/a/b/"
  356. String thisPath = location.getFile().substring(0,
  357. location.getFile().length()-1);
  358. if (!that.location.getFile().startsWith(thisPath))
  359. return false;
  360. } else if (location.getFile().endsWith("/*")) {
  361. // Matches the directory and all the files contained in that
  362. // directory.
  363. // For example, "/a/b/*" implies anything that starts with
  364. // "/a/b/" but has no further slashes
  365. int last = that.location.getFile().lastIndexOf('/');
  366. if (last == -1)
  367. return false;
  368. String thisPath = location.getFile().substring(0,
  369. location.getFile().length()-1);
  370. String thatPath = that.location.getFile().substring(0, last+1);
  371. if (!thatPath.equals(thisPath))
  372. return false;
  373. } else {
  374. // Exact matches only.
  375. // For example, "/a/b" and "/a/b/" both imply "/a/b/"
  376. if ((!that.location.getFile().equals(location.getFile()))
  377. && (!that.location.getFile().equals(location.getFile()+"/"))) {
  378. return false;
  379. }
  380. }
  381. if (location.getRef() == null)
  382. return true;
  383. else
  384. return location.getRef().equals(that.location.getRef());
  385. }
  386. /**
  387. * Returns a string describing this CodeSource, telling its
  388. * URL and certificates.
  389. *
  390. * @return information about this CodeSource.
  391. */
  392. public String toString() {
  393. StringBuilder sb = new StringBuilder();
  394. sb.append("(");
  395. sb.append(this.location);
  396. if (this.certs != null && this.certs.length > 0) {
  397. for (int i = 0; i < this.certs.length; i++) {
  398. sb.append( " " + this.certs[i]);
  399. }
  400. } else if (this.signers != null && this.signers.length > 0) {
  401. for (int i = 0; i < this.signers.length; i++) {
  402. sb.append( " " + this.signers[i]);
  403. }
  404. } else {
  405. sb.append(" <no signer certificates>");
  406. }
  407. sb.append(")");
  408. return sb.toString();
  409. }
  410. /**
  411. * Writes this object out to a stream (i.e., serializes it).
  412. *
  413. * @serialData An initial <code>URL</code> is followed by an
  414. * <code>int</code> indicating the number of certificates to follow
  415. * (a value of "zero" denotes that there are no certificates associated
  416. * with this object).
  417. * Each certificate is written out starting with a <code>String</code>
  418. * denoting the certificate type, followed by an
  419. * <code>int</code> specifying the length of the certificate encoding,
  420. * followed by the certificate encoding itself which is written out as an
  421. * array of bytes. Finally, if any code signers are present then the array
  422. * of code signers is serialized and written out too.
  423. */
  424. private synchronized void writeObject(java.io.ObjectOutputStream oos)
  425. throws IOException
  426. {
  427. oos.defaultWriteObject(); // location
  428. // Serialize the array of certs
  429. if (certs == null || certs.length == 0) {
  430. oos.writeInt(0);
  431. } else {
  432. // write out the total number of certs
  433. oos.writeInt(certs.length);
  434. // write out each cert, including its type
  435. for (int i = 0; i < certs.length; i++) {
  436. java.security.cert.Certificate cert = certs[i];
  437. try {
  438. oos.writeUTF(cert.getType());
  439. byte[] encoded = cert.getEncoded();
  440. oos.writeInt(encoded.length);
  441. oos.write(encoded);
  442. } catch (CertificateEncodingException cee) {
  443. throw new IOException(cee.getMessage());
  444. }
  445. }
  446. }
  447. // Serialize the array of code signers (if any)
  448. if (signers != null && signers.length > 0) {
  449. oos.writeObject(signers);
  450. }
  451. }
  452. /**
  453. * Restores this object from a stream (i.e., deserializes it).
  454. */
  455. private synchronized void readObject(java.io.ObjectInputStream ois)
  456. throws IOException, ClassNotFoundException
  457. {
  458. CertificateFactory cf;
  459. Hashtable cfs = null;
  460. ois.defaultReadObject(); // location
  461. // process any new-style certs in the stream (if present)
  462. int size = ois.readInt();
  463. if (size > 0) {
  464. // we know of 3 different cert types: X.509, PGP, SDSI, which
  465. // could all be present in the stream at the same time
  466. cfs = new Hashtable(3);
  467. this.certs = new java.security.cert.Certificate[size];
  468. }
  469. for (int i = 0; i < size; i++) {
  470. // read the certificate type, and instantiate a certificate
  471. // factory of that type (reuse existing factory if possible)
  472. String certType = ois.readUTF();
  473. if (cfs.containsKey(certType)) {
  474. // reuse certificate factory
  475. cf = (CertificateFactory)cfs.get(certType);
  476. } else {
  477. // create new certificate factory
  478. try {
  479. cf = CertificateFactory.getInstance(certType);
  480. } catch (CertificateException ce) {
  481. throw new ClassNotFoundException
  482. ("Certificate factory for " + certType + " not found");
  483. }
  484. // store the certificate factory so we can reuse it later
  485. cfs.put(certType, cf);
  486. }
  487. // parse the certificate
  488. byte[] encoded = null;
  489. try {
  490. encoded = new byte[ois.readInt()];
  491. } catch (OutOfMemoryError oome) {
  492. throw new IOException("Certificate too big");
  493. }
  494. ois.readFully(encoded);
  495. ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
  496. try {
  497. this.certs[i] = cf.generateCertificate(bais);
  498. } catch (CertificateException ce) {
  499. throw new IOException(ce.getMessage());
  500. }
  501. bais.close();
  502. }
  503. // Deserialize array of code signers (if any)
  504. try {
  505. this.signers = (CodeSigner[])ois.readObject();
  506. } catch (IOException ioe) {
  507. // no signers present
  508. }
  509. }
  510. /*
  511. * Convert an array of certificates to an array of code signers.
  512. * The array of certificates is a concatenation of certificate chains
  513. * where the initial certificate in each chain is the end-entity cert.
  514. *
  515. * @return An array of code signers or null if none are generated.
  516. */
  517. private CodeSigner[] convertCertArrayToSignerArray(
  518. java.security.cert.Certificate[] certs) {
  519. if (certs == null) {
  520. return null;
  521. }
  522. try {
  523. // Initialize certificate factory
  524. if (factory == null) {
  525. factory = CertificateFactory.getInstance("X.509");
  526. }
  527. // Iterate through all the certificates
  528. int i = 0;
  529. List signers = new ArrayList();
  530. while (i < certs.length) {
  531. List certChain = new ArrayList();
  532. certChain.add(certs[i++]); // first cert is an end-entity cert
  533. int j = i;
  534. // Extract chain of certificates
  535. // (loop while certs are not end-entity certs)
  536. while (j < certs.length &&
  537. certs[j] instanceof X509Certificate &&
  538. ((X509Certificate)certs[j]).getBasicConstraints() != -1) {
  539. certChain.add(certs[j]);
  540. j++;
  541. }
  542. i = j;
  543. CertPath certPath = factory.generateCertPath(certChain);
  544. signers.add(new CodeSigner(certPath, null));
  545. }
  546. if (signers.isEmpty()) {
  547. return null;
  548. } else {
  549. return (CodeSigner[])
  550. signers.toArray(new CodeSigner[signers.size()]);
  551. }
  552. } catch (CertificateException e) {
  553. return null; //TODO - may be better to throw an ex. here
  554. }
  555. }
  556. }