/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.jarutils;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
/**
* A provider of a dummy key to sign Android application for debugging purpose.
* <p/>This provider uses a custom keystore to create and store a key with a known password.
*/
public class DebugKeyProvider {
public interface IKeyGenOutput {
public void out(String message);
public void err(String message);
}
private static final String PASSWORD_STRING = "android";
private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray();
private static final String DEBUG_ALIAS = "AndroidDebugKey";
// Certificate CN value. This is a hard-coded value for the debug key.
// Android Market checks against this value in order to refuse applications signed with
// debug keys.
private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US";
private KeyStore.PrivateKeyEntry mEntry;
public static class KeytoolException extends Exception {
/** default serial uid */
private static final long serialVersionUID = 1L;
private String mJavaHome = null;
private String mCommandLine = null;
KeytoolException(String message) {
super(message);
}
KeytoolException(String message, String javaHome, String commandLine) {
super(message);
mJavaHome = javaHome;
mCommandLine = commandLine;
}
public String getJavaHome() {
return mJavaHome;
}
public String getCommandLine() {
return mCommandLine;
}
}
/**
* Creates a provider using a keystore at the given location.
* <p/>The keystore, and a new random android debug key are created if they do not yet exist.
* <p/>Password for the store/key is <code>android</code>, and the key alias is
* <code>AndroidDebugKey</code>.
* @param osKeyStorePath the OS path to the keystore, or <code>null</code> if the default one
* is to be used.
* @param storeType an optional keystore type, or <code>null</code> if the default is to
* be used.
* @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
* of the keytool process call.
* @throws KeytoolException If the creation of the debug key failed.
* @throws AndroidLocationException
*/
public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException {
if (osKeyStorePath == null) {
osKeyStorePath = getDefaultKeyStoreOsPath();
}
if (loadKeyEntry(osKeyStorePath, storeType) == false) {
// create the store with the key
createNewStore(osKeyStorePath, storeType, output);
}
}
/**
* Returns the OS path to the default debug keystore.
*
* @return The OS path to the default debug keystore.
* @throws KeytoolException
* @throws AndroidLocationException
*/
public static String getDefaultKeyStoreOsPath()
throws KeytoolException, AndroidLocationException {
String folder = AndroidLocation.getFolder();
if (folder == null) {
throw new KeytoolException("Failed to get HOME directory!\n");
}
String osKeyStorePath = folder + "debug.keystore";
return osKeyStorePath;
}
/**
* Returns the debug {@link PrivateKey} to use to sign applications for debug purpose.
* @return the private key or <code>null</code> if its creation failed.
*/
public PrivateKey getDebugKey() throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException, UnrecoverableEntryException {
if (mEntry != null) {
return mEntry.getPrivateKey();
}
return null;
}
/**
* Returns the debug {@link Certificate} to use to sign applications for debug purpose.
* @return the certificate or <code>null</code> if its creation failed.
*/
public Certificate getCertificate() throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException, UnrecoverableEntryException {
if (mEntry != null) {
return mEntry.getCertificate();
}
return null;
}
/**
* Loads the debug key from the keystore.
* @param osKeyStorePath the OS path to the keystore.
* @param storeType an optional keystore type, or <code>null</code> if the default is to
* be used.
* @return <code>true</code> if success, <code>false</code> if the keystore does not exist.
*/
private boolean loadKeyEntry(String osKeyStorePath, String storeType) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException,
UnrecoverableEntryException {
try {
KeyStore keyStore = KeyStore.getInstance(
storeType != null ? storeType : KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream(osKeyStorePath);
keyStore.load(fis, PASSWORD_CHAR);
fis.close();
mEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
DEBUG_ALIAS, new KeyStore.PasswordProtection(PASSWORD_CHAR));
} catch (FileNotFoundException e) {
return false;
}
return true;
}
/**
* Creates a new store
* @param osKeyStorePath the location of the store
* @param storeType an optional keystore type, or <code>null</code> if the default is to
* be used.
* @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr
* of the keytool process call.
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws UnrecoverableEntryException
* @throws IOException
* @throws KeytoolException
*/
private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
UnrecoverableEntryException, IOException, KeytoolException {
if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS,
PASSWORD_STRING, CERTIFICATE_DESC, 1 /* validity*/, output)) {
loadKeyEntry(osKeyStorePath, storeType);
}
}
}
|