Package java.security

Source Code of java.security.CodeSource

/*
* @(#)CodeSource.java  1.41 05/11/17
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.security;


import java.net.URL;
import java.net.SocketPermission;
import java.util.ArrayList;
import java.util.List;
import java.util.Hashtable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.*;

/**
*
* <p>This class extends the concept of a codebase to
* encapsulate not only the location (URL) but also the certificate chains
* that were used to verify signed code originating from that location.
*
* @version   1.41, 11/17/05
* @author Li Gong
* @author Roland Schemers
*/

public class CodeSource implements java.io.Serializable {

    private static final long serialVersionUID = 4977541819976013951L;

    /**
     * The code location.
     *
     * @serial
     */
    private URL location;

    /*
     * The code signers.
     */
    private transient CodeSigner[] signers = null;

    /*
     * The code signers. Certificate chains are concatenated.
     */
    private transient java.security.cert.Certificate certs[] = null;

    // cached SocketPermission used for matchLocation
    private transient SocketPermission sp;

    // for generating cert paths
    private transient CertificateFactory factory = null;

    /**
     * Constructs a CodeSource and associates it with the specified
     * location and set of certificates.
     *
     * @param url the location (URL).
     *
     * @param certs the certificate(s). It may be null. The contents of the
     * array are copied to protect against subsequent modification.
     */
    public CodeSource(URL url, java.security.cert.Certificate certs[]) {
  this.location = url;

  // Copy the supplied certs
  if (certs != null) {
      this.certs = (java.security.cert.Certificate[]) certs.clone();
  }
    }

    /**
     * Constructs a CodeSource and associates it with the specified
     * location and set of code signers.
     *
     * @param url the location (URL).
     * @param signers the code signers. It may be null. The contents of the
     * array are copied to protect against subsequent modification.
     *
     * @since 1.5
     */
    public CodeSource(URL url, CodeSigner[] signers) {
  this.location = url;

  // Copy the supplied signers
  if (signers != null) {
      this.signers = (CodeSigner[])signers.clone();
  }
    }

    /**
     * Returns the hash code value for this object.
     *
     * @return a hash code value for this object.
     */

    public int hashCode() {
  if (location != null)
      return location.hashCode();
  else
      return 0;
    }

    /**
     * Tests for equality between the specified object and this
     * object. Two CodeSource objects are considered equal if their
     * locations are of identical value and if their signer certificate
     * chains are of identical value. It is not required that
     * the certificate chains be in the same order.
     *
     * @param obj the object to test for equality with this object.
     *
     * @return true if the objects are considered equal, false otherwise.
     */
    public boolean equals(Object obj) {
  if (obj == this)
      return true;

  // objects types must be equal
  if (!(obj instanceof CodeSource))
      return false;

  CodeSource cs = (CodeSource) obj;

  // URLs must match
  if (location == null) {
      // if location is null, then cs.location must be null as well
      if (cs.location != null) return false;
  } else {
      // if location is not null, then it must equal cs.location
      if (!location.equals(cs.location)) return false;
  }

  // certs must match
  return matchCerts(cs, true);
    }

    /**
     * Returns the location associated with this CodeSource.
     *
     * @return the location (URL).
     */
    public final URL getLocation() {
  /* since URL is practically immutable, returning itself is not
           a security problem */
  return this.location;
    }

    /**
     * Returns the certificates associated with this CodeSource.
     * <p>
     * If this CodeSource object was created using the
     * {@link #CodeSource(URL url, CodeSigner[] signers)}
     * constructor then its certificate chains are extracted and used to
     * create an array of Certificate objects. Each signer certificate is
     * followed by its supporting certificate chain (which may be empty).
     * Each signer certificate and its supporting certificate chain is ordered
     * bottom-to-top (i.e., with the signer certificate first and the (root)
     * certificate authority last).
     *
     * @return A copy of the certificates array, or null if there is none.
     */
    public final java.security.cert.Certificate[] getCertificates() {
  if (certs != null) {
      return (java.security.cert.Certificate[]) certs.clone();

  } else if (signers != null) {
      // Convert the code signers to certs
      ArrayList certChains = new ArrayList();
      for (int i = 0; i < signers.length; i++) {
    certChains.addAll(
        signers[i].getSignerCertPath().getCertificates());
      }
      certs = (java.security.cert.Certificate[])
    certChains.toArray(
        new java.security.cert.Certificate[certChains.size()]);
      return (java.security.cert.Certificate[]) certs.clone();

  } else {
      return null;
  }
    }

