Home | History | Annotate | Download | only in ant
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ant;
     18 
     19 import com.android.sdklib.internal.build.SignedJarBuilder;
     20 import com.android.sdklib.internal.build.SignedJarBuilder.IZipEntryFilter;
     21 
     22 import org.apache.tools.ant.BuildException;
     23 import org.apache.tools.ant.types.Path;
     24 
     25 import java.io.FileInputStream;
     26 import java.io.FileNotFoundException;
     27 import java.io.FileOutputStream;
     28 import java.io.IOException;
     29 import java.security.KeyStore;
     30 import java.security.KeyStore.PrivateKeyEntry;
     31 import java.security.cert.X509Certificate;
     32 
     33 /**
     34  * Simple Task to sign an apk.
     35  *
     36  */
     37 public class SignApkTask extends SingleInputOutputTask {
     38 
     39     private String mKeystore;
     40     private String mStorepass;
     41     private String mAlias;
     42     private String mKeypass;
     43 
     44     public void setKeystore(Path keystore) {
     45         mKeystore = TaskHelper.checkSinglePath("keystore", keystore);
     46     }
     47 
     48     public void setStorepass(String storepass) {
     49         mStorepass = storepass;
     50     }
     51 
     52     public void setAlias(String alias) {
     53         mAlias = alias;
     54     }
     55 
     56     public void setKeypass(String keypass) {
     57         mKeypass = keypass;
     58     }
     59 
     60     @Override
     61     protected void createOutput() throws BuildException {
     62         PrivateKeyEntry key = loadKeyEntry(
     63                 mKeystore, null, mStorepass.toCharArray(),
     64                 mAlias, mKeypass.toCharArray());
     65 
     66         if (key == null) {
     67             throw new BuildException(String.format("Signing key %s not found", mAlias));
     68         }
     69 
     70         SignedJarBuilder mBuilder = null;
     71         try {
     72             mBuilder = new SignedJarBuilder(
     73                     new FileOutputStream(getOutput(), false /* append */),
     74                     key.getPrivateKey(), (X509Certificate) key.getCertificate());
     75 
     76             mBuilder.writeZip(new FileInputStream(getInput()), new NullZipFilter());
     77 
     78             mBuilder.close();
     79         } catch (FileNotFoundException e) {
     80             throw new BuildException(String.format("Keystore '%s' is not found!", mKeystore));
     81         } catch (Exception e) {
     82             throw new BuildException(e.getMessage());
     83         } finally {
     84             if (mBuilder != null) {
     85                 mBuilder.cleanUp();
     86             }
     87         }
     88     }
     89 
     90     /**
     91      * Loads the debug key from the keystore.
     92      * @param osKeyStorePath the OS path to the keystore.
     93      * @param storeType an optional keystore type, or <code>null</code> if the default is to
     94      * be used.
     95      * @return <code>true</code> if success, <code>false</code> if the keystore does not exist.
     96      */
     97     private PrivateKeyEntry loadKeyEntry(String osKeyStorePath, String storeType,
     98             char[] storePassword, String alias, char[] aliasPassword) {
     99         FileInputStream fis = null;
    100         try {
    101             KeyStore keyStore = KeyStore.getInstance(
    102                     storeType != null ? storeType : KeyStore.getDefaultType());
    103             fis = new FileInputStream(osKeyStorePath);
    104             keyStore.load(fis, storePassword);
    105             return (KeyStore.PrivateKeyEntry)keyStore.getEntry(
    106                     alias, new KeyStore.PasswordProtection(aliasPassword));
    107         } catch (Exception e) {
    108             String msg = e.getMessage();
    109             String causeMsg = null;
    110 
    111             Throwable cause = e.getCause();
    112             if (cause != null) {
    113                 causeMsg = cause.getMessage();
    114             }
    115 
    116             if (msg != null) {
    117                 if (causeMsg == null) {
    118                     throw new BuildException(msg);
    119                 } else {
    120                     throw new BuildException(msg + ": " + causeMsg);
    121                 }
    122             } else {
    123                 if (causeMsg == null) {
    124                     throw new BuildException(e);
    125                 } else {
    126                     throw new BuildException(causeMsg);
    127                 }
    128             }
    129         } finally {
    130             if (fis != null) {
    131                 try {
    132                     fis.close();
    133                 } catch (IOException e) {
    134                     // pass
    135                 }
    136             }
    137         }
    138     }
    139 
    140     private final static class NullZipFilter implements IZipEntryFilter {
    141 
    142         @Override
    143         public boolean checkEntry(String archivePath) throws ZipAbortException {
    144             return true;
    145         }
    146     }
    147 }
    148