/*
* Copyright (C) 2009 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.certinstaller;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import java.io.Serializable;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Installs certificates to the system keystore.
*/
public class CertInstaller extends Activity {
private static final String TAG = "CertInstaller";
private static final int STATE_INIT = 1;
private static final int STATE_RUNNING = 2;
private static final int STATE_PAUSED = 3;
private static final int NAME_CREDENTIAL_DIALOG = 1;
private static final int PKCS12_PASSWORD_DIALOG = 2;
private static final int PROGRESS_BAR_DIALOG = 3;
private static final int REQUEST_SYSTEM_INSTALL_CODE = 1;
// key to states Bundle
private static final String NEXT_ACTION_KEY = "na";
// key to KeyStore
private static final String PKEY_MAP_KEY = "PKEY_MAP";
private final KeyStore mKeyStore = KeyStore.getInstance();
private final ViewHelper mView = new ViewHelper();
private int mState;
private CredentialHelper mCredentials;
private MyAction mNextAction;
private CredentialHelper createCredentialHelper(Intent intent) {
try {
return new CredentialHelper(intent);
} catch (Throwable t) {
Log.w(TAG, "createCredentialHelper", t);
toastErrorAndFinish(R.string.invalid_cert);
return new CredentialHelper();
}
}
@Override
protected void onCreate(Bundle savedStates) {
super.onCreate(savedStates);
mCredentials = createCredentialHelper(getIntent());
mState = (savedStates == null) ? STATE_INIT : STATE_RUNNING;
if (mState == STATE_INIT) {
if (!mCredentials.containsAnyRawData()) {
toastErrorAndFinish(R.string.no_cert_to_saved);
finish();
} else if (mCredentials.hasPkcs12KeyStore()) {
showDialog(PKCS12_PASSWORD_DIALOG);
} else {
MyAction action = new InstallOthersAction();
if (needsKeyStoreAccess()) {
sendUnlockKeyStoreIntent();
mNextAction = action;
} else {
action.run(this);
}
}
} else {
mCredentials.onRestoreStates(savedStates);
mNextAction = (MyAction)
savedStates.getSerializable(NEXT_ACTION_KEY);
}
}
@Override
protected void onResume() {
super.onResume();
if (mState == STATE_INIT) {
mState = STATE_RUNNING;
} else {
if (mNextAction != null) {
mNextAction.run(this);
}
}
}
private boolean needsKeyStoreAccess() {
return ((mCredentials.hasKeyPair() || mCredentials.hasUserCertificate())
&& (mKeyStore.state() != KeyStore.State.UNLOCKED));
}
@Override
protected void onPause() {
super.onPause();
mState = STATE_PAUSED;
}
@Override
protected void onSaveInstanceState(Bundle outStates) {
super.onSaveInstanceState(outStates);
mCredentials.onSaveStates(outStates);
if (mNextAction != null) {
outStates.putSerializable(NEXT_ACTION_KEY, mNextAction);
}
}
@Override
protected Dialog onCreateDialog (int dialogId) {
switch (dialogId) {
case PKCS12_PASSWORD_DIALOG:
return createPkcs12PasswordDialog();
case NAME_CREDENTIAL_DIALOG:
return createNameCredentialDialog();
case PROGRESS_BAR_DIALOG:
ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage(getString(R.string.extracting_pkcs12));
dialog.setIndeterminate(true);
dialog.setCancelable(false);
return dialog;
default:
return null;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SYSTEM_INSTALL_CODE) {
if (resultCode == RESULT_OK) {
Log.d(TAG, "credential is added: " + mCredentials.getName());
Toast.makeText(this, getString(R.string.cert_is_added,
mCredentials.getName()), Toast.LENGTH_LONG).show();
if (mCredentials.hasCaCerts()) {
// more work to do, don't finish just yet
new InstallCaCertsToKeyChainTask().execute();
return;
}
setResult(RESULT_OK);
} else {
Log.d(TAG, "credential not saved, err: " + resultCode);
toastErrorAndFinish(R.string.cert_not_saved);
}
} else {
Log.w(TAG, "unknown request code: " + requestCode);
}
finish();
}
private class InstallCaCertsToKeyChainTask extends AsyncTask<Void, Void, Boolean> {
@Override protected Boolean doInBackground(Void... unused) {
try {
KeyChainConnection keyChainConnection = KeyChain.bind(CertInstaller.this);
try {
return mCredentials.installCaCertsToKeyChain(keyChainConnection.getService());
} finally {
keyChainConnection.close();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
@Override protected void onPostExecute(Boolean success) {
if (success) {
setResult(RESULT_OK);
}
finish();
}
}
void installOthers() {
if (mCredentials.hasKeyPair()) {
saveKeyPair();
finish();
} else {
X509Certificate cert = mCredentials.getUserCertificate();
if (cert != null) {
// find matched private key
String key = Util.toMd5(cert.getPublicKey().getEncoded());
Map<String, byte[]> map = getPkeyMap();
byte[] privatekey = map.get(key);
if (privatekey != null) {
Log.d(TAG, "found matched key: " + privatekey);
map.remove(key);
savePkeyMap(map);
mCredentials.setPrivateKey(privatekey);
} else {
Log.d(TAG, "didn't find matched private key: " + key);
}
}
nameCredential();
}
}
private void sendUnlockKeyStoreIntent() {
Credentials.getInstance().unlock(this);
}
pr