/** Write the signature file to the given output stream. */ private void generateSignatureFile(Manifest manifest, OutputStream out) throws IOException, GeneralSecurityException { out.write(("Signature-Version: 1.0\r\n").getBytes()); out.write(("Created-By: 1.0 (Android SignApk)\r\n").getBytes()); // BASE64Encoder base64 = new BASE64Encoder(); MessageDigest md = MessageDigest.getInstance("SHA1"); PrintStream print = new PrintStream(new DigestOutputStream(new ByteArrayOutputStream(), md), true, "UTF-8"); // Digest of the entire manifest manifest.write(print); print.flush(); out.write(("SHA1-Digest-Manifest: " + Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); Map<String, Attributes> entries = manifest.getEntries(); for (Map.Entry<String, Attributes> entry : entries.entrySet()) { if (canceled) break; progressHelper.progress(ProgressEvent.PRORITY_NORMAL, "Generating signature file"); // Digest of the manifest stanza for this entry. String nameEntry = "Name: " + entry.getKey() + "\r\n"; print.print(nameEntry); for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) { print.print(att.getKey() + ": " + att.getValue() + "\r\n"); } print.print("\r\n"); print.flush(); out.write(nameEntry.getBytes()); out.write(("SHA1-Digest: " + Base64.encode(md.digest()) + "\r\n\r\n").getBytes()); } }
/** Add the SHA1 of every file to the manifest, creating it if necessary. */ private Manifest addDigestsToManifest(Map<String, ZioEntry> entries) throws IOException, GeneralSecurityException { Manifest input = null; ZioEntry manifestEntry = entries.get(JarFile.MANIFEST_NAME); if (manifestEntry != null) { input = new Manifest(); input.read(manifestEntry.getInputStream()); } Manifest output = new Manifest(); Attributes main = output.getMainAttributes(); if (input != null) { main.putAll(input.getMainAttributes()); } else { main.putValue("Manifest-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); } // BASE64Encoder base64 = new BASE64Encoder(); MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] buffer = new byte[512]; int num; // We sort the input entries by name, and add them to the // output manifest in sorted order. We expect that the output // map will be deterministic. TreeMap<String, ZioEntry> byName = new TreeMap<String, ZioEntry>(); byName.putAll(entries); boolean debug = getLogger().isDebugEnabled(); if (debug) getLogger().debug("Manifest entries:"); for (ZioEntry entry : byName.values()) { if (canceled) break; String name = entry.getName(); if (debug) getLogger().debug(name); if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && (stripPattern == null || !stripPattern.matcher(name).matches())) { progressHelper.progress(ProgressEvent.PRORITY_NORMAL, "Generating manifest"); InputStream data = entry.getInputStream(); while ((num = data.read(buffer)) > 0) { md.update(buffer, 0, num); } Attributes attr = null; if (input != null) { java.util.jar.Attributes inAttr = input.getAttributes(name); if (inAttr != null) attr = new Attributes(inAttr); } if (attr == null) attr = new Attributes(); attr.putValue("SHA1-Digest", Base64.encode(md.digest())); output.getEntries().put(name, attr); } } return output; }