aboutsummaryrefslogtreecommitdiffstats
path: root/jarutils/src
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:16 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:16 -0800
commit82ea7a177797b844b252effea5c7c7c5d63ea4ac (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /jarutils/src
parentc9432be76d50a527da232d518f633add2f76242b (diff)
downloadsdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.zip
sdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.tar.gz
sdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'jarutils/src')
-rw-r--r--jarutils/src/Android.mk14
-rw-r--r--jarutils/src/com/android/jarutils/DebugKeyProvider.java202
-rw-r--r--jarutils/src/com/android/jarutils/JavaResourceFilter.java96
-rw-r--r--jarutils/src/com/android/jarutils/KeystoreHelper.java228
-rw-r--r--jarutils/src/com/android/jarutils/SignedJarBuilder.java324
5 files changed, 0 insertions, 864 deletions
diff --git a/jarutils/src/Android.mk b/jarutils/src/Android.mk
deleted file mode 100644
index 2248b7f..0000000
--- a/jarutils/src/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2008 The Android Open Source Project
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := \
- androidprefs
-
-LOCAL_MODULE := jarutils
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/jarutils/src/com/android/jarutils/DebugKeyProvider.java b/jarutils/src/com/android/jarutils/DebugKeyProvider.java
deleted file mode 100644
index 6dc32ba..0000000
--- a/jarutils/src/com/android/jarutils/DebugKeyProvider.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jarutils;
-
-import com.android.prefs.AndroidLocation;
-import com.android.prefs.AndroidLocation.AndroidLocationException;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.UnrecoverableEntryException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-
-/**
- * A provider of a dummy key to sign Android application for debugging purpose.
- * <p/>This provider uses a custom keystore to create and store a key with a known password.
- */
-public class DebugKeyProvider {
-
- public interface IKeyGenOutput {
- public void out(String message);
- public void err(String message);
- }
-
- private static final String PASSWORD_STRING = "android";
- private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray();
- private static final String DEBUG_ALIAS = "AndroidDebugKey";
-
- // Certificate CN value. This is a hard-coded value for the debug key.
- // Android Market checks against this value in order to refuse applications signed with
- // debug keys.
- private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US";
-
- private KeyStore.PrivateKeyEntry mEntry;
-
- public static class KeytoolException extends Exception {
- /** default serial uid */
- private static final long serialVersionUID = 1L;
- private String mJavaHome = null;
- private String mCommandLine = null;
-
- KeytoolException(String message) {
- super(message);
- }
-
- KeytoolException(String message, String javaHome, String commandLine) {
- super(message);
-
- mJavaHome = javaHome;
- mCommandLine = commandLine;
- }
-
- public String getJavaHome() {
- return mJavaHome;
- }
-
- public String getCommandLine() {
- return mCommandLine;
- }
- }
-
- /**
- * Creates a provider using a keystore at the given location.
- * <p/>The keystore, and a new random android debug key are created if they do not yet exist.
- * <p/>Password for the store/key is <code>android</code>, and the key alias is
- * <code>AndroidDebugKey</code>.
- * @param osKeyStorePath the OS path to the keystore, or <code>null</code> if the default one
- * is to be used.
- * @param storeType an optional keystore type, or <code>null</code> if the default is to
- * be used.
- * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
- * of the keytool process call.
- * @throws KeytoolException If the creation of the debug key failed.
- * @throws AndroidLocationException
- */
- public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output)
- throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
- UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException {
-
- if (osKeyStorePath == null) {
- osKeyStorePath = getDefaultKeyStoreOsPath();
- }
-
- if (loadKeyEntry(osKeyStorePath, storeType) == false) {
- // create the store with the key
- createNewStore(osKeyStorePath, storeType, output);
- }
- }
-
- /**
- * Returns the OS path to the default debug keystore.
- *
- * @return The OS path to the default debug keystore.
- * @throws KeytoolException
- * @throws AndroidLocationException
- */
- public static String getDefaultKeyStoreOsPath()
- throws KeytoolException, AndroidLocationException {
- String folder = AndroidLocation.getFolder();
- if (folder == null) {
- throw new KeytoolException("Failed to get HOME directory!\n");
- }
- String osKeyStorePath = folder + "debug.keystore";
-
- return osKeyStorePath;
- }
-
- /**
- * Returns the debug {@link PrivateKey} to use to sign applications for debug purpose.
- * @return the private key or <code>null</code> if its creation failed.
- */
- public PrivateKey getDebugKey() throws KeyStoreException, NoSuchAlgorithmException,
- UnrecoverableKeyException, UnrecoverableEntryException {
- if (mEntry != null) {
- return mEntry.getPrivateKey();
- }
-
- return null;
- }
-
- /**
- * Returns the debug {@link Certificate} to use to sign applications for debug purpose.
- * @return the certificate or <code>null</code> if its creation failed.
- */
- public Certificate getCertificate() throws KeyStoreException, NoSuchAlgorithmException,
- UnrecoverableKeyException, UnrecoverableEntryException {
- if (mEntry != null) {
- return mEntry.getCertificate();
- }
-
- return null;
- }
-
- /**
- * Loads the debug key from the keystore.
- * @param osKeyStorePath the OS path to the keystore.
- * @param storeType an optional keystore type, or <code>null</code> if the default is to
- * be used.
- * @return <code>true</code> if success, <code>false</code> if the keystore does not exist.
- */
- private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException,
- NoSuchAlgorithmException, CertificateException, IOException,
- UnrecoverableEntryException {
- try {
- KeyStore keyStore = KeyStore.getInstance(
- storeType != null ? storeType : KeyStore.getDefaultType());
- FileInputStream fis = new FileInputStream(osKeyStorePath);
- keyStore.load(fis, PASSWORD_CHAR);
- fis.close();
- mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
- DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR));
- } catch (FileNotFoundException e) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Creates a new store
- * @param osKeyStorePath the location of the store
- * @param storeType an optional keystore type, or <code>null</code> if the default is to
- * be used.
- * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
- * of the keytool process call.
- * @throws KeyStoreException
- * @throws NoSuchAlgorithmException
- * @throws CertificateException
- * @throws UnrecoverableEntryException
- * @throws IOException
- * @throws KeytoolException
- */
- private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)
- throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
- UnrecoverableEntryException, IOException, KeytoolException {
-
- if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS,
- PASSWORD_STRING, CERTIFICATE_DESC, 1 /* validity*/, output)) {
- loadKeyEntry(osKeyStorePath, storeType);
- }
- }
-}
diff --git a/jarutils/src/com/android/jarutils/JavaResourceFilter.java b/jarutils/src/com/android/jarutils/JavaResourceFilter.java
deleted file mode 100644
index d9f8da6..0000000
--- a/jarutils/src/com/android/jarutils/JavaResourceFilter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jarutils;
-
-import com.android.jarutils.SignedJarBuilder.IZipEntryFilter;
-
-/**
- * A basic implementation of {@link IZipEntryFilter} to filter out anything that is not a
- * java resource.
- */
-public class JavaResourceFilter implements IZipEntryFilter {
-
- public boolean checkEntry(String name) {
- // split the path into segments.
- String[] segments = name.split("/");
-
- // empty path? skip to next entry.
- if (segments.length == 0) {
- return false;
- }
-
- // Check each folders to make sure they should be included.
- // Folders like CVS, .svn, etc.. should already have been excluded from the
- // jar file, but we need to exclude some other folder (like /META-INF) so
- // we check anyway.
- for (int i = 0 ; i < segments.length - 1; i++) {
- if (checkFolderForPackaging(segments[i]) == false) {
- return false;
- }
- }
-
- // get the file name from the path
- String fileName = segments[segments.length-1];
-
- return checkFileForPackaging(fileName);
- }
-
- /**
- * Checks whether a folder and its content is valid for packaging into the .apk as
- * standard Java resource.
- * @param folderName the name of the folder.
- */
- public static boolean checkFolderForPackaging(String folderName) {
- return folderName.equals("CVS") == false &&
- folderName.equals(".svn") == false &&
- folderName.equals("SCCS") == false &&
- folderName.equals("META-INF") == false &&
- folderName.startsWith("_") == false;
- }
-
- /**
- * Checks a file to make sure it should be packaged as standard resources.
- * @param fileName the name of the file (including extension)
- * @return true if the file should be packaged as standard java resources.
- */
- public static boolean checkFileForPackaging(String fileName) {
- String[] fileSegments = fileName.split("\\.");
- String fileExt = "";
- if (fileSegments.length > 1) {
- fileExt = fileSegments[fileSegments.length-1];
- }
-
- return checkFileForPackaging(fileName, fileExt);
- }
-
- /**
- * Checks a file to make sure it should be packaged as standard resources.
- * @param fileName the name of the file (including extension)
- * @param extension the extension of the file (excluding '.')
- * @return true if the file should be packaged as standard java resources.
- */
- public static boolean checkFileForPackaging(String fileName, String extension) {
- return "aidl".equalsIgnoreCase(extension) == false &&
- "java".equalsIgnoreCase(extension) == false &&
- "class".equalsIgnoreCase(extension) == false &&
- "package.html".equalsIgnoreCase(fileName) == false &&
- "overview.html".equalsIgnoreCase(fileName) == false &&
- ".cvsignore".equalsIgnoreCase(fileName) == false &&
- ".DS_Store".equals(fileName) == false &&
- fileName.charAt(fileName.length()-1) != '~';
- }
-}
diff --git a/jarutils/src/com/android/jarutils/KeystoreHelper.java b/jarutils/src/com/android/jarutils/KeystoreHelper.java
deleted file mode 100644
index c694684..0000000
--- a/jarutils/src/com/android/jarutils/KeystoreHelper.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jarutils;
-
-import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
-import com.android.jarutils.DebugKeyProvider.KeytoolException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableEntryException;
-import java.security.cert.CertificateException;
-import java.util.ArrayList;
-
-/**
- * A Helper to create new keystore/key.
- */
-public final class KeystoreHelper {
-
- /**
- * Creates a new store
- * @param osKeyStorePath the location of the store
- * @param storeType an optional keystore type, or <code>null</code> if the default is to
- * be used.
- * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
- * of the keytool process call.
- * @throws KeyStoreException
- * @throws NoSuchAlgorithmException
- * @throws CertificateException
- * @throws UnrecoverableEntryException
- * @throws IOException
- * @throws KeytoolException
- */
- public static boolean createNewStore(
- String osKeyStorePath,
- String storeType,
- String storePassword,
- String alias,
- String keyPassword,
- String description,
- int validityYears,
- IKeyGenOutput output)
- throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
- UnrecoverableEntryException, IOException, KeytoolException {
-
- // get the executable name of keytool depending on the platform.
- String os = System.getProperty("os.name");
-
- String keytoolCommand;
- if (os.startsWith("Windows")) {
- keytoolCommand = "keytool.exe";
- } else {
- keytoolCommand = "keytool";
- }
-
- String javaHome = System.getProperty("java.home");
-
- if (javaHome != null && javaHome.length() > 0) {
- keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand;
- }
-
- // create the command line to call key tool to build the key with no user input.
- ArrayList<String> commandList = new ArrayList<String>();
- commandList.add(keytoolCommand);
- commandList.add("-genkey");
- commandList.add("-alias");
- commandList.add(alias);
- commandList.add("-keyalg");
- commandList.add("RSA");
- commandList.add("-dname");
- commandList.add(description);
- commandList.add("-validity");
- commandList.add(Integer.toString(validityYears * 365));
- commandList.add("-keypass");
- commandList.add(keyPassword);
- commandList.add("-keystore");
- commandList.add(osKeyStorePath);
- commandList.add("-storepass");
- commandList.add(storePassword);
- if (storeType != null) {
- commandList.add("-storetype");
- commandList.add(storeType);
- }
-
- String[] commandArray = commandList.toArray(new String[commandList.size()]);
-
- // launch the command line process
- int result = 0;
- try {
- result = grabProcessOutput(Runtime.getRuntime().exec(commandArray), output);
- } catch (Exception e) {
- // create the command line as one string
- StringBuilder builder = new StringBuilder();
- boolean firstArg = true;
- for (String arg : commandArray) {
- boolean hasSpace = arg.indexOf(' ') != -1;
-
- if (firstArg == true) {
- firstArg = false;
- } else {
- builder.append(' ');
- }
-
- if (hasSpace) {
- builder.append('"');
- }
-
- builder.append(arg);
-
- if (hasSpace) {
- builder.append('"');
- }
- }
-
- throw new KeytoolException("Failed to create key: " + e.getMessage(),
- javaHome, builder.toString());
- }
-
- if (result != 0) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Get the stderr/stdout outputs of a process and return when the process is done.
- * Both <b>must</b> be read or the process will block on windows.
- * @param process The process to get the ouput from
- * @return the process return code.
- * @throws InterruptedException
- */
- private static int grabProcessOutput(final Process process, final IKeyGenOutput output) {
- // read the lines as they come. if null is returned, it's
- // because the process finished
- Thread t1 = new Thread("") {
- @Override
- public void run() {
- // create a buffer to read the stderr output
- InputStreamReader is = new InputStreamReader(process.getErrorStream());
- BufferedReader errReader = new BufferedReader(is);
-
- try {
- while (true) {
- String line = errReader.readLine();
- if (line != null) {
- if (output != null) {
- output.err(line);
- } else {
- System.err.println(line);
- }
- } else {
- break;
- }
- }
- } catch (IOException e) {
- // do nothing.
- }
- }
- };
-
- Thread t2 = new Thread("") {
- @Override
- public void run() {
- InputStreamReader is = new InputStreamReader(process.getInputStream());
- BufferedReader outReader = new BufferedReader(is);
-
- try {
- while (true) {
- String line = outReader.readLine();
- if (line != null) {
- if (output != null) {
- output.out(line);
- } else {
- System.out.println(line);
- }
- } else {
- break;
- }
- }
- } catch (IOException e) {
- // do nothing.
- }
- }
- };
-
- t1.start();
- t2.start();
-
- // it looks like on windows process#waitFor() can return
- // before the thread have filled the arrays, so we wait for both threads and the
- // process itself.
- try {
- t1.join();
- } catch (InterruptedException e) {
- }
- try {
- t2.join();
- } catch (InterruptedException e) {
- }
-
- // get the return code from the process
- try {
- return process.waitFor();
- } catch (InterruptedException e) {
- // since we're waiting for the output thread above, we should never actually wait
- // on the process to end, since it'll be done by the time we call waitFor()
- return 0;
- }
- }
-}
diff --git a/jarutils/src/com/android/jarutils/SignedJarBuilder.java b/jarutils/src/com/android/jarutils/SignedJarBuilder.java
deleted file mode 100644
index 335ab7d..0000000
--- a/jarutils/src/com/android/jarutils/SignedJarBuilder.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jarutils;
-
-import sun.misc.BASE64Encoder;
-import sun.security.pkcs.ContentInfo;
-import sun.security.pkcs.PKCS7;
-import sun.security.pkcs.SignerInfo;
-import sun.security.x509.AlgorithmId;
-import sun.security.x509.X500Name;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.security.DigestOutputStream;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.X509Certificate;
-import java.util.Map;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-/**
- * A Jar file builder with signature support.
- */
-public class SignedJarBuilder {
- private static final String DIGEST_ALGORITHM = "SHA1";
- private static final String DIGEST_ATTR = "SHA1-Digest";
- private static final String DIGEST_MANIFEST_ATTR = "SHA1-Digest-Manifest";
-
- /** Write to another stream and also feed it to the Signature object. */
- private static class SignatureOutputStream extends FilterOutputStream {
- private Signature mSignature;
-
- public SignatureOutputStream(OutputStream out, Signature sig) {
- super(out);
- mSignature = sig;
- }
-
- @Override
- public void write(int b) throws IOException {
- try {
- mSignature.update((byte) b);
- } catch (SignatureException e) {
- throw new IOException("SignatureException: " + e);
- }
- super.write(b);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- try {
- mSignature.update(b, off, len);
- } catch (SignatureException e) {
- throw new IOException("SignatureException: " + e);
- }
- super.write(b, off, len);
- }
- }
-
- private JarOutputStream mOutputJar;
- private PrivateKey mKey;
- private X509Certificate mCertificate;
- private Manifest mManifest;
- private BASE64Encoder mBase64Encoder;
- private MessageDigest mMessageDigest;
-
- private byte[] mBuffer = new byte[4096];
-
- /**
- * Classes which implement this interface provides a method to check whether a file should
- * be added to a Jar file.
- */
- public interface IZipEntryFilter {
- /**
- * Checks a file for inclusion in a Jar archive.
- * @param name the archive file path of the entry
- * @return <code>true</code> if the file should be included.
- */
- public boolean checkEntry(String name);
- }
-
- /**
- * Creates a {@link SignedJarBuilder} with a given output stream, and signing information.
- * <p/>If either <code>key</code> or <code>certificate</code> is <code>null</code> then
- * the archive will not be signed.
- * @param out the {@link OutputStream} where to write the Jar archive.
- * @param key the {@link PrivateKey} used to sign the archive, or <code>null</code>.
- * @param certificate the {@link X509Certificate} used to sign the archive, or
- * <code>null</code>.
- * @throws IOException
- * @throws NoSuchAlgorithmException
- */
- public SignedJarBuilder(OutputStream out, PrivateKey key, X509Certificate certificate)
- throws IOException, NoSuchAlgorithmException {
- mOutputJar = new JarOutputStream(out);
- mOutputJar.setLevel(9);
- mKey = key;
- mCertificate = certificate;
-
- if (mKey != null && mCertificate != null) {
- mManifest = new Manifest();
- Attributes main = mManifest.getMainAttributes();
- main.putValue("Manifest-Version", "1.0");
- main.putValue("Created-By", "1.0 (Android)");
-
- mBase64Encoder = new BASE64Encoder();
- mMessageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM);
- }
- }
-
- /**
- * Writes a new {@link File} into the archive.
- * @param inputFile the {@link File} to write.
- * @param jarPath the filepath inside the archive.
- * @throws IOException
- */
- public void writeFile(File inputFile, String jarPath) throws IOException {
- // Get an input stream on the file.
- FileInputStream fis = new FileInputStream(inputFile);
- try {
-
- // create the zip entry
- JarEntry entry = new JarEntry(jarPath);
- entry.setTime(inputFile.lastModified());
-
- writeEntry(fis, entry);
- } finally {
- // close the file stream used to read the file
- fis.close();
- }
- }
-
- /**
- * Copies the content of a Jar/Zip archive into the receiver archive.
- * <p/>An optional {@link IZipEntryFilter} allows to selectively choose which files
- * to copy over.
- * @param input the {@link InputStream} for the Jar/Zip to copy.
- * @param filter the filter or <code>null</code>
- * @throws IOException
- */
- public void writeZip(InputStream input, IZipEntryFilter filter) throws IOException {
- ZipInputStream zis = new ZipInputStream(input);
-
- try {
- // loop on the entries of the intermediary package and put them in the final package.
- ZipEntry entry;
- while ((entry = zis.getNextEntry()) != null) {
- String name = entry.getName();
-
- // do not take directories or anything inside a potential META-INF folder.
- if (entry.isDirectory() || name.startsWith("META-INF/")) {
- continue;
- }
-
- // if we have a filter, we check the entry against it
- if (filter != null && filter.checkEntry(name) == false) {
- continue;
- }
-
- JarEntry newEntry;
-
- // Preserve the STORED method of the input entry.
- if (entry.getMethod() == JarEntry.STORED) {
- newEntry = new JarEntry(entry);
- } else {
- // Create a new entry so that the compressed len is recomputed.
- newEntry = new JarEntry(name);
- }
-
- writeEntry(zis, newEntry);
-
- zis.closeEntry();
- }
- } finally {
- zis.close();
- }
- }
-
- /**
- * Closes the Jar archive by creating the manifest, and signing the archive.
- * @throws IOException
- * @throws GeneralSecurityException
- */
- public void close() throws IOException, GeneralSecurityException {
- if (mManifest != null) {
- // write the manifest to the jar file
- mOutputJar.putNextEntry(new JarEntry(JarFile.MANIFEST_NAME));
- mManifest.write(mOutputJar);
-
- // CERT.SF
- Signature signature = Signature.getInstance("SHA1with" + mKey.getAlgorithm());
- signature.initSign(mKey);
- mOutputJar.putNextEntry(new JarEntry("META-INF/CERT.SF"));
- writeSignatureFile(new SignatureOutputStream(mOutputJar, signature));
-
- // CERT.*
- mOutputJar.putNextEntry(new JarEntry("META-INF/CERT." + mKey.getAlgorithm()));
- writeSignatureBlock(signature, mCertificate, mKey);
- }
-
- mOutputJar.close();
- }
-
- /**
- * Adds an entry to the output jar, and write its content from the {@link InputStream}
- * @param input The input stream from where to write the entry content.
- * @param entry the entry to write in the jar.
- * @throws IOException
- */
- private void writeEntry(InputStream input, JarEntry entry) throws IOException {
- // add the entry to the jar archive
- mOutputJar.putNextEntry(entry);
-
- // read the content of the entry from the input stream, and write it into the archive.
- int count;
- while ((count = input.read(mBuffer)) != -1) {
- mOutputJar.write(mBuffer, 0, count);
-
- // update the digest
- if (mMessageDigest != null) {
- mMessageDigest.update(mBuffer, 0, count);
- }
- }
-
- // close the entry for this file
- mOutputJar.closeEntry();
-
- if (mManifest != null) {
- // update the manifest for this entry.
- Attributes attr = mManifest.getAttributes(entry.getName());
- if (attr == null) {
- attr = new Attributes();
- mManifest.getEntries().put(entry.getName(), attr);
- }
- attr.putValue(DIGEST_ATTR, mBase64Encoder.encode(mMessageDigest.digest()));
- }
- }
-
- /** Writes a .SF file with a digest to the manifest. */
- private void writeSignatureFile(OutputStream out)
- throws IOException, GeneralSecurityException {
- Manifest sf = new Manifest();
- Attributes main = sf.getMainAttributes();
- main.putValue("Signature-Version", "1.0");
- main.putValue("Created-By", "1.0 (Android)");
-
- BASE64Encoder base64 = new BASE64Encoder();
- MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM);
- PrintStream print = new PrintStream(
- new DigestOutputStream(new ByteArrayOutputStream(), md),
- true, "UTF-8");
-
- // Digest of the entire manifest
- mManifest.write(print);
- print.flush();
- main.putValue(DIGEST_MANIFEST_ATTR, base64.encode(md.digest()));
-
- Map<String, Attributes> entries = mManifest.getEntries();
- for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
- // Digest of the manifest stanza for this entry.
- print.print("Name: " + entry.getKey() + "\r\n");
- for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
- print.print(att.getKey() + ": " + att.getValue() + "\r\n");
- }
- print.print("\r\n");
- print.flush();
-
- Attributes sfAttr = new Attributes();
- sfAttr.putValue(DIGEST_ATTR, base64.encode(md.digest()));
- sf.getEntries().put(entry.getKey(), sfAttr);
- }
-
- sf.write(out);
- }
-
- /** Write the certificate file with a digital signature. */
- private void writeSignatureBlock(Signature signature, X509Certificate publicKey,
- PrivateKey privateKey)
- throws IOException, GeneralSecurityException {
- SignerInfo signerInfo = new SignerInfo(
- new X500Name(publicKey.getIssuerX500Principal().getName()),
- publicKey.getSerialNumber(),
- AlgorithmId.get(DIGEST_ALGORITHM),
- AlgorithmId.get(privateKey.getAlgorithm()),
- signature.sign());
-
- PKCS7 pkcs7 = new PKCS7(
- new AlgorithmId[] { AlgorithmId.get(DIGEST_ALGORITHM) },
- new ContentInfo(ContentInfo.DATA_OID, null),
- new X509Certificate[] { publicKey },
- new SignerInfo[] { signerInfo });
-
- pkcs7.encodeSignedData(mOutputJar);
- }
-}