/* * Copyright (C) 2012 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.ant; import com.android.sdklib.internal.build.SignedJarBuilder; import com.android.sdklib.internal.build.SignedJarBuilder.IZipEntryFilter; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.Path; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.cert.X509Certificate; /** * Simple Task to sign an apk. * */ public class SignApkTask extends SingleInputOutputTask { private String mKeystore; private String mStorepass; private String mAlias; private String mKeypass; public void setKeystore(Path keystore) { mKeystore = TaskHelper.checkSinglePath("keystore", keystore); } public void setStorepass(String storepass) { mStorepass = storepass; } public void setAlias(String alias) { mAlias = alias; } public void setKeypass(String keypass) { mKeypass = keypass; } @Override protected void createOutput() throws BuildException { PrivateKeyEntry key = loadKeyEntry( mKeystore, null, mStorepass.toCharArray(), mAlias, mKeypass.toCharArray()); if (key == null) { throw new BuildException(String.format("Signing key %s not found", mAlias)); } SignedJarBuilder mBuilder = null; try { mBuilder = new SignedJarBuilder( new FileOutputStream(getOutput(), false /* append */), key.getPrivateKey(), (X509Certificate) key.getCertificate()); mBuilder.writeZip(new FileInputStream(getInput()), new NullZipFilter()); mBuilder.close(); } catch (FileNotFoundException e) { throw new BuildException(String.format("Keystore '%s' is not found!", mKeystore)); } catch (Exception e) { throw new BuildException(e.getMessage()); } finally { if (mBuilder != null) { mBuilder.cleanUp(); } } } /** * 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 PrivateKeyEntry loadKeyEntry(String osKeyStorePath, String storeType, char[] storePassword, String alias, char[] aliasPassword) { FileInputStream fis = null; try { KeyStore keyStore = KeyStore.getInstance( storeType != null ? storeType : KeyStore.getDefaultType()); fis = new FileInputStream(osKeyStorePath); keyStore.load(fis, storePassword); return (KeyStore.PrivateKeyEntry)keyStore.getEntry( alias, new KeyStore.PasswordProtection(aliasPassword)); } catch (Exception e) { String msg = e.getMessage(); String causeMsg = null; Throwable cause = e.getCause(); if (cause != null) { causeMsg = cause.getMessage(); } if (msg != null) { if (causeMsg == null) { throw new BuildException(msg); } else { throw new BuildException(msg + ": " + causeMsg); } } else { if (causeMsg == null) { throw new BuildException(e); } else { throw new BuildException(causeMsg); } } } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { // pass } } } } private static final class NullZipFilter implements IZipEntryFilter { @Override public boolean checkEntry(String archivePath) throws ZipAbortException { return true; } } }