    /**
     * Returns the code signers associated with this CodeSource.
     * <p>
     * If this CodeSource object was created using the
     * {@link #CodeSource(URL url, Certificate[] certs)}
     * constructor then its certificate chains are extracted and used to
     * create an array of CodeSigner objects. Note that only X.509 certificates
     * are examined - all other certificate types are ignored.
     *
     * @return A copy of the code signer array, or null if there is none.
     *
     * @since 1.5
     */
    public final CodeSigner[] getCodeSigners() {
  if (signers != null) {
      return (CodeSigner[]) signers.clone();

  } else if (certs != null) {
      // Convert the certs to code signers
      signers = convertCertArrayToSignerArray(certs);
      return (CodeSigner[]) signers.clone();
     
  } else {
      return null;
  }
    }

    /**
     * Returns true if this CodeSource object "implies" the specified CodeSource.
     * <P>
     * More specifically, this method makes the following checks, in order.
     * If any fail, it returns false. If they all succeed, it returns true.<p>
     * <ol>
     * <li> <i>codesource</i> must not be null.
     * <li> If this object's certificates are not null, then all
     * of this object's certificates must be present in <i>codesource</i>'s
     * certificates.
     * <li> If this object's location (getLocation()) is not null, then the
     * following checks are made against this object's location and
     * <i>codesource</i>'s:<p>
     *   <ol>
     *     <li>  <i>codesource</i>'s location must not be null.
     *
     *     <li>  If this object's location
     *           equals <i>codesource</i>'s location, then return true.
     *
     *     <li>  This object's protocol (getLocation().getProtocol()) must be
     *           equal to <i>codesource</i>'s protocol.
     *
     *     <li>  If this object's host (getLocation().getHost()) is not null, 
     *           then the SocketPermission
     *           constructed with this object's host must imply the
     *           SocketPermission constructed with <i>codesource</i>'s host.
     *
     *     <li>  If this object's port (getLocation().getPort()) is not
     *           equal to -1 (that is, if a port is specified), it must equal
     *           <i>codesource</i>'s port.
     *
     *     <li>  If this object's file (getLocation().getFile()) doesn't equal
     *           <i>codesource</i>'s file, then the following checks are made:
     *           If this object's file ends with "/-",
     *           then <i>codesource</i>'s file must start with this object's
     *           file (exclusive the trailing "-").
     *           If this object's file ends with a "/*",
     *           then <i>codesource</i>'s file must start with this object's
     *           file and must not have any further "/" separators.
     *           If this object's file doesn't end with a "/",
     *           then <i>codesource</i>'s file must match this object's
     *           file with a '/' appended.
     *
     *     <li>  If this object's reference (getLocation().getRef()) is
     *           not null, it must equal <i>codesource</i>'s reference.
     *
     *   </ol>
     * </ol>
     * <p>
     * For example, the codesource objects with the following locations
     * and null certificates all imply
     * the codesource with the location "http://java.sun.com/classes/foo.jar"
     * and null certificates:
     * <pre>
     *     http:
     *     http://*.sun.com/classes/*
     *     http://java.sun.com/classes/-
     *     http://java.sun.com/classes/foo.jar
     * </pre>
     *
     * Note that if this CodeSource has a null location and a null
     * certificate chain, then it implies every other CodeSource.
     *
     * @param codesource CodeSource to compare against.
     *
     * @return true if the specified codesource is implied by this codesource,
     * false if not. 
     */
    public boolean implies(CodeSource codesource)
    {
  if (codesource == null)
      return false;

  return matchCerts(codesource, false) && matchLocation(codesource);
    }

