|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.util; |
|
|
|
import java.security.*; |
|
import java.io.*; |
|
import java.security.CodeSigner; |
|
import java.util.*; |
|
import java.util.jar.*; |
|
|
|
import java.util.Base64; |
|
|
|
import sun.security.jca.Providers; |
|
|
|
/** |
|
* This class is used to verify each entry in a jar file with its |
|
* manifest value. |
|
*/ |
|
|
|
public class ManifestEntryVerifier { |
|
|
|
private static final Debug debug = Debug.getInstance("jar"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static class SunProviderHolder { |
|
private static final Provider instance = Providers.getSunProvider(); |
|
} |
|
|
|
|
|
HashMap<String, MessageDigest> createdDigests; |
|
|
|
|
|
ArrayList<MessageDigest> digests; |
|
|
|
|
|
ArrayList<byte[]> manifestHashes; |
|
|
|
private String name = null; |
|
private Manifest man; |
|
|
|
private boolean skip = true; |
|
|
|
private JarEntry entry; |
|
|
|
private CodeSigner[] signers = null; |
|
|
|
|
|
|
|
*/ |
|
public ManifestEntryVerifier(Manifest man) |
|
{ |
|
createdDigests = new HashMap<>(11); |
|
digests = new ArrayList<>(); |
|
manifestHashes = new ArrayList<>(); |
|
this.man = man; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void setEntry(String name, JarEntry entry) |
|
throws IOException |
|
{ |
|
digests.clear(); |
|
manifestHashes.clear(); |
|
this.name = name; |
|
this.entry = entry; |
|
|
|
skip = true; |
|
signers = null; |
|
|
|
if (man == null || name == null) { |
|
return; |
|
} |
|
|
|
/* get the headers from the manifest for this entry */ |
|
/* if there aren't any, we can't verify any digests for this entry */ |
|
|
|
skip = false; |
|
|
|
Attributes attr = man.getAttributes(name); |
|
if (attr == null) { |
|
// ugh. we should be able to remove this at some point. |
|
// there are broken jars floating around with ./name and /name |
|
|
|
attr = man.getAttributes("./"+name); |
|
if (attr == null) { |
|
attr = man.getAttributes("/"+name); |
|
if (attr == null) |
|
return; |
|
} |
|
} |
|
|
|
for (Map.Entry<Object,Object> se : attr.entrySet()) { |
|
String key = se.getKey().toString(); |
|
|
|
if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { |
|
|
|
String algorithm = key.substring(0, key.length()-7); |
|
|
|
MessageDigest digest = createdDigests.get(algorithm); |
|
|
|
if (digest == null) { |
|
try { |
|
|
|
digest = MessageDigest.getInstance |
|
(algorithm, SunProviderHolder.instance); |
|
createdDigests.put(algorithm, digest); |
|
} catch (NoSuchAlgorithmException nsae) { |
|
// ignore |
|
} |
|
} |
|
|
|
if (digest != null) { |
|
digest.reset(); |
|
digests.add(digest); |
|
manifestHashes.add( |
|
Base64.getMimeDecoder().decode((String)se.getValue())); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void update(byte buffer) { |
|
if (skip) return; |
|
|
|
for (int i=0; i < digests.size(); i++) { |
|
digests.get(i).update(buffer); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void update(byte[] buffer, int off, int len) { |
|
if (skip) return; |
|
|
|
for (int i=0; i < digests.size(); i++) { |
|
digests.get(i).update(buffer, off, len); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public JarEntry getEntry() |
|
{ |
|
return entry; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public CodeSigner[] verify(Hashtable<String, CodeSigner[]> verifiedSigners, |
|
Hashtable<String, CodeSigner[]> sigFileSigners) |
|
throws JarException |
|
{ |
|
if (skip) { |
|
return null; |
|
} |
|
|
|
if (digests.isEmpty()) { |
|
throw new SecurityException("digest missing for " + name); |
|
} |
|
|
|
if (signers != null) |
|
return signers; |
|
|
|
for (int i=0; i < digests.size(); i++) { |
|
|
|
MessageDigest digest = digests.get(i); |
|
byte [] manHash = manifestHashes.get(i); |
|
byte [] theHash = digest.digest(); |
|
|
|
if (debug != null) { |
|
debug.println("Manifest Entry: " + |
|
name + " digest=" + digest.getAlgorithm()); |
|
debug.println(" manifest " + toHex(manHash)); |
|
debug.println(" computed " + toHex(theHash)); |
|
debug.println(); |
|
} |
|
|
|
if (!MessageDigest.isEqual(theHash, manHash)) |
|
throw new SecurityException(digest.getAlgorithm()+ |
|
" digest error for "+name); |
|
} |
|
|
|
|
|
signers = sigFileSigners.remove(name); |
|
if (signers != null) { |
|
verifiedSigners.put(name, signers); |
|
} |
|
return signers; |
|
} |
|
|
|
|
|
private static final char[] hexc = |
|
{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; |
|
/** |
|
* convert a byte array to a hex string for debugging purposes |
|
* @param data the binary data to be converted to a hex string |
|
* @return an ASCII hex string |
|
*/ |
|
|
|
static String toHex(byte[] data) { |
|
|
|
StringBuilder sb = new StringBuilder(data.length*2); |
|
|
|
for (int i=0; i<data.length; i++) { |
|
sb.append(hexc[(data[i] >>4) & 0x0f]); |
|
sb.append(hexc[data[i] & 0x0f]); |
|
} |
|
return sb.toString(); |
|
} |
|
|
|
} |