/** * Load the policies from the specified file. Also checks that the policies are correctly signed. */ private static void loadPolicies( File jarPathName, CryptoPermissions defaultPolicy, CryptoPermissions exemptPolicy) throws Exception { JarFile jf = new JarFile(jarPathName); Enumeration<JarEntry> entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); InputStream is = null; try { if (je.getName().startsWith("default_")) { is = jf.getInputStream(je); defaultPolicy.load(is); } else if (je.getName().startsWith("exempt_")) { is = jf.getInputStream(je); exemptPolicy.load(is); } else { continue; } } finally { if (is != null) { is.close(); } } // Enforce the signer restraint, i.e. signer of JCE framework // jar should also be the signer of the two jurisdiction policy // jar files. JarVerifier.verifyPolicySigned(je.getCertificates()); } // Close and nullify the JarFile reference to help GC. jf.close(); jf = null; }
private Certificate[] getCertificates(final File container, final String entry) throws IOException { if (container.isDirectory()) { return null; } final JarFile jarFile = this.jarFiles.get(container); if (jarFile == null) { return null; } final JarEntry ent = jarFile.getJarEntry(entry); return (Certificate[]) ((ent == null) ? null : ent.getCertificates()); }
VerifierStream(Manifest man, JarEntry je, InputStream is, JarVerifier jv) throws IOException { this.is = is; this.jv = jv; this.mev = new ManifestEntryVerifier(man); this.jv.beginEntry(je, mev); this.numLeft = je.getSize(); if (this.numLeft == 0) this.jv.update(-1, this.mev); }
public String verify(JarFile jar, String... algorithms) throws IOException { if (algorithms == null || algorithms.length == 0) algorithms = new String[] {"MD5", "SHA"}; else if (algorithms.length == 1 && algorithms[0].equals("-")) return null; try { Manifest m = jar.getManifest(); if (m.getEntries().isEmpty()) return "No name sections"; for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) { JarEntry je = e.nextElement(); if (MANIFEST_ENTRY.matcher(je.getName()).matches()) continue; Attributes nameSection = m.getAttributes(je.getName()); if (nameSection == null) return "No name section for " + je.getName(); for (String algorithm : algorithms) { try { MessageDigest md = MessageDigest.getInstance(algorithm); String expected = nameSection.getValue(algorithm + "-Digest"); if (expected != null) { byte digest[] = Base64.decodeBase64(expected); copy(jar.getInputStream(je), md); if (!Arrays.equals(digest, md.digest())) return "Invalid digest for " + je.getName() + ", " + expected + " != " + Base64.encodeBase64(md.digest()); } else reporter.error("could not find digest for " + algorithm + "-Digest"); } catch (NoSuchAlgorithmException nsae) { return "Missing digest algorithm " + algorithm; } } } } catch (Exception e) { return "Failed to verify due to exception: " + e.getMessage(); } return null; }
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); }
/** called when we reach the end of entry in one of the read() methods. */ private void processEntry(ManifestEntryVerifier mev) throws IOException { if (!parsingBlockOrSF) { JarEntry je = mev.getEntry(); if ((je != null) && (je.signers == null)) { je.signers = mev.verify(verifiedSigners, sigFileSigners); je.certs = mapSignersToCertArray(je.signers); } } else { try { parsingBlockOrSF = false; if (debug != null) { debug.println("processEntry: processing block"); } String uname = mev.getEntry().getName().toUpperCase(Locale.ENGLISH); if (uname.endsWith(".SF")) { String key = uname.substring(0, uname.length() - 3); byte bytes[] = baos.toByteArray(); // add to sigFileData in case future blocks need it sigFileData.put(key, bytes); // check pending blocks, we can now process // anyone waiting for this .SF file Iterator it = pendingBlocks.iterator(); while (it.hasNext()) { SignatureFileVerifier sfv = (SignatureFileVerifier) it.next(); if (sfv.needSignatureFile(key)) { if (debug != null) { debug.println("processEntry: processing pending block"); } sfv.setSignatureFile(bytes); sfv.process(sigFileSigners, manifestDigests); } } return; } // now we are parsing a signature block file String key = uname.substring(0, uname.lastIndexOf(".")); if (signerCache == null) signerCache = new ArrayList(); if (manDig == null) { synchronized (manifestRawBytes) { if (manDig == null) { manDig = new ManifestDigester(manifestRawBytes); manifestRawBytes = null; } } } SignatureFileVerifier sfv = new SignatureFileVerifier(signerCache, manDig, uname, baos.toByteArray()); if (sfv.needSignatureFileBytes()) { // see if we have already parsed an external .SF file byte[] bytes = (byte[]) sigFileData.get(key); if (bytes == null) { // put this block on queue for later processing // since we don't have the .SF bytes yet // (uname, block); if (debug != null) { debug.println("adding pending block"); } pendingBlocks.add(sfv); return; } else { sfv.setSignatureFile(bytes); } } sfv.process(sigFileSigners, manifestDigests); } catch (IOException ioe) { // e.g. sun.security.pkcs.ParsingException if (debug != null) debug.println("processEntry caught: " + ioe); // ignore and treat as unsigned } catch (SignatureException se) { if (debug != null) debug.println("processEntry caught: " + se); // ignore and treat as unsigned } catch (NoSuchAlgorithmException nsae) { if (debug != null) debug.println("processEntry caught: " + nsae); // ignore and treat as unsigned } catch (CertificateException ce) { if (debug != null) debug.println("processEntry caught: " + ce); // ignore and treat as unsigned } } }
/** * This method scans to see which entry we're parsing and keeps various state information * depending on what type of file is being parsed. */ public void beginEntry(JarEntry je, ManifestEntryVerifier mev) throws IOException { if (je == null) return; if (debug != null) { debug.println("beginEntry " + je.getName()); } String name = je.getName(); /* * Assumptions: * 1. The manifest should be the first entry in the META-INF directory. * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries * 3. Any of the following will throw a SecurityException: * a. digest mismatch between a manifest section and * the SF section. * b. digest mismatch between the actual jar entry and the manifest */ if (parsingMeta) { String uname = name.toUpperCase(Locale.ENGLISH); if ((uname.startsWith("META-INF/") || uname.startsWith("/META-INF/"))) { if (je.isDirectory()) { mev.setEntry(null, je); return; } if (uname.equals(JarFile.MANIFEST_NAME) || uname.equals(JarIndex.INDEX_NAME)) { return; } if (SignatureFileVerifier.isBlockOrSF(uname)) { /* We parse only DSA, RSA or EC PKCS7 blocks. */ parsingBlockOrSF = true; baos.reset(); mev.setEntry(null, je); return; } // If a META-INF entry is not MF or block or SF, they should // be normal entries. According to 2 above, no more block or // SF will appear. Let's doneWithMeta. } } if (parsingMeta) { doneWithMeta(); } if (je.isDirectory()) { mev.setEntry(null, je); return; } // be liberal in what you accept. If the name starts with ./, remove // it as we internally canonicalize it with out the ./. if (name.startsWith("./")) name = name.substring(2); // be liberal in what you accept. If the name starts with /, remove // it as we internally canonicalize it with out the /. if (name.startsWith("/")) name = name.substring(1); // only set the jev object for entries that have a signature // (either verified or not) if (sigFileSigners.get(name) != null || verifiedSigners.get(name) != null) { mev.setEntry(name, je); return; } // don't compute the digest for this entry mev.setEntry(null, je); return; }