Save This Page
Home » openjdk-7 » java » security » [javadoc | source]
    1   /*
    2    * Copyright (c) 1997, 2011, 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   
  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       public boolean equals(Object obj) {
  137           if (obj == this)
  138               return true;
  139   
  140           // objects types must be equal
  141           if (!(obj instanceof CodeSource))
  142               return false;
  143   
  144           CodeSource cs = (CodeSource) obj;
  145   
  146           // URLs must match
  147           if (location == null) {
  148               // if location is null, then cs.location must be null as well
  149               if (cs.location != null) return false;
  150           } else {
  151               // if location is not null, then it must equal cs.location
  152               if (!location.equals(cs.location)) return false;
  153           }
  154   
  155           // certs must match
  156           return matchCerts(cs, true);
  157       }
  158   
  159       /**
  160        * Returns the location associated with this CodeSource.
  161        *
  162        * @return the location (URL).
  163        */
  164       public final URL getLocation() {
  165           /* since URL is practically immutable, returning itself is not
  166              a security problem */
  167           return this.location;
  168       }
  169   
  170       /**
  171        * Returns the certificates associated with this CodeSource.
  172        * <p>
  173        * If this CodeSource object was created using the
  174        * {@link #CodeSource(URL url, CodeSigner[] signers)}
  175        * constructor then its certificate chains are extracted and used to
  176        * create an array of Certificate objects. Each signer certificate is
  177        * followed by its supporting certificate chain (which may be empty).
  178        * Each signer certificate and its supporting certificate chain is ordered
  179        * bottom-to-top (i.e., with the signer certificate first and the (root)
  180        * certificate authority last).
  181        *
  182        * @return A copy of the certificates array, or null if there is none.
  183        */
  184       public final java.security.cert.Certificate[] getCertificates() {
  185           if (certs != null) {
  186               return certs.clone();
  187   
  188           } else if (signers != null) {
  189               // Convert the code signers to certs
  190               ArrayList<java.security.cert.Certificate> certChains =
  191                           new ArrayList<>();
  192               for (int i = 0; i < signers.length; i++) {
  193                   certChains.addAll(
  194                       signers[i].getSignerCertPath().getCertificates());
  195               }
  196               certs = certChains.toArray(
  197                           new java.security.cert.Certificate[certChains.size()]);
  198               return certs.clone();
  199   
  200           } else {
  201               return null;
  202           }
  203       }
  204   
  205       /**
  206        * Returns the code signers associated with this CodeSource.
  207        * <p>
  208        * If this CodeSource object was created using the
  209        * {@link #CodeSource(URL url, Certificate[] certs)}
  210        * constructor then its certificate chains are extracted and used to
  211        * create an array of CodeSigner objects. Note that only X.509 certificates
  212        * are examined - all other certificate types are ignored.
  213        *
  214        * @return A copy of the code signer array, or null if there is none.
  215        *
  216        * @since 1.5
  217        */
  218       public final CodeSigner[] getCodeSigners() {
  219           if (signers != null) {
  220               return signers.clone();
  221   
  222           } else if (certs != null) {
  223               // Convert the certs to code signers
  224               signers = convertCertArrayToSignerArray(certs);
  225               return signers.clone();
  226   
  227           } else {
  228               return null;
  229           }
  230       }
  231   
  232       /**
  233        * Returns true if this CodeSource object "implies" the specified CodeSource.
  234        * <P>
  235        * More specifically, this method makes the following checks, in order.
  236        * If any fail, it returns false. If they all succeed, it returns true.<p>
  237        * <ol>
  238        * <li> <i>codesource</i> must not be null.
  239        * <li> If this object's certificates are not null, then all
  240        * of this object's certificates must be present in <i>codesource</i>'s
  241        * certificates.
  242        * <li> If this object's location (getLocation()) is not null, then the
  243        * following checks are made against this object's location and
  244        * <i>codesource</i>'s:<p>
  245        *   <ol>
  246        *     <li>  <i>codesource</i>'s location must not be null.
  247        *
  248        *     <li>  If this object's location
  249        *           equals <i>codesource</i>'s location, then return true.
  250        *
  251        *     <li>  This object's protocol (getLocation().getProtocol()) must be
  252        *           equal to <i>codesource</i>'s protocol.
  253        *
  254        *     <li>  If this object's host (getLocation().getHost()) is not null,
  255        *           then the SocketPermission
  256        *           constructed with this object's host must imply the
  257        *           SocketPermission constructed with <i>codesource</i>'s host.
  258        *
  259        *     <li>  If this object's port (getLocation().getPort()) is not
  260        *           equal to -1 (that is, if a port is specified), it must equal
  261        *           <i>codesource</i>'s port.
  262        *
  263        *     <li>  If this object's file (getLocation().getFile()) doesn't equal
  264        *           <i>codesource</i>'s file, then the following checks are made:
  265        *           If this object's file ends with "/-",
  266        *           then <i>codesource</i>'s file must start with this object's
  267        *           file (exclusive the trailing "-").
  268        *           If this object's file ends with a "/*",
  269        *           then <i>codesource</i>'s file must start with this object's
  270        *           file and must not have any further "/" separators.
  271        *           If this object's file doesn't end with a "/",
  272        *           then <i>codesource</i>'s file must match this object's
  273        *           file with a '/' appended.
  274        *
  275        *     <li>  If this object's reference (getLocation().getRef()) is
  276        *           not null, it must equal <i>codesource</i>'s reference.
  277        *
  278        *   </ol>
  279        * </ol>
  280        * <p>
  281        * For example, the codesource objects with the following locations
  282        * and null certificates all imply
  283        * the codesource with the location "http://java.sun.com/classes/foo.jar"
  284        * and null certificates:
  285        * <pre>
  286        *     http:
  287        *     http://*.sun.com/classes/*
  288        *     http://java.sun.com/classes/-
  289        *     http://java.sun.com/classes/foo.jar
  290        * </pre>
  291        *
  292        * Note that if this CodeSource has a null location and a null
  293        * certificate chain, then it implies every other CodeSource.
  294        *
  295        * @param codesource CodeSource to compare against.
  296        *
  297        * @return true if the specified codesource is implied by this codesource,
  298        * false if not.
  299        */
  300   
  301       public boolean implies(CodeSource codesource)
  302       {
  303           if (codesource == null)
  304               return false;
  305   
  306           return matchCerts(codesource, false) && matchLocation(codesource);
  307       }
  308   
  309       /**
  310        * Returns true if all the certs in this
  311        * CodeSource are also in <i>that</i>.
  312        *
  313        * @param that the CodeSource to check against.
  314        * @param strict If true then a strict equality match is performed.
  315        *               Otherwise a subset match is performed.
  316        */
  317       private boolean matchCerts(CodeSource that, boolean strict)
  318       {
  319           boolean match;
  320   
  321           // match any key
  322           if (certs == null && signers == null) {
  323               if (strict) {
  324                   return (that.certs == null && that.signers == null);
  325               } else {
  326                   return true;
  327               }
  328           // both have signers
  329           } else if (signers != null && that.signers != null) {
  330               if (strict && signers.length != that.signers.length) {
  331                   return false;
  332               }
  333               for (int i = 0; i < signers.length; i++) {
  334                   match = false;
  335                   for (int j = 0; j < that.signers.length; j++) {
  336                       if (signers[i].equals(that.signers[j])) {
  337                           match = true;
  338                           break;
  339                       }
  340                   }
  341                   if (!match) return false;
  342               }
  343               return true;
  344   
  345           // both have certs
  346           } else if (certs != null && that.certs != null) {
  347               if (strict && certs.length != that.certs.length) {
  348                   return false;
  349               }
  350               for (int i = 0; i < certs.length; i++) {
  351                   match = false;
  352                   for (int j = 0; j < that.certs.length; j++) {
  353                       if (certs[i].equals(that.certs[j])) {
  354                           match = true;
  355                           break;
  356                       }
  357                   }
  358                   if (!match) return false;
  359               }
  360               return true;
  361           }
  362   
  363           return false;
  364       }
  365   
  366   
  367       /**
  368        * Returns true if two CodeSource's have the "same" location.
  369        *
  370        * @param that CodeSource to compare against
  371        */
  372       private boolean matchLocation(CodeSource that)
  373           {
  374               if (location == null) {
  375                   return true;
  376               }
  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().equals(that.location.getProtocol()))
  385                   return false;
  386   
  387               String thisHost = location.getHost();
  388               String thatHost = that.location.getHost();
  389   
  390               if (thisHost != null) {
  391                   if (("".equals(thisHost) || "localhost".equals(thisHost)) &&
  392                       ("".equals(thatHost) || "localhost".equals(thatHost))) {
  393                       // ok
  394                   } else if (!thisHost.equals(thatHost)) {
  395                       if (thatHost == null) {
  396                           return false;
  397                       }
  398                       if (this.sp == null) {
  399                           this.sp = new SocketPermission(thisHost, "resolve");
  400                       }
  401                       if (that.sp == null) {
  402                           that.sp = new SocketPermission(thatHost, "resolve");
  403                       }
  404                       if (!this.sp.implies(that.sp)) {
  405                           return false;
  406                       }
  407                   }
  408               }
  409   
  410               if (location.getPort() != -1) {
  411                   if (location.getPort() != that.location.getPort())
  412                       return false;
  413               }
  414   
  415               if (location.getFile().endsWith("/-")) {
  416                   // Matches the directory and (recursively) all files
  417                   // and subdirectories contained in that directory.
  418                   // For example, "/a/b/-" implies anything that starts with
  419                   // "/a/b/"
  420                   String thisPath = location.getFile().substring(0,
  421                                                   location.getFile().length()-1);
  422                   if (!that.location.getFile().startsWith(thisPath))
  423                       return false;
  424               } else if (location.getFile().endsWith("/*")) {
  425                   // Matches the directory and all the files contained in that
  426                   // directory.
  427                   // For example, "/a/b/*" implies anything that starts with
  428                   // "/a/b/" but has no further slashes
  429                   int last = that.location.getFile().lastIndexOf('/');
  430                   if (last == -1)
  431                       return false;
  432                   String thisPath = location.getFile().substring(0,
  433                                                   location.getFile().length()-1);
  434                   String thatPath = that.location.getFile().substring(0, last+1);
  435                   if (!thatPath.equals(thisPath))
  436                       return false;
  437               } else {
  438                   // Exact matches only.
  439                   // For example, "/a/b" and "/a/b/" both imply "/a/b/"
  440                   if ((!that.location.getFile().equals(location.getFile()))
  441                   && (!that.location.getFile().equals(location.getFile()+"/"))) {
  442                       return false;
  443                   }
  444               }
  445   
  446               if (location.getRef() == null)
  447                   return true;
  448               else
  449                   return location.getRef().equals(that.location.getRef());
  450           }
  451   
  452       /**
  453        * Returns a string describing this CodeSource, telling its
  454        * URL and certificates.
  455        *
  456        * @return information about this CodeSource.
  457        */
  458       public String toString() {
  459           StringBuilder sb = new StringBuilder();
  460           sb.append("(");
  461           sb.append(this.location);
  462   
  463           if (this.certs != null && this.certs.length > 0) {
  464               for (int i = 0; i < this.certs.length; i++) {
  465                   sb.append( " " + this.certs[i]);
  466               }
  467   
  468           } else if (this.signers != null && this.signers.length > 0) {
  469               for (int i = 0; i < this.signers.length; i++) {
  470                   sb.append( " " + this.signers[i]);
  471               }
  472           } else {
  473               sb.append(" <no signer certificates>");
  474           }
  475           sb.append(")");
  476           return sb.toString();
  477       }
  478   
  479       /**
  480        * Writes this object out to a stream (i.e., serializes it).
  481        *
  482        * @serialData An initial <code>URL</code> is followed by an
  483        * <code>int</code> indicating the number of certificates to follow
  484        * (a value of "zero" denotes that there are no certificates associated
  485        * with this object).
  486        * Each certificate is written out starting with a <code>String</code>
  487        * denoting the certificate type, followed by an
  488        * <code>int</code> specifying the length of the certificate encoding,
  489        * followed by the certificate encoding itself which is written out as an
  490        * array of bytes. Finally, if any code signers are present then the array
  491        * of code signers is serialized and written out too.
  492        */
  493       private void writeObject(java.io.ObjectOutputStream oos)
  494           throws IOException
  495       {
  496           oos.defaultWriteObject(); // location
  497   
  498           // Serialize the array of certs
  499           if (certs == null || certs.length == 0) {
  500               oos.writeInt(0);
  501           } else {
  502               // write out the total number of certs
  503               oos.writeInt(certs.length);
  504               // write out each cert, including its type
  505               for (int i = 0; i < certs.length; i++) {
  506                   java.security.cert.Certificate cert = certs[i];
  507                   try {
  508                       oos.writeUTF(cert.getType());
  509                       byte[] encoded = cert.getEncoded();
  510                       oos.writeInt(encoded.length);
  511                       oos.write(encoded);
  512                   } catch (CertificateEncodingException cee) {
  513                       throw new IOException(cee.getMessage());
  514                   }
  515               }
  516           }
  517   
  518           // Serialize the array of code signers (if any)
  519           if (signers != null && signers.length > 0) {
  520               oos.writeObject(signers);
  521           }
  522       }
  523   
  524       /**
  525        * Restores this object from a stream (i.e., deserializes it).
  526        */
  527       private void readObject(java.io.ObjectInputStream ois)
  528           throws IOException, ClassNotFoundException
  529       {
  530           CertificateFactory cf;
  531           Hashtable<String, CertificateFactory> cfs = null;
  532   
  533           ois.defaultReadObject(); // location
  534   
  535           // process any new-style certs in the stream (if present)
  536           int size = ois.readInt();
  537           if (size > 0) {
  538               // we know of 3 different cert types: X.509, PGP, SDSI, which
  539               // could all be present in the stream at the same time
  540               cfs = new Hashtable<String, CertificateFactory>(3);
  541               this.certs = new java.security.cert.Certificate[size];
  542           }
  543   
  544           for (int i = 0; i < size; i++) {
  545               // read the certificate type, and instantiate a certificate
  546               // factory of that type (reuse existing factory if possible)
  547               String certType = ois.readUTF();
  548               if (cfs.containsKey(certType)) {
  549                   // reuse certificate factory
  550                   cf = cfs.get(certType);
  551               } else {
  552                   // create new certificate factory
  553                   try {
  554                       cf = CertificateFactory.getInstance(certType);
  555                   } catch (CertificateException ce) {
  556                       throw new ClassNotFoundException
  557                           ("Certificate factory for " + certType + " not found");
  558                   }
  559                   // store the certificate factory so we can reuse it later
  560                   cfs.put(certType, cf);
  561               }
  562               // parse the certificate
  563               byte[] encoded = null;
  564               try {
  565                   encoded = new byte[ois.readInt()];
  566               } catch (OutOfMemoryError oome) {
  567                   throw new IOException("Certificate too big");
  568               }
  569               ois.readFully(encoded);
  570               ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
  571               try {
  572                   this.certs[i] = cf.generateCertificate(bais);
  573               } catch (CertificateException ce) {
  574                   throw new IOException(ce.getMessage());
  575               }
  576               bais.close();
  577           }
  578   
  579           // Deserialize array of code signers (if any)
  580           try {
  581               this.signers = ((CodeSigner[])ois.readObject()).clone();
  582           } catch (IOException ioe) {
  583               // no signers present
  584           }
  585       }
  586   
  587       /*
  588        * Convert an array of certificates to an array of code signers.
  589        * The array of certificates is a concatenation of certificate chains
  590        * where the initial certificate in each chain is the end-entity cert.
  591        *
  592        * @return An array of code signers or null if none are generated.
  593        */
  594       private CodeSigner[] convertCertArrayToSignerArray(
  595           java.security.cert.Certificate[] certs) {
  596   
  597           if (certs == null) {
  598               return null;
  599           }
  600   
  601           try {
  602               // Initialize certificate factory
  603               if (factory == null) {
  604                   factory = CertificateFactory.getInstance("X.509");
  605               }
  606   
  607               // Iterate through all the certificates
  608               int i = 0;
  609               List<CodeSigner> signers = new ArrayList<>();
  610               while (i < certs.length) {
  611                   List<java.security.cert.Certificate> certChain =
  612                           new ArrayList<>();
  613                   certChain.add(certs[i++]); // first cert is an end-entity cert
  614                   int j = i;
  615   
  616                   // Extract chain of certificates
  617                   // (loop while certs are not end-entity certs)
  618                   while (j < certs.length &&
  619                       certs[j] instanceof X509Certificate &&
  620                       ((X509Certificate)certs[j]).getBasicConstraints() != -1) {
  621                       certChain.add(certs[j]);
  622                       j++;
  623                   }
  624                   i = j;
  625                   CertPath certPath = factory.generateCertPath(certChain);
  626                   signers.add(new CodeSigner(certPath, null));
  627               }
  628   
  629               if (signers.isEmpty()) {
  630                   return null;
  631               } else {
  632                   return signers.toArray(new CodeSigner[signers.size()]);
  633               }
  634   
  635           } catch (CertificateException e) {
  636               return null; //TODO - may be better to throw an ex. here
  637           }
  638       }
  639   }

Save This Page
Home » openjdk-7 » java » security » [javadoc | source]