6742654: Code insertion/replacement attacks against signed jars
6911041: JCK api/signaturetest tests fails for Mixed Code PIT builds (b91) for all trains 6921823: JarVerifier csdomain field not initialized 6921839: Update trusted.libraries list Reviewed-by: dgu
This commit is contained in:
parent
0e63901156
commit
d43bb4114c
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 1996, 2010 Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 1996, 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
|
||||
@ -65,6 +65,8 @@ CACERTS_BUILD = $(LIBDIR)/security/cacerts
|
||||
ifndef OPENJDK
|
||||
BLACKLIST_SRC = $(CLOSED_SHARE_SRC)/lib/security/blacklist
|
||||
BLACKLIST_BUILD = $(LIBDIR)/security/blacklist
|
||||
TRUSTEDLIBS_SRC = $(CLOSED_SHARE_SRC)/lib/security/trusted.libraries
|
||||
TRUSTEDLIBS_BUILD = $(LIBDIR)/security/trusted.libraries
|
||||
endif
|
||||
|
||||
FILES_class = $(FILES_java:%.java=$(CLASSBINDIR)/%.class)
|
||||
@ -77,7 +79,7 @@ include $(BUILDDIR)/common/Rules.gmk
|
||||
ifdef OPENJDK
|
||||
build: properties policy cacerts
|
||||
else
|
||||
build: properties policy cacerts blacklist
|
||||
build: properties policy cacerts blacklist trustedlibs
|
||||
endif
|
||||
|
||||
install: all
|
||||
@ -90,6 +92,8 @@ cacerts: classes $(CACERTS_BUILD)
|
||||
|
||||
blacklist: classes $(BLACKLIST_BUILD)
|
||||
|
||||
trustedlibs: classes $(TRUSTEDLIBS_BUILD)
|
||||
|
||||
$(PROPS_BUILD): $(PROPS_SRC)
|
||||
$(install-file)
|
||||
|
||||
@ -102,9 +106,12 @@ $(CACERTS_BUILD): $(CACERTS_SRC)
|
||||
$(BLACKLIST_BUILD): $(BLACKLIST_SRC)
|
||||
$(install-file)
|
||||
|
||||
$(TRUSTEDLIBS_BUILD): $(TRUSTEDLIBS_SRC)
|
||||
$(install-file)
|
||||
|
||||
clean clobber:: .delete.classlist
|
||||
$(RM) -r $(CLASSBINDIR)/java/security
|
||||
$(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD)
|
||||
$(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD) $(TRUSTEDLIBS_BUILD)
|
||||
|
||||
# Additional Rule for building sun.security.util
|
||||
$(CLASSBINDIR)/%.class: $(SHARE_SRC)/sun/%.java
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -27,11 +27,13 @@ package java.util.jar;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.util.ManifestEntryVerifier;
|
||||
import sun.misc.SharedSecrets;
|
||||
@ -262,7 +264,7 @@ class JarFile extends ZipFile {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (certs == null && jv != null) {
|
||||
certs = jv.getCerts(getName());
|
||||
certs = jv.getCerts(JarFile.this, this);
|
||||
}
|
||||
return certs == null ? null : certs.clone();
|
||||
}
|
||||
@ -273,7 +275,7 @@ class JarFile extends ZipFile {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (signers == null && jv != null) {
|
||||
signers = jv.getCodeSigners(getName());
|
||||
signers = jv.getCodeSigners(JarFile.this, this);
|
||||
}
|
||||
return signers == null ? null : signers.clone();
|
||||
}
|
||||
@ -544,4 +546,191 @@ class JarFile extends ZipFile {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private synchronized void ensureInitialization() {
|
||||
try {
|
||||
maybeInstantiateVerifier();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (jv != null && !jvInitialized) {
|
||||
initializeVerifier();
|
||||
jvInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
JarEntry newEntry(ZipEntry ze) {
|
||||
return new JarFileEntry(ze);
|
||||
}
|
||||
|
||||
Enumeration<String> entryNames(CodeSource[] cs) {
|
||||
ensureInitialization();
|
||||
if (jv != null) {
|
||||
return jv.entryNames(this, cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* JAR file has no signed content. Is there a non-signing
|
||||
* code source?
|
||||
*/
|
||||
boolean includeUnsigned = false;
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
if (cs[i].getCodeSigners() == null) {
|
||||
includeUnsigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (includeUnsigned) {
|
||||
return unsignedEntryNames();
|
||||
} else {
|
||||
return new Enumeration<String>() {
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of the zip file entries
|
||||
* excluding internal JAR mechanism entries and including
|
||||
* signed entries missing from the ZIP directory.
|
||||
*/
|
||||
Enumeration<JarEntry> entries2() {
|
||||
ensureInitialization();
|
||||
if (jv != null) {
|
||||
return jv.entries2(this, super.entries());
|
||||
}
|
||||
|
||||
// screen out entries which are never signed
|
||||
final Enumeration enum_ = super.entries();
|
||||
return new Enumeration<JarEntry>() {
|
||||
|
||||
ZipEntry entry;
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
if (entry != null) {
|
||||
return true;
|
||||
}
|
||||
while (enum_.hasMoreElements()) {
|
||||
ZipEntry ze = (ZipEntry) enum_.nextElement();
|
||||
if (JarVerifier.isSigningRelated(ze.getName())) {
|
||||
continue;
|
||||
}
|
||||
entry = ze;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public JarFileEntry nextElement() {
|
||||
if (hasMoreElements()) {
|
||||
ZipEntry ze = entry;
|
||||
entry = null;
|
||||
return new JarFileEntry(ze);
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CodeSource[] getCodeSources(URL url) {
|
||||
ensureInitialization();
|
||||
if (jv != null) {
|
||||
return jv.getCodeSources(this, url);
|
||||
}
|
||||
|
||||
/*
|
||||
* JAR file has no signed content. Is there a non-signing
|
||||
* code source?
|
||||
*/
|
||||
Enumeration unsigned = unsignedEntryNames();
|
||||
if (unsigned.hasMoreElements()) {
|
||||
return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Enumeration<String> unsignedEntryNames() {
|
||||
final Enumeration entries = entries();
|
||||
return new Enumeration<String>() {
|
||||
|
||||
String name;
|
||||
|
||||
/*
|
||||
* Grab entries from ZIP directory but screen out
|
||||
* metadata.
|
||||
*/
|
||||
public boolean hasMoreElements() {
|
||||
if (name != null) {
|
||||
return true;
|
||||
}
|
||||
while (entries.hasMoreElements()) {
|
||||
String value;
|
||||
ZipEntry e = (ZipEntry) entries.nextElement();
|
||||
value = e.getName();
|
||||
if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
|
||||
continue;
|
||||
}
|
||||
name = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String nextElement() {
|
||||
if (hasMoreElements()) {
|
||||
String value = name;
|
||||
name = null;
|
||||
return value;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CodeSource getCodeSource(URL url, String name) {
|
||||
ensureInitialization();
|
||||
if (jv != null) {
|
||||
if (jv.eagerValidation) {
|
||||
CodeSource cs = null;
|
||||
JarEntry je = getJarEntry(name);
|
||||
if (je != null) {
|
||||
cs = jv.getCodeSource(url, this, je);
|
||||
} else {
|
||||
cs = jv.getCodeSource(url, name);
|
||||
}
|
||||
return cs;
|
||||
} else {
|
||||
return jv.getCodeSource(url, name);
|
||||
}
|
||||
}
|
||||
|
||||
return JarVerifier.getUnsignedCS(url);
|
||||
}
|
||||
|
||||
void setEagerValidation(boolean eager) {
|
||||
try {
|
||||
maybeInstantiateVerifier();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (jv != null) {
|
||||
jv.setEagerValidation(eager);
|
||||
}
|
||||
}
|
||||
|
||||
List getManifestDigests() {
|
||||
ensureInitialization();
|
||||
if (jv != null) {
|
||||
return jv.getManifestDigests();
|
||||
}
|
||||
return new ArrayList();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -26,9 +26,11 @@
|
||||
package java.util.jar;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import sun.security.util.ManifestDigester;
|
||||
import sun.security.util.ManifestEntryVerifier;
|
||||
@ -81,6 +83,15 @@ class JarVerifier {
|
||||
/** the bytes for the manDig object */
|
||||
byte manifestRawBytes[] = null;
|
||||
|
||||
/** controls eager signature validation */
|
||||
boolean eagerValidation;
|
||||
|
||||
/** makes code source singleton instances unique to us */
|
||||
private Object csdomain = new Object();
|
||||
|
||||
/** collect -DIGEST-MANIFEST values for blacklist */
|
||||
private List manifestDigests;
|
||||
|
||||
public JarVerifier(byte rawBytes[]) {
|
||||
manifestRawBytes = rawBytes;
|
||||
sigFileSigners = new Hashtable();
|
||||
@ -88,6 +99,7 @@ class JarVerifier {
|
||||
sigFileData = new Hashtable(11);
|
||||
pendingBlocks = new ArrayList();
|
||||
baos = new ByteArrayOutputStream();
|
||||
manifestDigests = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,7 +259,7 @@ class JarVerifier {
|
||||
}
|
||||
|
||||
sfv.setSignatureFile(bytes);
|
||||
sfv.process(sigFileSigners);
|
||||
sfv.process(sigFileSigners, manifestDigests);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -290,7 +302,7 @@ class JarVerifier {
|
||||
sfv.setSignatureFile(bytes);
|
||||
}
|
||||
}
|
||||
sfv.process(sigFileSigners);
|
||||
sfv.process(sigFileSigners, manifestDigests);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
// e.g. sun.security.pkcs.ParsingException
|
||||
@ -312,12 +324,18 @@ class JarVerifier {
|
||||
/**
|
||||
* Return an array of java.security.cert.Certificate objects for
|
||||
* the given file in the jar.
|
||||
* @deprecated
|
||||
*/
|
||||
public java.security.cert.Certificate[] getCerts(String name)
|
||||
{
|
||||
return mapSignersToCertArray(getCodeSigners(name));
|
||||
}
|
||||
|
||||
public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
|
||||
{
|
||||
return mapSignersToCertArray(getCodeSigners(jar, entry));
|
||||
}
|
||||
|
||||
/**
|
||||
* return an array of CodeSigner objects for
|
||||
* the given file in the jar. this array is not cloned.
|
||||
@ -328,6 +346,28 @@ class JarVerifier {
|
||||
return (CodeSigner[])verifiedSigners.get(name);
|
||||
}
|
||||
|
||||
public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
|
||||
{
|
||||
String name = entry.getName();
|
||||
if (eagerValidation && sigFileSigners.get(name) != null) {
|
||||
/*
|
||||
* Force a read of the entry data to generate the
|
||||
* verification hash.
|
||||
*/
|
||||
try {
|
||||
InputStream s = jar.getInputStream(entry);
|
||||
byte[] buffer = new byte[1024];
|
||||
int n = buffer.length;
|
||||
while (n != -1) {
|
||||
n = s.read(buffer, 0, buffer.length);
|
||||
}
|
||||
s.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
return getCodeSigners(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of signers into an array of concatenated certificate
|
||||
* arrays.
|
||||
@ -444,4 +484,393 @@ class JarVerifier {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Extended JavaUtilJarAccess CodeSource API Support
|
||||
|
||||
private Map urlToCodeSourceMap = new HashMap();
|
||||
private Map signerToCodeSource = new HashMap();
|
||||
private URL lastURL;
|
||||
private Map lastURLMap;
|
||||
|
||||
/*
|
||||
* Create a unique mapping from codeSigner cache entries to CodeSource.
|
||||
* In theory, multiple URLs origins could map to a single locally cached
|
||||
* and shared JAR file although in practice there will be a single URL in use.
|
||||
*/
|
||||
private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
|
||||
Map map;
|
||||
if (url == lastURL) {
|
||||
map = lastURLMap;
|
||||
} else {
|
||||
map = (Map) urlToCodeSourceMap.get(url);
|
||||
if (map == null) {
|
||||
map = new HashMap();
|
||||
urlToCodeSourceMap.put(url, map);
|
||||
}
|
||||
lastURLMap = map;
|
||||
lastURL = url;
|
||||
}
|
||||
CodeSource cs = (CodeSource) map.get(signers);
|
||||
if (cs == null) {
|
||||
cs = new VerifierCodeSource(csdomain, url, signers);
|
||||
signerToCodeSource.put(signers, cs);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
private CodeSource[] mapSignersToCodeSources(URL url, List signers, boolean unsigned) {
|
||||
List sources = new ArrayList();
|
||||
|
||||
for (int i = 0; i < signers.size(); i++) {
|
||||
sources.add(mapSignersToCodeSource(url, (CodeSigner[]) signers.get(i)));
|
||||
}
|
||||
if (unsigned) {
|
||||
sources.add(mapSignersToCodeSource(url, null));
|
||||
}
|
||||
return (CodeSource[]) sources.toArray(new CodeSource[sources.size()]);
|
||||
}
|
||||
private CodeSigner[] emptySigner = new CodeSigner[0];
|
||||
|
||||
/*
|
||||
* Match CodeSource to a CodeSigner[] in the signer cache.
|
||||
*/
|
||||
private CodeSigner[] findMatchingSigners(CodeSource cs) {
|
||||
if (cs instanceof VerifierCodeSource) {
|
||||
VerifierCodeSource vcs = (VerifierCodeSource) cs;
|
||||
if (vcs.isSameDomain(csdomain)) {
|
||||
return ((VerifierCodeSource) cs).getPrivateSigners();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In practice signers should always be optimized above
|
||||
* but this handles a CodeSource of any type, just in case.
|
||||
*/
|
||||
CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
|
||||
List sourceList = new ArrayList();
|
||||
for (int i = 0; i < sources.length; i++) {
|
||||
sourceList.add(sources[i]);
|
||||
}
|
||||
int j = sourceList.indexOf(cs);
|
||||
if (j != -1) {
|
||||
CodeSigner[] match;
|
||||
match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
|
||||
if (match == null) {
|
||||
match = emptySigner;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instances of this class hold uncopied references to internal
|
||||
* signing data that can be compared by object reference identity.
|
||||
*/
|
||||
private static class VerifierCodeSource extends CodeSource {
|
||||
|
||||
URL vlocation;
|
||||
CodeSigner[] vsigners;
|
||||
java.security.cert.Certificate[] vcerts;
|
||||
Object csdomain;
|
||||
|
||||
VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
|
||||
super(location, signers);
|
||||
this.csdomain = csdomain;
|
||||
vlocation = location;
|
||||
vsigners = signers; // from signerCache
|
||||
}
|
||||
|
||||
VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
|
||||
super(location, certs);
|
||||
this.csdomain = csdomain;
|
||||
vlocation = location;
|
||||
vcerts = certs; // from signerCache
|
||||
}
|
||||
|
||||
/*
|
||||
* All VerifierCodeSource instances are constructed based on
|
||||
* singleton signerCache or signerCacheCert entries for each unique signer.
|
||||
* No CodeSigner<->Certificate[] conversion is required.
|
||||
* We use these assumptions to optimize equality comparisons.
|
||||
*/
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof VerifierCodeSource) {
|
||||
VerifierCodeSource that = (VerifierCodeSource) obj;
|
||||
|
||||
/*
|
||||
* Only compare against other per-signer singletons constructed
|
||||
* on behalf of the same JarFile instance. Otherwise, compare
|
||||
* things the slower way.
|
||||
*/
|
||||
if (isSameDomain(that.csdomain)) {
|
||||
if (that.vsigners != this.vsigners
|
||||
|| that.vcerts != this.vcerts) {
|
||||
return false;
|
||||
}
|
||||
if (that.vlocation != null) {
|
||||
return that.vlocation.equals(this.vlocation);
|
||||
} else if (this.vlocation != null) {
|
||||
return this.vlocation.equals(that.vlocation);
|
||||
} else { // both null
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
boolean isSameDomain(Object csdomain) {
|
||||
return this.csdomain == csdomain;
|
||||
}
|
||||
|
||||
private CodeSigner[] getPrivateSigners() {
|
||||
return vsigners;
|
||||
}
|
||||
|
||||
private java.security.cert.Certificate[] getPrivateCertificates() {
|
||||
return vcerts;
|
||||
}
|
||||
}
|
||||
private Map signerMap;
|
||||
|
||||
private synchronized Map signerMap() {
|
||||
if (signerMap == null) {
|
||||
/*
|
||||
* Snapshot signer state so it doesn't change on us. We care
|
||||
* only about the asserted signatures. Verification of
|
||||
* signature validity happens via the JarEntry apis.
|
||||
*/
|
||||
signerMap = new HashMap(verifiedSigners.size() + sigFileSigners.size());
|
||||
signerMap.putAll(verifiedSigners);
|
||||
signerMap.putAll(sigFileSigners);
|
||||
}
|
||||
return signerMap;
|
||||
}
|
||||
|
||||
public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
|
||||
final Map map = signerMap();
|
||||
final Iterator itor = map.entrySet().iterator();
|
||||
boolean matchUnsigned = false;
|
||||
|
||||
/*
|
||||
* Grab a single copy of the CodeSigner arrays. Check
|
||||
* to see if we can optimize CodeSigner equality test.
|
||||
*/
|
||||
List req = new ArrayList(cs.length);
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
CodeSigner[] match = findMatchingSigners(cs[i]);
|
||||
if (match != null) {
|
||||
if (match.length > 0) {
|
||||
req.add(match);
|
||||
} else {
|
||||
matchUnsigned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List signersReq = req;
|
||||
final Enumeration enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
|
||||
|
||||
return new Enumeration<String>() {
|
||||
|
||||
String name;
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
if (name != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (itor.hasNext()) {
|
||||
Map.Entry e = (Map.Entry) itor.next();
|
||||
if (signersReq.contains((CodeSigner[]) e.getValue())) {
|
||||
name = (String) e.getKey();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while (enum2.hasMoreElements()) {
|
||||
name = (String) enum2.nextElement();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String nextElement() {
|
||||
if (hasMoreElements()) {
|
||||
String value = name;
|
||||
name = null;
|
||||
return value;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Like entries() but screens out internal JAR mechanism entries
|
||||
* and includes signed entries with no ZIP data.
|
||||
*/
|
||||
public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration e) {
|
||||
final Map map = new HashMap();
|
||||
map.putAll(signerMap());
|
||||
final Enumeration enum_ = e;
|
||||
return new Enumeration<JarEntry>() {
|
||||
|
||||
Enumeration signers = null;
|
||||
JarEntry entry;
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
if (entry != null) {
|
||||
return true;
|
||||
}
|
||||
while (enum_.hasMoreElements()) {
|
||||
ZipEntry ze = (ZipEntry) enum_.nextElement();
|
||||
if (JarVerifier.isSigningRelated(ze.getName())) {
|
||||
continue;
|
||||
}
|
||||
entry = jar.newEntry(ze);
|
||||
return true;
|
||||
}
|
||||
if (signers == null) {
|
||||
signers = Collections.enumeration(map.keySet());
|
||||
}
|
||||
while (signers.hasMoreElements()) {
|
||||
String name = (String) signers.nextElement();
|
||||
entry = jar.newEntry(new ZipEntry(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any map entries left?
|
||||
return false;
|
||||
}
|
||||
|
||||
public JarEntry nextElement() {
|
||||
if (hasMoreElements()) {
|
||||
JarEntry je = entry;
|
||||
map.remove(je.getName());
|
||||
entry = null;
|
||||
return je;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
private Enumeration emptyEnumeration = new Enumeration<String>() {
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
|
||||
// true if file is part of the signature mechanism itself
|
||||
static boolean isSigningRelated(String name) {
|
||||
name = name.toUpperCase(Locale.ENGLISH);
|
||||
if (!name.startsWith("META-INF/")) {
|
||||
return false;
|
||||
}
|
||||
name = name.substring(9);
|
||||
if (name.indexOf('/') != -1) {
|
||||
return false;
|
||||
}
|
||||
if (name.endsWith(".DSA")
|
||||
|| name.endsWith(".RSA")
|
||||
|| name.endsWith(".SF")
|
||||
|| name.endsWith(".EC")
|
||||
|| name.startsWith("SIG-")
|
||||
|| name.equals("MANIFEST.MF")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Enumeration<String> unsignedEntryNames(JarFile jar) {
|
||||
final Map map = signerMap();
|
||||
final Enumeration entries = jar.entries();
|
||||
return new Enumeration<String>() {
|
||||
|
||||
String name;
|
||||
|
||||
/*
|
||||
* Grab entries from ZIP directory but screen out
|
||||
* metadata.
|
||||
*/
|
||||
public boolean hasMoreElements() {
|
||||
if (name != null) {
|
||||
return true;
|
||||
}
|
||||
while (entries.hasMoreElements()) {
|
||||
String value;
|
||||
ZipEntry e = (ZipEntry) entries.nextElement();
|
||||
value = e.getName();
|
||||
if (e.isDirectory() || isSigningRelated(value)) {
|
||||
continue;
|
||||
}
|
||||
if (map.get(value) == null) {
|
||||
name = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String nextElement() {
|
||||
if (hasMoreElements()) {
|
||||
String value = name;
|
||||
name = null;
|
||||
return value;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
private List jarCodeSigners;
|
||||
|
||||
private synchronized List getJarCodeSigners() {
|
||||
CodeSigner[] signers;
|
||||
if (jarCodeSigners == null) {
|
||||
HashSet set = new HashSet();
|
||||
set.addAll(signerMap().values());
|
||||
jarCodeSigners = new ArrayList();
|
||||
jarCodeSigners.addAll(set);
|
||||
}
|
||||
return jarCodeSigners;
|
||||
}
|
||||
|
||||
public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
|
||||
boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
|
||||
|
||||
return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
|
||||
}
|
||||
|
||||
public CodeSource getCodeSource(URL url, String name) {
|
||||
CodeSigner[] signers;
|
||||
|
||||
signers = (CodeSigner[]) signerMap().get(name);
|
||||
return mapSignersToCodeSource(url, signers);
|
||||
}
|
||||
|
||||
public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
|
||||
CodeSigner[] signers;
|
||||
|
||||
return mapSignersToCodeSource(url, getCodeSigners(jar, je));
|
||||
}
|
||||
|
||||
public void setEagerValidation(boolean eager) {
|
||||
eagerValidation = eager;
|
||||
}
|
||||
|
||||
public synchronized List getManifestDigests() {
|
||||
return Collections.unmodifiableList(manifestDigests);
|
||||
}
|
||||
|
||||
static CodeSource getUnsignedCS(URL url) {
|
||||
return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 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
|
||||
@ -26,10 +26,38 @@
|
||||
package java.util.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import sun.misc.JavaUtilJarAccess;
|
||||
|
||||
class JavaUtilJarAccessImpl implements JavaUtilJarAccess {
|
||||
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException {
|
||||
return jar.hasClassPathAttribute();
|
||||
}
|
||||
|
||||
public CodeSource[] getCodeSources(JarFile jar, URL url) {
|
||||
return jar.getCodeSources(url);
|
||||
}
|
||||
|
||||
public CodeSource getCodeSource(JarFile jar, URL url, String name) {
|
||||
return jar.getCodeSource(url, name);
|
||||
}
|
||||
|
||||
public Enumeration<String> entryNames(JarFile jar, CodeSource[] cs) {
|
||||
return jar.entryNames(cs);
|
||||
}
|
||||
|
||||
public Enumeration<JarEntry> entries2(JarFile jar) {
|
||||
return jar.entries2();
|
||||
}
|
||||
|
||||
public void setEagerValidation(JarFile jar, boolean eager) {
|
||||
jar.setEagerValidation(eager);
|
||||
}
|
||||
|
||||
public List getManifestDigests(JarFile jar) {
|
||||
return jar.getManifestDigests();
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,19 @@ public class JarIndex {
|
||||
parseJars(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the jar index, or <code>null</code> if none.
|
||||
*
|
||||
* This single parameter version of the method is retained
|
||||
* for binary compatibility with earlier releases.
|
||||
*
|
||||
* @param jar the JAR file to get the index from.
|
||||
* @exception IOException if an I/O error has occurred.
|
||||
*/
|
||||
public static JarIndex getJarIndex(JarFile jar) throws IOException {
|
||||
return getJarIndex(jar, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the jar index, or <code>null</code> if none.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 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
|
||||
@ -26,8 +26,19 @@
|
||||
package sun.misc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public interface JavaUtilJarAccess {
|
||||
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
|
||||
public CodeSource[] getCodeSources(JarFile jar, URL url);
|
||||
public CodeSource getCodeSource(JarFile jar, URL url, String name);
|
||||
public Enumeration<String> entryNames(JarFile jar, CodeSource[] cs);
|
||||
public Enumeration<JarEntry> entries2(JarFile jar);
|
||||
public void setEagerValidation(JarFile jar, boolean eager);
|
||||
public List getManifestDigests(JarFile jar);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -181,7 +181,8 @@ public class SignatureFileVerifier {
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void process(Hashtable<String, CodeSigner[]> signers)
|
||||
public void process(Hashtable<String, CodeSigner[]> signers,
|
||||
List manifestDigests)
|
||||
throws IOException, SignatureException, NoSuchAlgorithmException,
|
||||
JarException, CertificateException
|
||||
{
|
||||
@ -190,14 +191,15 @@ public class SignatureFileVerifier {
|
||||
Object obj = null;
|
||||
try {
|
||||
obj = Providers.startJarVerification();
|
||||
processImpl(signers);
|
||||
processImpl(signers, manifestDigests);
|
||||
} finally {
|
||||
Providers.stopJarVerification(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void processImpl(Hashtable<String, CodeSigner[]> signers)
|
||||
private void processImpl(Hashtable<String, CodeSigner[]> signers,
|
||||
List manifestDigests)
|
||||
throws IOException, SignatureException, NoSuchAlgorithmException,
|
||||
JarException, CertificateException
|
||||
{
|
||||
@ -232,7 +234,7 @@ public class SignatureFileVerifier {
|
||||
sf.getEntries().entrySet().iterator();
|
||||
|
||||
// see if we can verify the whole manifest first
|
||||
boolean manifestSigned = verifyManifestHash(sf, md, decoder);
|
||||
boolean manifestSigned = verifyManifestHash(sf, md, decoder, manifestDigests);
|
||||
|
||||
// verify manifest main attributes
|
||||
if (!manifestSigned && !verifyManifestMainAttrs(sf, md, decoder)) {
|
||||
@ -275,7 +277,8 @@ public class SignatureFileVerifier {
|
||||
*/
|
||||
private boolean verifyManifestHash(Manifest sf,
|
||||
ManifestDigester md,
|
||||
BASE64Decoder decoder)
|
||||
BASE64Decoder decoder,
|
||||
List manifestDigests)
|
||||
throws IOException
|
||||
{
|
||||
Attributes mattr = sf.getMainAttributes();
|
||||
@ -290,6 +293,8 @@ public class SignatureFileVerifier {
|
||||
// 16 is length of "-Digest-Manifest"
|
||||
String algorithm = key.substring(0, key.length()-16);
|
||||
|
||||
manifestDigests.add(key);
|
||||
manifestDigests.add(se.getValue());
|
||||
MessageDigest digest = getDigest(algorithm);
|
||||
if (digest != null) {
|
||||
byte[] computedHash = md.manifestDigest(digest);
|
||||
|
Loading…
x
Reference in New Issue
Block a user