    /**
     * Returns true if all the certs in this
     * CodeSource are also in <i>that</i>.
     *
     * @param that the CodeSource to check against.
     * @param strict If true then a strict equality match is performed.
     *               Otherwise a subset match is performed.
     */
    private boolean matchCerts(CodeSource that, boolean strict)
    {
  boolean match;

  // match any key
  if (certs == null && signers == null) {
      if (strict) {
    return (that.certs == null && that.signers == null);
      } else {
    return true;
      }
  // both have signers
  } else if (signers != null && that.signers != null) {
      if (strict && signers.length != that.signers.length) {
    return false;
      }
      for (int i = 0; i < signers.length; i++) {
    match = false;
    for (int j = 0; j < that.signers.length; j++) {
        if (signers[i].equals(that.signers[j])) {
      match = true;
      break;
        }
    }
    if (!match) return false;
      }
      return true;

  // both have certs
  } else if (certs != null && that.certs != null) {
      if (strict && certs.length != that.certs.length) {
    return false;
      }
      for (int i = 0; i < certs.length; i++) {
    match = false;
    for (int j = 0; j < that.certs.length; j++) {
        if (certs[i].equals(that.certs[j])) {
      match = true;
      break;
        }
    }
    if (!match) return false;
      }
      return true;
  }

  return false;
    }


    /**
     * Returns true if two CodeSource's have the "same" location.
     *
     * @param that CodeSource to compare against
     */
    private boolean matchLocation(CodeSource that)
  {
      if (location == null) {
    return true;
      }

      if ((that == null) || (that.location == null))
    return false;

      if (location.equals(that.location))
    return true;

      if (!location.getProtocol().equals(that.location.getProtocol()))
    return false;

      String thisHost = location.getHost();
      String thatHost = that.location.getHost();

      if (thisHost != null) {
    if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
        ("".equals(thatHost) || "localhost".equals(thatHost))) {
        // ok
    } else if (!thisHost.equals(thatHost)) {
        if (thatHost == null) {
      return false;
        }
        if (this.sp == null) {
      this.sp = new SocketPermission(thisHost, "resolve");
        }
        if (that.sp == null) {
      that.sp = new SocketPermission(thatHost, "resolve");
        }
        if (!this.sp.implies(that.sp)) {
      return false;
        }
    }
      }

      if (location.getPort() != -1) {
    if (location.getPort() != that.location.getPort())
        return false;
      }

      if (location.getFile().endsWith("/-")) {
    // Matches the directory and (recursively) all files
    // and subdirectories contained in that directory.
    // For example, "/a/b/-" implies anything that starts with
    // "/a/b/"
    String thisPath = location.getFile().substring(0,
                                                location.getFile().length()-1);
    if (!that.location.getFile().startsWith(thisPath))
        return false;
      } else if (location.getFile().endsWith("/*")) {
    // Matches the directory and all the files contained in that
    // directory.
    // For example, "/a/b/*" implies anything that starts with
    // "/a/b/" but has no further slashes
    int last = that.location.getFile().lastIndexOf('/');
    if (last == -1)
        return false;
    String thisPath = location.getFile().substring(0,
                                                location.getFile().length()-1);
    String thatPath = that.location.getFile().substring(0, last+1);
    if (!thatPath.equals(thisPath))
        return false;
      } else {
    // Exact matches only.
    // For example, "/a/b" and "/a/b/" both imply "/a/b/"
    if ((!that.location.getFile().equals(location.getFile()))
    && (!that.location.getFile().equals(location.getFile()+"/"))) {
        return false;
    }
      }

