CodeSourcepublic class CodeSource extends Object implements Serializable{@code CodeSource} encapsulates the location from where code is loaded and
the certificates that were used to verify that code. This information is used
by {@code SecureClassLoader} to define protection domains for loaded classes. |
Fields Summary |
---|
private static final long | serialVersionUID | private URL | location | private transient Certificate[] | certs | private transient CodeSigner[] | signers | private transient SocketPermission | sp | private transient CertificateFactory | factory |
Constructors Summary |
---|
public CodeSource(URL location, Certificate[] certs)Constructs a new instance of {@code CodeSource} with the specified
{@code URL} and the {@code Certificate}s.
this.location = location;
if (certs != null) {
this.certs = new Certificate[certs.length];
System.arraycopy(certs, 0, this.certs, 0, certs.length);
}
| public CodeSource(URL location, CodeSigner[] signers)Constructs a new instance of {@code CodeSource} with the specified
{@code URL} and the {@code CodeSigner}s.
this.location = location;
if (signers != null) {
this.signers = new CodeSigner[signers.length];
System.arraycopy(signers, 0, this.signers, 0, signers.length);
}
|
Methods Summary |
---|
public boolean | equals(java.lang.Object obj)Compares the specified object with this {@code CodeSource} for equality.
Returns {@code true} if the specified object is also an instance of
{@code CodeSource}, points to the same {@code URL} location and the two
code sources encapsulate the same {@code Certificate}s. The order of the
{@code Certificate}s is ignored by this method.
if (obj == this) {
return true;
}
if (!(obj instanceof CodeSource)) {
return false;
}
CodeSource that = (CodeSource) obj;
if (this.location != null) {
if (that.location == null) {
return false;
}
if (!this.location.equals(that.location)) {
return false;
}
} else if (that.location != null) {
return false;
}
// do not use this.certs, as we also need to take care about
// CodeSigners' certificates
Certificate[] thizCerts = getCertificatesNoClone();
Certificate[] thatCerts = that.getCertificatesNoClone();
if (!PolicyUtils.matchSubset(thizCerts, thatCerts)) {
return false;
}
if (!PolicyUtils.matchSubset(thatCerts, thizCerts)) {
return false;
}
return true;
| public final java.security.cert.Certificate[] | getCertificates()Returns the certificates of this {@code CodeSource}. If the
{@link #CodeSource(URL, CodeSigner[])} constructor was used to create
this instance, the certificates are obtained from the supplied signers.
External modifications of the returned {@code Certificate[]} has no
impact on this {@code CodeSource}.
getCertificatesNoClone();
if (certs == null) {
return null;
}
Certificate[] tmp = new Certificate[certs.length];
System.arraycopy(certs, 0, tmp, 0, certs.length);
return tmp;
| private java.security.cert.Certificate[] | getCertificatesNoClone()
if (certs != null) {
return certs;
}
if (signers == null) {
return null;
}
// Extract Certificates from the CodeSigner-s
ArrayList<Certificate> v = new ArrayList<Certificate>();
for (int i = 0; i < signers.length; i++) {
v.addAll(signers[i].getSignerCertPath().getCertificates());
}
certs = v.toArray(new Certificate[v.size()]);
return certs;
| public final java.security.CodeSigner[] | getCodeSigners()Returns the {@code CodeSigner}s of this {@code CodeSource}. If the
{@link #CodeSource(URL, Certificate[])} constructor was used to create
this instance, the signers are obtained from the supplied certificates.
Only X.509 certificates are analyzed.
if (signers != null) {
CodeSigner[] tmp = new CodeSigner[signers.length];
System.arraycopy(signers, 0, tmp, 0, tmp.length);
return tmp;
}
if(certs == null || factory != null){
// factory != null means we've done this exercise already.
return null;
}
X500Principal prevIssuer = null;
ArrayList<Certificate> list = new ArrayList<Certificate>(certs.length);
ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>();
// The presumption is that the chains of certificates are placed
// according to the CertPath agreement:
//
// the lowest certs first; the CAs are at the last
//
// So the following loop scans trough the certs and checks
// that every next certificate is an Issuer of the previous one.
// Any certificate that is not an Issuer of the previous one starts a
// new chain (== a new CertPath)
for (int i = 0; i < certs.length; i++) {
if (!(certs[i] instanceof X509Certificate)) {
// Only X509Certificate-s are taken into account - see API spec.
continue;
}
X509Certificate x509 = (X509Certificate) certs[i];
if (prevIssuer == null) {
// start a very first chain
prevIssuer = x509.getIssuerX500Principal();
list.add(x509);
} else {
X500Principal subj = x509.getSubjectX500Principal();
if (!prevIssuer.equals(subj)) {
// Ok, this ends the previous chain,
// so transform this one into CertPath ...
CertPath cpath = makeCertPath(list);
if (cpath != null) {
asigners.add(new CodeSigner(cpath, null));
}
// ... and start a new one
list.clear();
}// else { it's still the same chain }
prevIssuer = x509.getSubjectX500Principal();
list.add(x509);
}
}
if (!list.isEmpty()) {
CertPath cpath = makeCertPath(list);
if (cpath != null) {
asigners.add(new CodeSigner(cpath, null));
}
}
if (asigners.isEmpty()) {
// 'signers' is 'null' already
return null;
}
signers = new CodeSigner[asigners.size()];
asigners.toArray(signers);
CodeSigner[] tmp = new CodeSigner[asigners.size()];
System.arraycopy(signers, 0, tmp, 0, tmp.length);
return tmp;
| public final java.net.URL | getLocation()Returns the location of this {@code CodeSource}.
return location;
| public int | hashCode()Returns the hash code value for this {@code CodeSource}.
Returns the same hash code for {@code CodeSource}s that are
equal to each other as required by the general contract of
{@link Object#hashCode}.
//
// hashCode() is undocumented there. Should we also use certs[i] to
// compute the hash ?
// for now, I don't take certs[] into account
return location == null ? 0 : location.hashCode();
| public boolean | implies(java.security.CodeSource cs)Indicates whether the specified code source is implied by this {@code
CodeSource}. Returns {@code true} if all of the following conditions are
{@code true}, otherwise {@code false}:
- {@code cs} is not {@code null}
- if this {@code CodeSource} has associated certificates, all
certificates are present in {@code cs}. The certificates are extracted
from the signers if signers are present.
- if this {@code CodeSource}'s location is not {@code null}, the
following conditions are checked
- this {@code CodeSource}'s location is not {@code null}
- this {@code CodeSource}'s location protocol is equal to {@code cs}'s
location protocol
- if this {@code CodeSource}'s location host is not {@code null}, the
following conditions are checked
- {@code cs}'s host is not {@code null}
- the {@link SocketPermission} of this {@code CodeSource}'s location
host implies the {@code SocketPermission} of {@code cs}'s location host
- if this {@code CodeSource}'s location port != -1 the port of {@code
cs}'s location is equal to this {@code CodeSource}'s location port
- this {@code CodeSource}'s location file matches {@code cs}'s file
whereas special wildcard matching applies as described below
- this {@code CodeSource}'s location reference is equal to to {@code
cs}'s location reference
Note: If this {@code CodeSource} has a {@code null} location and not any
certificates, this method returns {@code true}.
Matching rules for the {@code CodeSource}'s location file:
- if this {@code CodeSource}'s location file ends with {@code "/-"},
then {@code cs}'s file must start with {@code CodeSource}'s location file
(exclusive the trailing '-')
- if this {@code CodeSource}'s location file ends with {@code "/*"},
then {@code cs}'s file must start with {@code CodeSource}'s location file
(exclusive the trailing '*') and must not have any further '/'
- if this {@code CodeSource}'s location file ends with {@code "/"},
then {@code cs}'s file must start with {@code CodeSource}'s location file
- if this {@code CodeSource}'s location file does not end with {@code
"/"}, then {@code cs}'s file must start with {@code CodeSource}'s
location file with the '/' appended to it.
Examples for locations that imply the location
"http://code.google.com/android/security.apk":
http:
http://*/android/*
http://*.google.com/android/*
http://code.google.com/android/-
http://code.google.com/android/security.apk
//
// Here, javadoc:N refers to the appropriate item in the API spec for
// the CodeSource.implies()
// The info was taken from the 1.5 final API spec
// javadoc:1
if (cs == null) {
return false;
}
// javadoc:2
// with a comment: the javadoc says only about certificates and does
// not explicitly mention CodeSigners' certs.
// It seems more convenient to use getCerts() to get the real
// certificates - with a certificates got form the signers
Certificate[] thizCerts = getCertificatesNoClone();
if (thizCerts != null) {
Certificate[] thatCerts = cs.getCertificatesNoClone();
if (thatCerts == null
|| !PolicyUtils.matchSubset(thizCerts, thatCerts)) {
return false;
}
}
// javadoc:3
if (this.location != null) {
//javadoc:3.1
if (cs.location == null) {
return false;
}
//javadoc:3.2
if (this.location.equals(cs.location)) {
return true;
}
//javadoc:3.3
if (!this.location.getProtocol().equals(cs.location.getProtocol())) {
return false;
}
//javadoc:3.4
String thisHost = this.location.getHost();
if (thisHost != null) {
String thatHost = cs.location.getHost();
if (thatHost == null) {
return false;
}
// 1. According to the spec, an empty string will be considered
// as "localhost" in the SocketPermission
// 2. 'file://' URLs will have an empty getHost()
// so, let's make a special processing of localhost-s, I do
// believe this'll improve performance of file:// code sources
//
// Don't have to evaluate both the boolean-s each time.
// It's better to evaluate them directly under if() statement.
//
// boolean thisIsLocalHost = thisHost.length() == 0 || "localhost".equals(thisHost);
// boolean thatIsLocalHost = thatHost.length() == 0 || "localhost".equals(thatHost);
//
// if( !(thisIsLocalHost && thatIsLocalHost) &&
// !thisHost.equals(thatHost)) {
if (!((thisHost.length() == 0 || "localhost".equals(thisHost)) && (thatHost //$NON-NLS-1$
.length() == 0 || "localhost".equals(thatHost))) //$NON-NLS-1$
&& !thisHost.equals(thatHost)) {
// Obvious, but very slow way....
//
// SocketPermission thisPerm = new SocketPermission(
// this.location.getHost(), "resolve");
// SocketPermission thatPerm = new SocketPermission(
// cs.location.getHost(), "resolve");
// if (!thisPerm.implies(thatPerm)) {
// return false;
// }
//
// let's cache it:
if (this.sp == null) {
this.sp = new SocketPermission(thisHost, "resolve"); //$NON-NLS-1$
}
if (cs.sp == null) {
cs.sp = new SocketPermission(thatHost, "resolve"); //$NON-NLS-1$
}
if (!this.sp.implies(cs.sp)) {
return false;
}
} // if( ! this.location.getHost().equals(cs.location.getHost())
} // if (this.location.getHost() != null)
//javadoc:3.5
if (this.location.getPort() != -1) {
if (this.location.getPort() != cs.location.getPort()) {
return false;
}
}
//javadoc:3.6
String thisFile = this.location.getFile();
String thatFile = cs.location.getFile();
if (thisFile.endsWith("/-")) { //javadoc:3.6."/-" //$NON-NLS-1$
if (!thatFile.startsWith(thisFile.substring(0, thisFile
.length() - 2))) {
return false;
}
} else if (thisFile.endsWith("/*")) { //javadoc:3.6."/*" //$NON-NLS-1$
if (!thatFile.startsWith(thisFile.substring(0, thisFile
.length() - 2))) {
return false;
}
// no further separators(s) allowed
if (thatFile.indexOf("/", thisFile.length() - 1) != -1) { //$NON-NLS-1$
return false;
}
} else {
// javadoc:3.6."/"
if (!thisFile.equals(thatFile)) {
if (!thisFile.endsWith("/")) { //$NON-NLS-1$
if (!thatFile.equals(thisFile + "/")) { //$NON-NLS-1$
return false;
}
} else {
return false;
}
}
}
//javadoc:3.7
if (this.location.getRef() != null) {
if (!this.location.getRef().equals(cs.location.getRef())) {
return false;
}
}
// ok, every check was made, and they all were successful.
// it's ok to return true.
} // if this.location != null
// javadoc: a note about CodeSource with null location and null Certs
// is applicable here
return true;
| private java.security.cert.CertPath | makeCertPath(java.util.List list)
if (factory == null) {
try {
factory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
} catch (CertificateException ex) {
//? throw new Error("X.509 is a 'must be'", ex);
return null;
}
}
try {
return factory.generateCertPath(list);
} catch (CertificateException ex) {
// ignore(ex)
}
return null;
| private void | readObject(java.io.ObjectInputStream ois)
ois.defaultReadObject();
int certsCount = ois.readInt();
certs = null;
if (certsCount != 0) {
certs = new Certificate[certsCount];
for (int i = 0; i < certsCount; i++) {
String type = ois.readUTF();
CertificateFactory factory;
try {
factory = CertificateFactory.getInstance(type);
} catch (CertificateException ex) {
throw new ClassNotFoundException(
Messages.getString("security.19", type), //$NON-NLS-1$
ex);
}
int dataLen = ois.readInt();
byte[] data = new byte[dataLen];
ois.readFully(data);
ByteArrayInputStream bais = new ByteArrayInputStream(data);
try {
certs[i] = factory.generateCertificate(bais);
} catch (CertificateException ex) {
throw (IOException) new IOException(
Messages.getString("security.1A")).initCause(ex); //$NON-NLS-1$
}
}
}
try {
signers = (CodeSigner[]) ois.readObject();
} catch (OptionalDataException ex) {
if (!ex.eof) {
throw ex;
}
// no signers (ex.eof==true <= no data left) is allowed
}
| public java.lang.String | toString()Returns a string containing a concise, human-readable description of the
this {@code CodeSource} including its location, its certificates and its
signers.
StringBuilder buf = new StringBuilder();
buf.append("CodeSource, url="); //$NON-NLS-1$
buf.append(location == null ? "<null>" : location.toString()); //$NON-NLS-1$
if (certs == null) {
buf.append(", <no certificates>"); //$NON-NLS-1$
} else {
buf.append("\nCertificates [\n"); //$NON-NLS-1$
for (int i = 0; i < certs.length; i++) {
buf.append(i + 1).append(") ").append(certs[i]).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
buf.append("]\n"); //$NON-NLS-1$
}
if (signers != null) {
buf.append("\nCodeSigners [\n"); //$NON-NLS-1$
for (int i = 0; i < signers.length; i++) {
buf.append(i + 1).append(") ").append(signers[i]).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
buf.append("]\n"); //$NON-NLS-1$
}
return buf.toString();
| private void | writeObject(java.io.ObjectOutputStream oos)
oos.defaultWriteObject();
if (certs == null || certs.length == 0) {
oos.writeInt(0);
} else {
oos.writeInt(certs.length);
for (int i = 0; i < certs.length; i++) {
try {
oos.writeUTF(certs[i].getType());
byte[] data = certs[i].getEncoded();
// hope there are no certificates with 'data==null'
oos.writeInt(data.length);
oos.write(data);
} catch (CertificateEncodingException ex) {
throw (IOException) new IOException(
Messages.getString("security.18")).initCause(ex); //$NON-NLS-1$
}
}
}
if (signers != null && signers.length != 0) {
oos.writeObject(signers);
}
|
|