Home | History | Annotate | Download | only in build
      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