blob: a2028b8644a9153c500a27cf645b5b4b0e14904e [file] [log] [blame]
/*
* Copyright 2012 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.example.android.keychain;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.security.KeyChainException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
public class KeyChainDemoActivity extends Activity implements
KeyChainAliasCallback {
/**
* The file name of the PKCS12 file used
*/
public static final String PKCS12_FILENAME = "keychain.p12";
/**
* The pass phrase of the PKCS12 file
*/
public static final String PKCS12_PASSWORD = "changeit";
/**
* Intent extra name to indicate to stop server
*/
public static final String EXTRA_STOP_SERVER = "stop_server";
// Log tag for this class
private static final String TAG = "KeyChainApiActivity";
// Alias for certificate
private static final String DEFAULT_ALIAS = "My Key Chain";
// Name of the application preference
private static final String KEYCHAIN_PREF = "keychain";
// Name of preference name that saves the alias
private static final String KEYCHAIN_PREF_ALIAS = "alias";
// Request code used when starting the activity using the KeyChain install
// intent
private static final int INSTALL_KEYCHAIN_CODE = 1;
// Test SSL URL
private static final String TEST_SSL_URL = "https://localhost:8080";
// Button to start/stop the simple SSL web server
private Button serverButton;
// Button to install the key chain
private Button keyChainButton;
// Button to launch the browser for testing https://localhost:8080
private Button testSslButton;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the view using the main.xml layout
setContentView(R.layout.main);
// Check whether the key chain is installed or not. This takes time and
// should be done in another thread other than the main thread.
new Thread(new Runnable() {
@Override
public void run() {
if (isKeyChainAccessible()) {
// Key chain installed. Disable the install button and print
// the key chain information
disableKeyChainButton();
printInfo();
} else {
Log.d(TAG, "Key Chain is not accessible");
}
}
}).start();
// Setup the key chain installation button
keyChainButton = (Button) findViewById(R.id.keychain_button);
keyChainButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
installPkcs12();
}
});
// Setup the simple SSL web server start/stop button
serverButton = (Button) findViewById(R.id.server_button);
serverButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (serverButton.getText().equals(
getResources().getString(R.string.server_start))) {
serverButton.setText(R.string.server_stop);
startServer();
} else {
serverButton.setText(R.string.server_start);
stopServer();
}
}
});
// Setup the test SSL page button
testSslButton = (Button) findViewById(R.id.test_ssl_button);
testSslButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri
.parse(TEST_SSL_URL));
startActivity(i);
}
});
}
/**
* This will be called when the user click on the notification to stop the
* SSL server
*/
@Override
protected void onNewIntent(Intent intent) {
Log.d(TAG, "In onNewIntent()");
super.onNewIntent(intent);
boolean isStopServer = intent.getBooleanExtra(EXTRA_STOP_SERVER, false);
if (isStopServer) {
serverButton.setText(R.string.server_start);
stopServer();
}
}
/**
* This implements the KeyChainAliasCallback
*/
@Override
public void alias(String alias) {
if (alias != null) {
setAlias(alias); // Set the alias in the application preference
disableKeyChainButton();
printInfo();
} else {
Log.d(TAG, "User hit Disallow");
}
}
/**
* This method returns the alias of the key chain from the application
* preference
*
* @return The alias of the key chain
*/
private String getAlias() {
SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
MODE_PRIVATE);
return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
}
/**
* This method sets the alias of the key chain to the application preference
*/
private void setAlias(String alias) {
SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
MODE_PRIVATE);
Editor editor = pref.edit();
editor.putString(KEYCHAIN_PREF_ALIAS, alias);
editor.commit();
}
/**
* This method prints the key chain information.
*/
private void printInfo() {
String alias = getAlias();
X509Certificate[] certs = getCertificateChain(alias);
final PrivateKey privateKey = getPrivateKey(alias);
final StringBuffer sb = new StringBuffer();
for (X509Certificate cert : certs) {
sb.append(cert.getIssuerDN());
sb.append("\n");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
TextView certTv = (TextView) findViewById(R.id.cert);
TextView privateKeyTv = (TextView) findViewById(R.id.private_key);
certTv.setText(sb.toString());
privateKeyTv.setText(privateKey.getFormat() + ":" + privateKey);
}
});
}
/**
* This method will launch an intent to install the key chain
*/
private void installPkcs12() {
try {
BufferedInputStream bis = new BufferedInputStream(getAssets().open(
PKCS12_FILENAME));
byte[] keychain = new byte[bis.available()];
bis.read(keychain);
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychain);
installIntent.putExtra(KeyChain.EXTRA_NAME, DEFAULT_ALIAS);
startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INSTALL_KEYCHAIN_CODE) {
switch (resultCode) {
case Activity.RESULT_OK:
chooseCert();
break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
}
private void chooseCert() {
KeyChain.choosePrivateKeyAlias(this, this, // Callback
new String[] {}, // Any key types.
null, // Any issuers.
"localhost", // Any host
-1, // Any port
DEFAULT_ALIAS);
}
private X509Certificate[] getCertificateChain(String alias) {
try {
return KeyChain.getCertificateChain(this, alias);
} catch (KeyChainException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
private PrivateKey getPrivateKey(String alias) {
try {
return KeyChain.getPrivateKey(this, alias);
} catch (KeyChainException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* This method checks if the key chain is installed
*
* @return true if the key chain is not installed or allowed
*/
private boolean isKeyChainAccessible() {
return getCertificateChain(getAlias()) != null
&& getPrivateKey(getAlias()) != null;
}
/**
* This method starts the background service of the simple SSL web server
*/
private void startServer() {
Intent secureWebServerIntent = new Intent(this,
SecureWebServerService.class);
startService(secureWebServerIntent);
}
/**
* This method stops the background service of the simple SSL web server
*/
private void stopServer() {
Intent secureWebServerIntent = new Intent(this,
SecureWebServerService.class);
stopService(secureWebServerIntent);
}
/**
* This is a convenient method to disable the key chain install button
*/
private void disableKeyChainButton() {
runOnUiThread(new Runnable() {
@Override
public void run() {
keyChainButton.setText(R.string.keychain_installed);
keyChainButton.setEnabled(false);
}
});
}
}