diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:16 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:16 -0800 |
commit | 82ea7a177797b844b252effea5c7c7c5d63ea4ac (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /jarutils/src | |
parent | c9432be76d50a527da232d518f633add2f76242b (diff) | |
download | sdk-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.mk | 14 | ||||
-rw-r--r-- | jarutils/src/com/android/jarutils/DebugKeyProvider.java | 202 | ||||
-rw-r--r-- | jarutils/src/com/android/jarutils/JavaResourceFilter.java | 96 | ||||
-rw-r--r-- | jarutils/src/com/android/jarutils/KeystoreHelper.java | 228 | ||||
-rw-r--r-- | jarutils/src/com/android/jarutils/SignedJarBuilder.java | 324 |
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); - } -} |