| /* |
| * 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); |
| } |
| }); |
| } |
| |
| } |