Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/security/CodeSource.java
38829 views
/*1* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.security;262728import java.net.URL;29import java.net.SocketPermission;30import java.util.ArrayList;31import java.util.List;32import java.util.Hashtable;33import java.io.ByteArrayInputStream;34import java.io.IOException;35import java.security.cert.*;36import sun.misc.IOUtils;3738/**39*40* <p>This class extends the concept of a codebase to41* encapsulate not only the location (URL) but also the certificate chains42* that were used to verify signed code originating from that location.43*44* @author Li Gong45* @author Roland Schemers46*/4748public class CodeSource implements java.io.Serializable {4950private static final long serialVersionUID = 4977541819976013951L;5152/**53* The code location.54*55* @serial56*/57private URL location;5859/*60* The code signers.61*/62private transient CodeSigner[] signers = null;6364/*65* The code signers. Certificate chains are concatenated.66*/67private transient java.security.cert.Certificate certs[] = null;6869// cached SocketPermission used for matchLocation70private transient SocketPermission sp;7172// for generating cert paths73private transient CertificateFactory factory = null;7475/**76* Constructs a CodeSource and associates it with the specified77* 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 the82* array are copied to protect against subsequent modification.83*/84public CodeSource(URL url, java.security.cert.Certificate certs[]) {85this.location = url;8687// Copy the supplied certs88if (certs != null) {89this.certs = certs.clone();90}91}9293/**94* Constructs a CodeSource and associates it with the specified95* 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 the99* array are copied to protect against subsequent modification.100*101* @since 1.5102*/103public CodeSource(URL url, CodeSigner[] signers) {104this.location = url;105106// Copy the supplied signers107if (signers != null) {108this.signers = signers.clone();109}110}111112/**113* Returns the hash code value for this object.114*115* @return a hash code value for this object.116*/117@Override118public int hashCode() {119if (location != null)120return location.hashCode();121else122return 0;123}124125/**126* Tests for equality between the specified object and this127* object. Two CodeSource objects are considered equal if their128* locations are of identical value and if their signer certificate129* chains are of identical value. It is not required that130* 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@Override137public boolean equals(Object obj) {138if (obj == this)139return true;140141// objects types must be equal142if (!(obj instanceof CodeSource))143return false;144145CodeSource cs = (CodeSource) obj;146147// URLs must match148if (location == null) {149// if location is null, then cs.location must be null as well150if (cs.location != null) return false;151} else {152// if location is not null, then it must equal cs.location153if (!location.equals(cs.location)) return false;154}155156// certs must match157return matchCerts(cs, true);158}159160/**161* Returns the location associated with this CodeSource.162*163* @return the location (URL).164*/165public final URL getLocation() {166/* since URL is practically immutable, returning itself is not167a security problem */168return this.location;169}170171/**172* Returns the certificates associated with this CodeSource.173* <p>174* If this CodeSource object was created using the175* {@link #CodeSource(URL url, CodeSigner[] signers)}176* constructor then its certificate chains are extracted and used to177* create an array of Certificate objects. Each signer certificate is178* followed by its supporting certificate chain (which may be empty).179* Each signer certificate and its supporting certificate chain is ordered180* bottom-to-top (i.e., with the signer certificate first and the (root)181* certificate authority last).182*183* @return A copy of the certificates array, or null if there is none.184*/185public final java.security.cert.Certificate[] getCertificates() {186if (certs != null) {187return certs.clone();188189} else if (signers != null) {190// Convert the code signers to certs191ArrayList<java.security.cert.Certificate> certChains =192new ArrayList<>();193for (int i = 0; i < signers.length; i++) {194certChains.addAll(195signers[i].getSignerCertPath().getCertificates());196}197certs = certChains.toArray(198new java.security.cert.Certificate[certChains.size()]);199return certs.clone();200201} else {202return null;203}204}205206/**207* Returns the code signers associated with this CodeSource.208* <p>209* If this CodeSource object was created using the210* {@link #CodeSource(URL url, java.security.cert.Certificate[] certs)}211* constructor then its certificate chains are extracted and used to212* create an array of CodeSigner objects. Note that only X.509 certificates213* are examined - all other certificate types are ignored.214*215* @return A copy of the code signer array, or null if there is none.216*217* @since 1.5218*/219public final CodeSigner[] getCodeSigners() {220if (signers != null) {221return signers.clone();222223} else if (certs != null) {224// Convert the certs to code signers225signers = convertCertArrayToSignerArray(certs);226return signers.clone();227228} else {229return null;230}231}232233/**234* Returns true if this CodeSource object "implies" the specified CodeSource.235* <p>236* More specifically, this method makes the following checks.237* If any fail, it returns false. If they all succeed, it returns true.238* <ul>239* <li> <i>codesource</i> must not be null.240* <li> If this object's certificates are not null, then all241* of this object's certificates must be present in <i>codesource</i>'s242* certificates.243* <li> If this object's location (getLocation()) is not null, then the244* following checks are made against this object's location and245* <i>codesource</i>'s:246* <ul>247* <li> <i>codesource</i>'s location must not be null.248*249* <li> If this object's location250* equals <i>codesource</i>'s location, then return true.251*252* <li> This object's protocol (getLocation().getProtocol()) must be253* equal to <i>codesource</i>'s protocol, ignoring case.254*255* <li> If this object's host (getLocation().getHost()) is not null,256* then the SocketPermission257* constructed with this object's host must imply the258* SocketPermission constructed with <i>codesource</i>'s host.259*260* <li> If this object's port (getLocation().getPort()) is not261* equal to -1 (that is, if a port is specified), it must equal262* <i>codesource</i>'s port or default port263* (codesource.getLocation().getDefaultPort()).264*265* <li> If this object's file (getLocation().getFile()) doesn't equal266* <i>codesource</i>'s file, then the following checks are made:267* If this object's file ends with "/-",268* then <i>codesource</i>'s file must start with this object's269* file (exclusive the trailing "-").270* If this object's file ends with a "/*",271* then <i>codesource</i>'s file must start with this object's272* file and must not have any further "/" separators.273* If this object's file doesn't end with a "/",274* then <i>codesource</i>'s file must match this object's275* file with a '/' appended.276*277* <li> If this object's reference (getLocation().getRef()) is278* not null, it must equal <i>codesource</i>'s reference.279*280* </ul>281* </ul>282* <p>283* For example, the codesource objects with the following locations284* and null certificates all imply285* the codesource with the location "http://java.sun.com/classes/foo.jar"286* and null certificates:287* <pre>288* http:289* http://*.sun.com/classes/*290* http://java.sun.com/classes/-291* http://java.sun.com/classes/foo.jar292* </pre>293*294* Note that if this CodeSource has a null location and a null295* certificate chain, then it implies every other CodeSource.296*297* @param codesource CodeSource to compare against.298*299* @return true if the specified codesource is implied by this codesource,300* false if not.301*/302303public boolean implies(CodeSource codesource)304{305if (codesource == null)306return false;307308return matchCerts(codesource, false) && matchLocation(codesource);309}310311/**312* Returns true if all the certs in this313* CodeSource are also in <i>that</i>.314*315* @param that the CodeSource to check against.316* @param strict If true then a strict equality match is performed.317* Otherwise a subset match is performed.318*/319private boolean matchCerts(CodeSource that, boolean strict)320{321boolean match;322323// match any key324if (certs == null && signers == null) {325if (strict) {326return (that.certs == null && that.signers == null);327} else {328return true;329}330// both have signers331} else if (signers != null && that.signers != null) {332if (strict && signers.length != that.signers.length) {333return false;334}335for (int i = 0; i < signers.length; i++) {336match = false;337for (int j = 0; j < that.signers.length; j++) {338if (signers[i].equals(that.signers[j])) {339match = true;340break;341}342}343if (!match) return false;344}345return true;346347// both have certs348} else if (certs != null && that.certs != null) {349if (strict && certs.length != that.certs.length) {350return false;351}352for (int i = 0; i < certs.length; i++) {353match = false;354for (int j = 0; j < that.certs.length; j++) {355if (certs[i].equals(that.certs[j])) {356match = true;357break;358}359}360if (!match) return false;361}362return true;363}364365return false;366}367368369/**370* Returns true if two CodeSource's have the "same" location.371*372* @param that CodeSource to compare against373*/374private boolean matchLocation(CodeSource that) {375if (location == null)376return true;377378if ((that == null) || (that.location == null))379return false;380381if (location.equals(that.location))382return true;383384if (!location.getProtocol().equalsIgnoreCase(that.location.getProtocol()))385return false;386387int thisPort = location.getPort();388if (thisPort != -1) {389int thatPort = that.location.getPort();390int port = thatPort != -1 ? thatPort391: that.location.getDefaultPort();392if (thisPort != port)393return false;394}395396if (location.getFile().endsWith("/-")) {397// Matches the directory and (recursively) all files398// and subdirectories contained in that directory.399// For example, "/a/b/-" implies anything that starts with400// "/a/b/"401String thisPath = location.getFile().substring(0,402location.getFile().length()-1);403if (!that.location.getFile().startsWith(thisPath))404return false;405} else if (location.getFile().endsWith("/*")) {406// Matches the directory and all the files contained in that407// directory.408// For example, "/a/b/*" implies anything that starts with409// "/a/b/" but has no further slashes410int last = that.location.getFile().lastIndexOf('/');411if (last == -1)412return false;413String thisPath = location.getFile().substring(0,414location.getFile().length()-1);415String thatPath = that.location.getFile().substring(0, last+1);416if (!thatPath.equals(thisPath))417return false;418} else {419// Exact matches only.420// For example, "/a/b" and "/a/b/" both imply "/a/b/"421if ((!that.location.getFile().equals(location.getFile()))422&& (!that.location.getFile().equals(location.getFile()+"/"))) {423return false;424}425}426427if (location.getRef() != null428&& !location.getRef().equals(that.location.getRef())) {429return false;430}431432String thisHost = location.getHost();433String thatHost = that.location.getHost();434if (thisHost != null) {435if (("".equals(thisHost) || "localhost".equals(thisHost)) &&436("".equals(thatHost) || "localhost".equals(thatHost))) {437// ok438} else if (!thisHost.equals(thatHost)) {439if (thatHost == null) {440return false;441}442if (this.sp == null) {443this.sp = new SocketPermission(thisHost, "resolve");444}445if (that.sp == null) {446that.sp = new SocketPermission(thatHost, "resolve");447}448if (!this.sp.implies(that.sp)) {449return false;450}451}452}453// everything matches454return true;455}456457/**458* Returns a string describing this CodeSource, telling its459* URL and certificates.460*461* @return information about this CodeSource.462*/463@Override464public String toString() {465StringBuilder sb = new StringBuilder();466sb.append("(");467sb.append(this.location);468469if (this.certs != null && this.certs.length > 0) {470for (int i = 0; i < this.certs.length; i++) {471sb.append( " " + this.certs[i]);472}473474} else if (this.signers != null && this.signers.length > 0) {475for (int i = 0; i < this.signers.length; i++) {476sb.append( " " + this.signers[i]);477}478} else {479sb.append(" <no signer certificates>");480}481sb.append(")");482return sb.toString();483}484485/**486* Writes this object out to a stream (i.e., serializes it).487*488* @serialData An initial {@code URL} is followed by an489* {@code int} indicating the number of certificates to follow490* (a value of "zero" denotes that there are no certificates associated491* with this object).492* Each certificate is written out starting with a {@code String}493* denoting the certificate type, followed by an494* {@code int} specifying the length of the certificate encoding,495* followed by the certificate encoding itself which is written out as an496* array of bytes. Finally, if any code signers are present then the array497* of code signers is serialized and written out too.498*/499private void writeObject(java.io.ObjectOutputStream oos)500throws IOException501{502oos.defaultWriteObject(); // location503504// Serialize the array of certs505if (certs == null || certs.length == 0) {506oos.writeInt(0);507} else {508// write out the total number of certs509oos.writeInt(certs.length);510// write out each cert, including its type511for (int i = 0; i < certs.length; i++) {512java.security.cert.Certificate cert = certs[i];513try {514oos.writeUTF(cert.getType());515byte[] encoded = cert.getEncoded();516oos.writeInt(encoded.length);517oos.write(encoded);518} catch (CertificateEncodingException cee) {519throw new IOException(cee.getMessage());520}521}522}523524// Serialize the array of code signers (if any)525if (signers != null && signers.length > 0) {526oos.writeObject(signers);527}528}529530/**531* Restores this object from a stream (i.e., deserializes it).532*/533private void readObject(java.io.ObjectInputStream ois)534throws IOException, ClassNotFoundException535{536CertificateFactory cf;537Hashtable<String, CertificateFactory> cfs = null;538List<java.security.cert.Certificate> certList = null;539540ois.defaultReadObject(); // location541542// process any new-style certs in the stream (if present)543int size = ois.readInt();544if (size > 0) {545// we know of 3 different cert types: X.509, PGP, SDSI, which546// could all be present in the stream at the same time547cfs = new Hashtable<String, CertificateFactory>(3);548certList = new ArrayList<>(size > 20 ? 20 : size);549} else if (size < 0) {550throw new IOException("size cannot be negative");551}552553for (int i = 0; i < size; i++) {554// read the certificate type, and instantiate a certificate555// factory of that type (reuse existing factory if possible)556String certType = ois.readUTF();557if (cfs.containsKey(certType)) {558// reuse certificate factory559cf = cfs.get(certType);560} else {561// create new certificate factory562try {563cf = CertificateFactory.getInstance(certType);564} catch (CertificateException ce) {565throw new ClassNotFoundException566("Certificate factory for " + certType + " not found");567}568// store the certificate factory so we can reuse it later569cfs.put(certType, cf);570}571// parse the certificate572byte[] encoded = IOUtils.readExactlyNBytes(ois, ois.readInt());573ByteArrayInputStream bais = new ByteArrayInputStream(encoded);574try {575certList.add(cf.generateCertificate(bais));576} catch (CertificateException ce) {577throw new IOException(ce.getMessage());578}579bais.close();580}581582if (certList != null) {583this.certs = certList.toArray(584new java.security.cert.Certificate[size]);585}586// Deserialize array of code signers (if any)587try {588this.signers = ((CodeSigner[])ois.readObject()).clone();589} catch (IOException ioe) {590// no signers present591}592}593594/*595* Convert an array of certificates to an array of code signers.596* The array of certificates is a concatenation of certificate chains597* where the initial certificate in each chain is the end-entity cert.598*599* @return An array of code signers or null if none are generated.600*/601private CodeSigner[] convertCertArrayToSignerArray(602java.security.cert.Certificate[] certs) {603604if (certs == null) {605return null;606}607608try {609// Initialize certificate factory610if (factory == null) {611factory = CertificateFactory.getInstance("X.509");612}613614// Iterate through all the certificates615int i = 0;616List<CodeSigner> signers = new ArrayList<>();617while (i < certs.length) {618List<java.security.cert.Certificate> certChain =619new ArrayList<>();620certChain.add(certs[i++]); // first cert is an end-entity cert621int j = i;622623// Extract chain of certificates624// (loop while certs are not end-entity certs)625while (j < certs.length &&626certs[j] instanceof X509Certificate &&627((X509Certificate)certs[j]).getBasicConstraints() != -1) {628certChain.add(certs[j]);629j++;630}631i = j;632CertPath certPath = factory.generateCertPath(certChain);633signers.add(new CodeSigner(certPath, null));634}635636if (signers.isEmpty()) {637return null;638} else {639return signers.toArray(new CodeSigner[signers.size()]);640}641642} catch (CertificateException e) {643return null; //TODO - may be better to throw an ex. here644}645}646}647648649