      if (location.getRef() == null)
    return true;
      else
    return location.getRef().equals(that.location.getRef());
  }

    /**
     * Returns a string describing this CodeSource, telling its
     * URL and certificates.
     *
     * @return information about this CodeSource.
     */
    public String toString() {
  StringBuilder sb = new StringBuilder();
  sb.append("(");
  sb.append(this.location);

  if (this.certs != null && this.certs.length > 0) {
      for (int i = 0; i < this.certs.length; i++) {
    sb.append( " " + this.certs[i]);
      }

  } else if (this.signers != null && this.signers.length > 0) {
      for (int i = 0; i < this.signers.length; i++) {
    sb.append( " " + this.signers[i]);
      }
  } else {
      sb.append(" <no signer certificates>");
  }
  sb.append(")");
  return sb.toString();
    }

    /**
     * Writes this object out to a stream (i.e., serializes it).
     *
     * @serialData An initial <code>URL</code> is followed by an
     * <code>int</code> indicating the number of certificates to follow
     * (a value of "zero" denotes that there are no certificates associated
     * with this object).
     * Each certificate is written out starting with a <code>String</code>
     * denoting the certificate type, followed by an
     * <code>int</code> specifying the length of the certificate encoding,
     * followed by the certificate encoding itself which is written out as an
     * array of bytes. Finally, if any code signers are present then the array
     * of code signers is serialized and written out too.
     */
    private void writeObject(java.io.ObjectOutputStream oos)
        throws IOException
    {
  oos.defaultWriteObject(); // location

  // Serialize the array of certs
  if (certs == null || certs.length == 0) {
      oos.writeInt(0);
  } else {
      // write out the total number of certs
      oos.writeInt(certs.length);
      // write out each cert, including its type
      for (int i = 0; i < certs.length; i++) {
    java.security.cert.Certificate cert = certs[i];
    try {
        oos.writeUTF(cert.getType());
        byte[] encoded = cert.getEncoded();
        oos.writeInt(encoded.length);
        oos.write(encoded);
    } catch (CertificateEncodingException cee) {
        throw new IOException(cee.getMessage());
    }
      }
  }

  // Serialize the array of code signers (if any)
  if (signers != null && signers.length > 0) {
      oos.writeObject(signers);
  }
    }

    /**
     * Restores this object from a stream (i.e., deserializes it).
     */
    private void readObject(java.io.ObjectInputStream ois)
  throws IOException, ClassNotFoundException
    {
  CertificateFactory cf;
  Hashtable cfs = null;

  ois.defaultReadObject(); // location

  // process any new-style certs in the stream (if present)
  int size = ois.readInt();
  if (size > 0) {
      // we know of 3 different cert types: X.509, PGP, SDSI, which
      // could all be present in the stream at the same time
      cfs = new Hashtable(3);
      this.certs = new java.security.cert.Certificate[size];
  }

  for (int i = 0; i < size; i++) {
      // read the certificate type, and instantiate a certificate
      // factory of that type (reuse existing factory if possible)
      String certType = ois.readUTF();
      if (cfs.containsKey(certType)) {
    // reuse certificate factory
    cf = (CertificateFactory)cfs.get(certType);
      } else {
    // create new certificate factory
    try {
        cf = CertificateFactory.getInstance(certType);
    } catch (CertificateException ce) {
        throw new ClassNotFoundException
      ("Certificate factory for " + certType + " not found");
    }
    // store the certificate factory so we can reuse it later
    cfs.put(certType, cf);
      }
      // parse the certificate
      byte[] encoded = null;
      try {
    encoded = new byte[ois.readInt()];
      } catch (OutOfMemoryError oome) {
    throw new IOException("Certificate too big");
      }
      ois.readFully(encoded);
      ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
      try {
    this.certs[i] = cf.generateCertificate(bais);
      } catch (CertificateException ce) {
    throw new IOException(ce.getMessage());
      }
      bais.close();
  }

  // Deserialize array of code signers (if any)
  try {
      this.signers = (CodeSigner[])ois.readObject();
  } catch (IOException ioe) {
      // no signers present
  }
    }

    /*
     * Convert an array of certificates to an array of code signers.
     * The array of certificates is a concatenation of certificate chains
     * where the initial certificate in each chain is the end-entity cert.
     *
     * @return An array of code signers or null if none are generated.
     */
    private CodeSigner[] convertCertArrayToSignerArray(
   java.security.cert.Certificate[] certs) {

  if (certs == null) {
      return null;
  }

  try {
      // Initialize certificate factory
      if (factory == null) {
    factory = CertificateFactory.getInstance("X.509");
      }

      // Iterate through all the certificates
      int i = 0;
      List signers = new ArrayList();
      while (i < certs.length) {
    List certChain = new ArrayList();
    certChain.add(certs[i++]); // first cert is an end-entity cert
    int j = i;

    // Extract chain of certificates
    // (loop while certs are not end-entity certs)
    while (j < certs.length &&
        certs[j] instanceof X509Certificate &&
        ((X509Certificate)certs[j]).getBasicConstraints() != -1) {
        certChain.add(certs[j]);
        j++;
    }
    i = j;
    CertPath certPath = factory.generateCertPath(certChain);
    signers.add(new CodeSigner(certPath, null));
      }

      if (signers.isEmpty()) {
    return null;
      } else {
    return (CodeSigner[])
        signers.toArray(new CodeSigner[signers.size()]);
      }

  } catch (CertificateException e) {
      return null; //TODO - may be better to throw an ex. here
  }
    }
}
TOP

Related Classes of java.security.CodeSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.