0N/A/*
4955N/A * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.security;
0N/A
0N/A
0N/Aimport java.net.URL;
0N/Aimport java.net.SocketPermission;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.List;
0N/Aimport java.util.Hashtable;
0N/Aimport java.io.ByteArrayInputStream;
0N/Aimport java.io.IOException;
0N/Aimport java.security.cert.*;
0N/A
0N/A/**
0N/A *
0N/A * <p>This class extends the concept of a codebase to
0N/A * encapsulate not only the location (URL) but also the certificate chains
0N/A * that were used to verify signed code originating from that location.
0N/A *
0N/A * @author Li Gong
0N/A * @author Roland Schemers
0N/A */
0N/A
0N/Apublic class CodeSource implements java.io.Serializable {
0N/A
0N/A private static final long serialVersionUID = 4977541819976013951L;
0N/A
0N/A /**
0N/A * The code location.
0N/A *
0N/A * @serial
0N/A */
0N/A private URL location;
0N/A
0N/A /*
0N/A * The code signers.
0N/A */
0N/A private transient CodeSigner[] signers = null;
0N/A
0N/A /*
0N/A * The code signers. Certificate chains are concatenated.
0N/A */
0N/A private transient java.security.cert.Certificate certs[] = null;
0N/A
0N/A // cached SocketPermission used for matchLocation
0N/A private transient SocketPermission sp;
0N/A
0N/A // for generating cert paths
0N/A private transient CertificateFactory factory = null;
0N/A
0N/A /**
0N/A * Constructs a CodeSource and associates it with the specified
0N/A * location and set of certificates.
0N/A *
0N/A * @param url the location (URL).
0N/A *
0N/A * @param certs the certificate(s). It may be null. The contents of the
0N/A * array are copied to protect against subsequent modification.
0N/A */
0N/A public CodeSource(URL url, java.security.cert.Certificate certs[]) {
0N/A this.location = url;
0N/A
0N/A // Copy the supplied certs
0N/A if (certs != null) {
0N/A this.certs = certs.clone();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Constructs a CodeSource and associates it with the specified
0N/A * location and set of code signers.
0N/A *
0N/A * @param url the location (URL).
0N/A * @param signers the code signers. It may be null. The contents of the
0N/A * array are copied to protect against subsequent modification.
0N/A *
0N/A * @since 1.5
0N/A */
0N/A public CodeSource(URL url, CodeSigner[] signers) {
0N/A this.location = url;
0N/A
0N/A // Copy the supplied signers
0N/A if (signers != null) {
0N/A this.signers = signers.clone();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the hash code value for this object.
0N/A *
0N/A * @return a hash code value for this object.
0N/A */
0N/A
0N/A public int hashCode() {
0N/A if (location != null)
0N/A return location.hashCode();
0N/A else
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Tests for equality between the specified object and this
0N/A * object. Two CodeSource objects are considered equal if their
0N/A * locations are of identical value and if their signer certificate
0N/A * chains are of identical value. It is not required that
0N/A * the certificate chains be in the same order.
0N/A *
0N/A * @param obj the object to test for equality with this object.
0N/A *
0N/A * @return true if the objects are considered equal, false otherwise.
0N/A */
0N/A public boolean equals(Object obj) {
0N/A if (obj == this)
0N/A return true;
0N/A
0N/A // objects types must be equal
0N/A if (!(obj instanceof CodeSource))
0N/A return false;
0N/A
0N/A CodeSource cs = (CodeSource) obj;
0N/A
0N/A // URLs must match
0N/A if (location == null) {
0N/A // if location is null, then cs.location must be null as well
0N/A if (cs.location != null) return false;
0N/A } else {
0N/A // if location is not null, then it must equal cs.location
0N/A if (!location.equals(cs.location)) return false;
0N/A }
0N/A
0N/A // certs must match
0N/A return matchCerts(cs, true);
0N/A }
0N/A
0N/A /**
0N/A * Returns the location associated with this CodeSource.
0N/A *
0N/A * @return the location (URL).
0N/A */
0N/A public final URL getLocation() {
0N/A /* since URL is practically immutable, returning itself is not
0N/A a security problem */
0N/A return this.location;
0N/A }
0N/A
0N/A /**
0N/A * Returns the certificates associated with this CodeSource.
0N/A * <p>
0N/A * If this CodeSource object was created using the
0N/A * {@link #CodeSource(URL url, CodeSigner[] signers)}
0N/A * constructor then its certificate chains are extracted and used to
0N/A * create an array of Certificate objects. Each signer certificate is
0N/A * followed by its supporting certificate chain (which may be empty).
0N/A * Each signer certificate and its supporting certificate chain is ordered
0N/A * bottom-to-top (i.e., with the signer certificate first and the (root)
0N/A * certificate authority last).
0N/A *
0N/A * @return A copy of the certificates array, or null if there is none.
0N/A */
0N/A public final java.security.cert.Certificate[] getCertificates() {
0N/A if (certs != null) {
0N/A return certs.clone();
0N/A
0N/A } else if (signers != null) {
0N/A // Convert the code signers to certs
0N/A ArrayList<java.security.cert.Certificate> certChains =
3381N/A new ArrayList<>();
0N/A for (int i = 0; i < signers.length; i++) {
0N/A certChains.addAll(
0N/A signers[i].getSignerCertPath().getCertificates());
0N/A }
0N/A certs = certChains.toArray(
0N/A new java.security.cert.Certificate[certChains.size()]);
0N/A return certs.clone();
0N/A
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the code signers associated with this CodeSource.
0N/A * <p>
0N/A * If this CodeSource object was created using the
0N/A * {@link #CodeSource(URL url, Certificate[] certs)}
0N/A * constructor then its certificate chains are extracted and used to
0N/A * create an array of CodeSigner objects. Note that only X.509 certificates
0N/A * are examined - all other certificate types are ignored.
0N/A *
0N/A * @return A copy of the code signer array, or null if there is none.
0N/A *
0N/A * @since 1.5
0N/A */
0N/A public final CodeSigner[] getCodeSigners() {
0N/A if (signers != null) {
0N/A return signers.clone();
0N/A
0N/A } else if (certs != null) {
0N/A // Convert the certs to code signers
0N/A signers = convertCertArrayToSignerArray(certs);
0N/A return signers.clone();
0N/A
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this CodeSource object "implies" the specified CodeSource.
0N/A * <P>
0N/A * More specifically, this method makes the following checks, in order.
0N/A * If any fail, it returns false. If they all succeed, it returns true.<p>
0N/A * <ol>
0N/A * <li> <i>codesource</i> must not be null.
0N/A * <li> If this object's certificates are not null, then all
0N/A * of this object's certificates must be present in <i>codesource</i>'s
0N/A * certificates.
0N/A * <li> If this object's location (getLocation()) is not null, then the
0N/A * following checks are made against this object's location and
0N/A * <i>codesource</i>'s:<p>
0N/A * <ol>
0N/A * <li> <i>codesource</i>'s location must not be null.
0N/A *
0N/A * <li> If this object's location
0N/A * equals <i>codesource</i>'s location, then return true.
0N/A *
0N/A * <li> This object's protocol (getLocation().getProtocol()) must be
0N/A * equal to <i>codesource</i>'s protocol.
0N/A *
0N/A * <li> If this object's host (getLocation().getHost()) is not null,
0N/A * then the SocketPermission
0N/A * constructed with this object's host must imply the
0N/A * SocketPermission constructed with <i>codesource</i>'s host.
0N/A *
0N/A * <li> If this object's port (getLocation().getPort()) is not
0N/A * equal to -1 (that is, if a port is specified), it must equal
0N/A * <i>codesource</i>'s port.
0N/A *
0N/A * <li> If this object's file (getLocation().getFile()) doesn't equal
0N/A * <i>codesource</i>'s file, then the following checks are made:
0N/A * If this object's file ends with "/-",
0N/A * then <i>codesource</i>'s file must start with this object's
0N/A * file (exclusive the trailing "-").
0N/A * If this object's file ends with a "/*",
0N/A * then <i>codesource</i>'s file must start with this object's
0N/A * file and must not have any further "/" separators.
0N/A * If this object's file doesn't end with a "/",
0N/A * then <i>codesource</i>'s file must match this object's
0N/A * file with a '/' appended.
0N/A *
0N/A * <li> If this object's reference (getLocation().getRef()) is
0N/A * not null, it must equal <i>codesource</i>'s reference.
0N/A *
0N/A * </ol>
0N/A * </ol>
0N/A * <p>
0N/A * For example, the codesource objects with the following locations
0N/A * and null certificates all imply
0N/A * the codesource with the location "http://java.sun.com/classes/foo.jar"
0N/A * and null certificates:
0N/A * <pre>
0N/A * http:
0N/A * http://*.sun.com/classes/*
0N/A * http://java.sun.com/classes/-
0N/A * http://java.sun.com/classes/foo.jar
0N/A * </pre>
0N/A *
0N/A * Note that if this CodeSource has a null location and a null
0N/A * certificate chain, then it implies every other CodeSource.
0N/A *
0N/A * @param codesource CodeSource to compare against.
0N/A *
0N/A * @return true if the specified codesource is implied by this codesource,
0N/A * false if not.
0N/A */
0N/A
0N/A public boolean implies(CodeSource codesource)
0N/A {
0N/A if (codesource == null)
0N/A return false;
0N/A
0N/A return matchCerts(codesource, false) && matchLocation(codesource);
0N/A }
0N/A
0N/A /**
0N/A * Returns true if all the certs in this
0N/A * CodeSource are also in <i>that</i>.
0N/A *
0N/A * @param that the CodeSource to check against.
0N/A * @param strict If true then a strict equality match is performed.
0N/A * Otherwise a subset match is performed.
0N/A */
0N/A private boolean matchCerts(CodeSource that, boolean strict)
0N/A {
0N/A boolean match;
0N/A
0N/A // match any key
0N/A if (certs == null && signers == null) {
0N/A if (strict) {
0N/A return (that.certs == null && that.signers == null);
0N/A } else {
0N/A return true;
0N/A }
0N/A // both have signers
0N/A } else if (signers != null && that.signers != null) {
0N/A if (strict && signers.length != that.signers.length) {
0N/A return false;
0N/A }
0N/A for (int i = 0; i < signers.length; i++) {
0N/A match = false;
0N/A for (int j = 0; j < that.signers.length; j++) {
0N/A if (signers[i].equals(that.signers[j])) {
0N/A match = true;
0N/A break;
0N/A }
0N/A }
0N/A if (!match) return false;
0N/A }
0N/A return true;
0N/A
0N/A // both have certs
0N/A } else if (certs != null && that.certs != null) {
0N/A if (strict && certs.length != that.certs.length) {
0N/A return false;
0N/A }
0N/A for (int i = 0; i < certs.length; i++) {
0N/A match = false;
0N/A for (int j = 0; j < that.certs.length; j++) {
0N/A if (certs[i].equals(that.certs[j])) {
0N/A match = true;
0N/A break;
0N/A }
0N/A }
0N/A if (!match) return false;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A return false;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns true if two CodeSource's have the "same" location.
0N/A *
0N/A * @param that CodeSource to compare against
0N/A */
0N/A private boolean matchLocation(CodeSource that)
0N/A {
4955N/A if (location == null)
0N/A return true;
0N/A
0N/A if ((that == null) || (that.location == null))
0N/A return false;
0N/A
0N/A if (location.equals(that.location))
0N/A return true;
0N/A
4955N/A if (!location.getProtocol().equalsIgnoreCase(that.location.getProtocol()))
0N/A return false;
0N/A
0N/A if (location.getPort() != -1) {
0N/A if (location.getPort() != that.location.getPort())
0N/A return false;
0N/A }
0N/A
0N/A if (location.getFile().endsWith("/-")) {
0N/A // Matches the directory and (recursively) all files
0N/A // and subdirectories contained in that directory.
0N/A // For example, "/a/b/-" implies anything that starts with
0N/A // "/a/b/"
0N/A String thisPath = location.getFile().substring(0,
0N/A location.getFile().length()-1);
0N/A if (!that.location.getFile().startsWith(thisPath))
0N/A return false;
0N/A } else if (location.getFile().endsWith("/*")) {
0N/A // Matches the directory and all the files contained in that
0N/A // directory.
0N/A // For example, "/a/b/*" implies anything that starts with
0N/A // "/a/b/" but has no further slashes
0N/A int last = that.location.getFile().lastIndexOf('/');
0N/A if (last == -1)
0N/A return false;
0N/A String thisPath = location.getFile().substring(0,
0N/A location.getFile().length()-1);
0N/A String thatPath = that.location.getFile().substring(0, last+1);
0N/A if (!thatPath.equals(thisPath))
0N/A return false;
0N/A } else {
0N/A // Exact matches only.
0N/A // For example, "/a/b" and "/a/b/" both imply "/a/b/"
0N/A if ((!that.location.getFile().equals(location.getFile()))
0N/A && (!that.location.getFile().equals(location.getFile()+"/"))) {
0N/A return false;
0N/A }
0N/A }
0N/A
4955N/A if (location.getRef() != null) {
4955N/A if (!location.getRef().equals(that.location.getRef()))
4955N/A return false;
4955N/A }
4955N/A
4955N/A String thisHost = location.getHost();
4955N/A String thatHost = that.location.getHost();
4955N/A if (thisHost != null) {
4955N/A if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
4955N/A ("".equals(thatHost) || "localhost".equals(thatHost))) {
4955N/A // ok
4955N/A } else if (!thisHost.equalsIgnoreCase(thatHost)) {
4955N/A if (thatHost == null) {
4955N/A return false;
4955N/A }
4955N/A if (this.sp == null) {
4955N/A this.sp = new SocketPermission(thisHost, "resolve");
4955N/A }
4955N/A if (that.sp == null) {
4955N/A that.sp = new SocketPermission(thatHost, "resolve");
4955N/A }
4955N/A if (!this.sp.implies(that.sp)) {
4955N/A return false;
4955N/A }
4955N/A }
4955N/A }
4955N/A // everything matches
4955N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Returns a string describing this CodeSource, telling its
0N/A * URL and certificates.
0N/A *
0N/A * @return information about this CodeSource.
0N/A */
0N/A public String toString() {
0N/A StringBuilder sb = new StringBuilder();
0N/A sb.append("(");
0N/A sb.append(this.location);
0N/A
0N/A if (this.certs != null && this.certs.length > 0) {
0N/A for (int i = 0; i < this.certs.length; i++) {
0N/A sb.append( " " + this.certs[i]);
0N/A }
0N/A
0N/A } else if (this.signers != null && this.signers.length > 0) {
0N/A for (int i = 0; i < this.signers.length; i++) {
0N/A sb.append( " " + this.signers[i]);
0N/A }
0N/A } else {
0N/A sb.append(" <no signer certificates>");
0N/A }
0N/A sb.append(")");
0N/A return sb.toString();
0N/A }
0N/A
0N/A /**
0N/A * Writes this object out to a stream (i.e., serializes it).
0N/A *
0N/A * @serialData An initial <code>URL</code> is followed by an
0N/A * <code>int</code> indicating the number of certificates to follow
0N/A * (a value of "zero" denotes that there are no certificates associated
0N/A * with this object).
0N/A * Each certificate is written out starting with a <code>String</code>
0N/A * denoting the certificate type, followed by an
0N/A * <code>int</code> specifying the length of the certificate encoding,
0N/A * followed by the certificate encoding itself which is written out as an
0N/A * array of bytes. Finally, if any code signers are present then the array
0N/A * of code signers is serialized and written out too.
0N/A */
0N/A private void writeObject(java.io.ObjectOutputStream oos)
0N/A throws IOException
0N/A {
0N/A oos.defaultWriteObject(); // location
0N/A
0N/A // Serialize the array of certs
0N/A if (certs == null || certs.length == 0) {
0N/A oos.writeInt(0);
0N/A } else {
0N/A // write out the total number of certs
0N/A oos.writeInt(certs.length);
0N/A // write out each cert, including its type
0N/A for (int i = 0; i < certs.length; i++) {
0N/A java.security.cert.Certificate cert = certs[i];
0N/A try {
0N/A oos.writeUTF(cert.getType());
0N/A byte[] encoded = cert.getEncoded();
0N/A oos.writeInt(encoded.length);
0N/A oos.write(encoded);
0N/A } catch (CertificateEncodingException cee) {
0N/A throw new IOException(cee.getMessage());
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Serialize the array of code signers (if any)
0N/A if (signers != null && signers.length > 0) {
0N/A oos.writeObject(signers);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Restores this object from a stream (i.e., deserializes it).
0N/A */
0N/A private void readObject(java.io.ObjectInputStream ois)
0N/A throws IOException, ClassNotFoundException
0N/A {
0N/A CertificateFactory cf;
0N/A Hashtable<String, CertificateFactory> cfs = null;
0N/A
0N/A ois.defaultReadObject(); // location
0N/A
0N/A // process any new-style certs in the stream (if present)
0N/A int size = ois.readInt();
0N/A if (size > 0) {
0N/A // we know of 3 different cert types: X.509, PGP, SDSI, which
0N/A // could all be present in the stream at the same time
0N/A cfs = new Hashtable<String, CertificateFactory>(3);
0N/A this.certs = new java.security.cert.Certificate[size];
0N/A }
0N/A
0N/A for (int i = 0; i < size; i++) {
0N/A // read the certificate type, and instantiate a certificate
0N/A // factory of that type (reuse existing factory if possible)
0N/A String certType = ois.readUTF();
0N/A if (cfs.containsKey(certType)) {
0N/A // reuse certificate factory
0N/A cf = cfs.get(certType);
0N/A } else {
0N/A // create new certificate factory
0N/A try {
0N/A cf = CertificateFactory.getInstance(certType);
0N/A } catch (CertificateException ce) {
0N/A throw new ClassNotFoundException
0N/A ("Certificate factory for " + certType + " not found");
0N/A }
0N/A // store the certificate factory so we can reuse it later
0N/A cfs.put(certType, cf);
0N/A }
0N/A // parse the certificate
0N/A byte[] encoded = null;
0N/A try {
0N/A encoded = new byte[ois.readInt()];
0N/A } catch (OutOfMemoryError oome) {
0N/A throw new IOException("Certificate too big");
0N/A }
0N/A ois.readFully(encoded);
0N/A ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
0N/A try {
0N/A this.certs[i] = cf.generateCertificate(bais);
0N/A } catch (CertificateException ce) {
0N/A throw new IOException(ce.getMessage());
0N/A }
0N/A bais.close();
0N/A }
0N/A
0N/A // Deserialize array of code signers (if any)
0N/A try {
3755N/A this.signers = ((CodeSigner[])ois.readObject()).clone();
0N/A } catch (IOException ioe) {
0N/A // no signers present
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Convert an array of certificates to an array of code signers.
0N/A * The array of certificates is a concatenation of certificate chains
0N/A * where the initial certificate in each chain is the end-entity cert.
0N/A *
0N/A * @return An array of code signers or null if none are generated.
0N/A */
0N/A private CodeSigner[] convertCertArrayToSignerArray(
0N/A java.security.cert.Certificate[] certs) {
0N/A
0N/A if (certs == null) {
0N/A return null;
0N/A }
0N/A
0N/A try {
0N/A // Initialize certificate factory
0N/A if (factory == null) {
0N/A factory = CertificateFactory.getInstance("X.509");
0N/A }
0N/A
0N/A // Iterate through all the certificates
0N/A int i = 0;
3381N/A List<CodeSigner> signers = new ArrayList<>();
0N/A while (i < certs.length) {
0N/A List<java.security.cert.Certificate> certChain =
3381N/A new ArrayList<>();
0N/A certChain.add(certs[i++]); // first cert is an end-entity cert
0N/A int j = i;
0N/A
0N/A // Extract chain of certificates
0N/A // (loop while certs are not end-entity certs)
0N/A while (j < certs.length &&
0N/A certs[j] instanceof X509Certificate &&
0N/A ((X509Certificate)certs[j]).getBasicConstraints() != -1) {
0N/A certChain.add(certs[j]);
0N/A j++;
0N/A }
0N/A i = j;
0N/A CertPath certPath = factory.generateCertPath(certChain);
0N/A signers.add(new CodeSigner(certPath, null));
0N/A }
0N/A
0N/A if (signers.isEmpty()) {
0N/A return null;
0N/A } else {
0N/A return signers.toArray(new CodeSigner[signers.size()]);
0N/A }
0N/A
0N/A } catch (CertificateException e) {
0N/A return null; //TODO - may be better to throw an ex. here
0N/A }
0N/A }
0N/A}