• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
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      */
132     @SuppressWarnings("unused") // the thrown Exceptions are not actually thrown
getDebugKey()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
getCertificate()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      */
loadKeyEntry(String osKeyStorePath, String storeType)163     private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException,
164             NoSuchAlgorithmException, CertificateException, IOException,
165             UnrecoverableEntryException {
166         FileInputStream fis = null;
167         try {
168             KeyStore keyStore = KeyStore.getInstance(
169                     storeType != null ? storeType : KeyStore.getDefaultType());
170             fis = new FileInputStream(osKeyStorePath);
171             keyStore.load(fis, PASSWORD_CHAR);
172             mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
173                     DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR));
174         } catch (FileNotFoundException e) {
175             return false;
176         } finally {
177             if (fis != null) {
178                 try {
179                     fis.close();
180                 } catch (IOException e) {
181                     // pass
182                 }
183             }
184         }
185 
186         return true;
187     }
188 
189     /**
190      * Creates a new store
191      * @param osKeyStorePath the location of the store
192      * @param storeType an optional keystore type, or <code>null</code> if the default is to
193      * be used.
194      * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
195      * of the keytool process call.
196      * @throws KeyStoreException
197      * @throws NoSuchAlgorithmException
198      * @throws CertificateException
199      * @throws UnrecoverableEntryException
200      * @throws IOException
201      * @throws KeytoolException
202      */
createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)203     private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)
204             throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
205             UnrecoverableEntryException, IOException, KeytoolException {
206 
207         if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS,
208                 PASSWORD_STRING, CERTIFICATE_DESC, 30 /* validity*/, output)) {
209             loadKeyEntry(osKeyStorePath, storeType);
210         }
211     }
212 }
213