326 lines
10 KiB
Java
326 lines
10 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|