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.sdklib.internal.build; 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 { 41 public void out(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 62 KeytoolException(String message) { 63 super(message); 64 } 65 66 KeytoolException(String message, String javaHome, String commandLine) { 67 super(message); 68 69 mJavaHome = javaHome; 70 mCommandLine = commandLine; 71 } 72 73 public String getJavaHome() { 74 return mJavaHome; 75 } 76 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 */ 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 */ 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 */ 132 @SuppressWarnings("unused") // the thrown Exceptions are not actually thrown 133 public PrivateKey getDebugKey() throws KeyStoreException, NoSuchAlgorithmException, 134 UnrecoverableKeyException, UnrecoverableEntryException { 135 if (mEntry != null) { 136 return mEntry.getPrivateKey(); 137 } 138 139 return null; 140 } 141 142 /** 143 * Returns the debug {@link Certificate} to use to sign applications for debug purpose. 144 * @return the certificate or <code>null</code> if its creation failed. 145 */ 146 @SuppressWarnings("unused") // the thrown Exceptions are not actually thrown 147 public Certificate getCertificate() throws KeyStoreException, NoSuchAlgorithmException, 148 UnrecoverableKeyException, UnrecoverableEntryException { 149 if (mEntry != null) { 150 return mEntry.getCertificate(); 151 } 152 153 return null; 154 } 155 156 /** 157 * Loads the debug key from the keystore. 158 * @param osKeyStorePath the OS path to the keystore. 159 * @param storeType an optional keystore type, or <code>null</code> if the default is to 160 * be used. 161 * @return <code>true</code> if success, <code>false</code> if the keystore does not exist. 162 */ 163 private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException, 164 NoSuchAlgorithmException, CertificateException, IOException, 165 UnrecoverableEntryException { 166 try { 167 KeyStore keyStore = KeyStore.getInstance( 168 storeType != null ? storeType : KeyStore.getDefaultType()); 169 FileInputStream fis = new FileInputStream(osKeyStorePath); 170 keyStore.load(fis, PASSWORD_CHAR); 171 fis.close(); 172 mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( 173 DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR)); 174 } catch (FileNotFoundException e) { 175 return false; 176 } 177 178 return true; 179 } 180 181 /** 182 * Creates a new store 183 * @param osKeyStorePath the location of the store 184 * @param storeType an optional keystore type, or <code>null</code> if the default is to 185 * be used. 186 * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr 187 * of the keytool process call. 188 * @throws KeyStoreException 189 * @throws NoSuchAlgorithmException 190 * @throws CertificateException 191 * @throws UnrecoverableEntryException 192 * @throws IOException 193 * @throws KeytoolException 194 */ 195 private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output) 196 throws KeyStoreException, NoSuchAlgorithmException, CertificateException, 197 UnrecoverableEntryException, IOException, KeytoolException { 198 199 if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS, 200 PASSWORD_STRING, CERTIFICATE_DESC, 30 /* validity*/, output)) { 201 loadKeyEntry(osKeyStorePath, storeType); 202 } 203 } 204 } 205