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