639 lines · 22.5 KB · Java
RawDownload
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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. * * @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 = 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 = 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 certs.clone();         } else if (signers != null) {            // Convert the code signers to certs            ArrayList<java.security.cert.Certificate> certChains =                        new ArrayList<>();            for (int i = 0; i < signers.length; i++) {                certChains.addAll(                    signers[i].getSignerCertPath().getCertificates());            }            certs = certChains.toArray(                        new java.security.cert.Certificate[certChains.size()]);            return 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 signers.clone();         } else if (certs != null) {            // Convert the certs to code signers            signers = convertCertArrayToSignerArray(certs);            return 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);

Lines 1–500 of 639.