1 /* 2 * Copyright (C) 2008 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.jarutils; 18 19 import com.android.prefs.AndroidLocation; 20 import com.android.prefs.AndroidLocation.AndroidLocationException; 21 22 import java.io.FileInputStream; 23 import java.io.FileNotFoundException; 24 import java.io.IOException; 25 import java.security.KeyStore; 26 import java.security.KeyStoreException; 27 import java.security.NoSuchAlgorithmException; 28 import java.security.PrivateKey; 29 import java.security.UnrecoverableEntryException; 30 import java.security.UnrecoverableKeyException; 31 import java.security.cert.Certificate; 32 import java.security.cert.CertificateException; 33 34 /** 35 * A provider of a dummy key to sign Android application for debugging purpose. 36 * <p/>This provider uses a custom keystore to create and store a key with a known password. 37 */ 38 public class DebugKeyProvider { 39 40 public interface IKeyGenOutput { out(String message)41 public void out(String message); err(String message)42 public void err(String message); 43 } 44 45 private static final String PASSWORD_STRING = "android"; 46 private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray(); 47 private static final String DEBUG_ALIAS = "AndroidDebugKey"; 48 49 // Certificate CN value. This is a hard-coded value for the debug key. 50 // Android Market checks against this value in order to refuse applications signed with 51 // debug keys. 52 private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US"; 53 54 private KeyStore.PrivateKeyEntry mEntry; 55 56 public static class KeytoolException extends Exception { 57 /** default serial uid */ 58 private static final long serialVersionUID = 1L; 59 private String mJavaHome = null; 60 private String mCommandLine = null; 61 KeytoolException(String message)62 KeytoolException(String message) { 63 super(message); 64 } 65 KeytoolException(String message, String javaHome, String commandLine)66 KeytoolException(String message, String javaHome, String commandLine) { 67 super(message); 68 69 mJavaHome = javaHome; 70 mCommandLine = commandLine; 71 } 72 getJavaHome()73 public String getJavaHome() { 74 return mJavaHome; 75 } 76 getCommandLine()77 public String getCommandLine() { 78 return mCommandLine; 79 } 80 } 81 82 /** 83 * Creates a provider using a keystore at the given location. 84 * <p/>The keystore, and a new random android debug key are created if they do not yet exist. 85 * <p/>Password for the store/key is <code>android</code>, and the key alias is 86 * <code>AndroidDebugKey</code>. 87 * @param osKeyStorePath the OS path to the keystore, or <code>null</code> if the default one 88 * is to be used. 89 * @param storeType an optional keystore type, or <code>null</code> if the default is to 90 * be used. 91 * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr 92 * of the keytool process call. 93 * @throws KeytoolException If the creation of the debug key failed. 94 * @throws AndroidLocationException 95 */ DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output)96 public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output) 97 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, 98 UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException { 99 100 if (osKeyStorePath == null) { 101 osKeyStorePath = getDefaultKeyStoreOsPath(); 102 } 103 104 if (loadKeyEntry(osKeyStorePath, storeType) == false) { 105 // create the store with the key 106 createNewStore(osKeyStorePath, storeType, output); 107 } 108 } 109 110 /** 111 * Returns the OS path to the default debug keystore. 112 * 113 * @return The OS path to the default debug keystore. 114 * @throws KeytoolException 115 * @throws AndroidLocationException 116 */ getDefaultKeyStoreOsPath()117 public static String getDefaultKeyStoreOsPath() 118 throws KeytoolException, AndroidLocationException { 119 String folder = AndroidLocation.getFolder(); 120 if (folder == null) { 121 throw new KeytoolException("Failed to get HOME directory!\n"); 122 } 123 String osKeyStorePath = folder + "debug.keystore"; 124 125 return osKeyStorePath; 126 } 127 128 /** 129 * Returns the debug {@link PrivateKey} to use to sign applications for debug purpose. 130 * @return the private key or <code>null</code> if its creation failed. 131 */ getDebugKey()132 public PrivateKey getDebugKey() throws KeyStoreException, NoSuchAlgorithmException, 133 UnrecoverableKeyException, UnrecoverableEntryException { 134 if (mEntry != null) { 135 return mEntry.getPrivateKey(); 136 } 137 138 return null; 139 } 140 141 /** 142 * Returns the debug {@link Certificate} to use to sign applications for debug purpose. 143 * @return the certificate or <code>null</code> if its creation failed. 144 */ getCertificate()145 public Certificate getCertificate() throws KeyStoreException, NoSuchAlgorithmException, 146 UnrecoverableKeyException, UnrecoverableEntryException { 147 if (mEntry != null) { 148 return mEntry.getCertificate(); 149 } 150 151 return null; 152 } 153 154 /** 155 * Loads the debug key from the keystore. 156 * @param osKeyStorePath the OS path to the keystore. 157 * @param storeType an optional keystore type, or <code>null</code> if the default is to 158 * be used. 159 * @return <code>true</code> if success, <code>false</code> if the keystore does not exist. 160 */ loadKeyEntry(String osKeyStorePath, String storeType)161 private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException, 162 NoSuchAlgorithmException, CertificateException, IOException, 163 UnrecoverableEntryException { 164 try { 165 KeyStore keyStore = KeyStore.getInstance( 166 storeType != null ? storeType : KeyStore.getDefaultType()); 167 FileInputStream fis = new FileInputStream(osKeyStorePath); 168 keyStore.load(fis, PASSWORD_CHAR); 169 fis.close(); 170 mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( 171 DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR)); 172 } catch (FileNotFoundException e) { 173 return false; 174 } 175 176 return true; 177 } 178 179 /** 180 * Creates a new store 181 * @param osKeyStorePath the location of the store 182 * @param storeType an optional keystore type, or <code>null</code> if the default is to 183 * be used. 184 * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr 185 * of the keytool process call. 186 * @throws KeyStoreException 187 * @throws NoSuchAlgorithmException 188 * @throws CertificateException 189 * @throws UnrecoverableEntryException 190 * @throws IOException 191 * @throws KeytoolException 192 */ createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)193 private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output) 194 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, 195 UnrecoverableEntryException, IOException, KeytoolException { 196 197 if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS, 198 PASSWORD_STRING, CERTIFICATE_DESC, 1 /* validity*/, output)) { 199 loadKeyEntry(osKeyStorePath, storeType); 200 } 201 } 202 } 203