Home | History | Annotate | Download | only in keychain
      1 /*
      2  * Copyright 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.keychain;
     18 
     19 import android.app.Activity;
     20 import android.content.Intent;
     21 import android.content.SharedPreferences;
     22 import android.content.SharedPreferences.Editor;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.security.KeyChain;
     26 import android.security.KeyChainAliasCallback;
     27 import android.security.KeyChainException;
     28 import android.util.Log;
     29 import android.view.View;
     30 import android.view.View.OnClickListener;
     31 import android.widget.Button;
     32 import android.widget.TextView;
     33 
     34 import java.io.BufferedInputStream;
     35 import java.io.IOException;
     36 import java.security.PrivateKey;
     37 import java.security.cert.X509Certificate;
     38 
     39 public class KeyChainDemoActivity extends Activity implements
     40         KeyChainAliasCallback {
     41 
     42     /**
     43      * The file name of the PKCS12 file used
     44      */
     45     public static final String PKCS12_FILENAME = "keychain.p12";
     46 
     47     /**
     48      * The pass phrase of the PKCS12 file
     49      */
     50     public static final String PKCS12_PASSWORD = "changeit";
     51 
     52     /**
     53      * Intent extra name to indicate to stop server
     54      */
     55     public static final String EXTRA_STOP_SERVER = "stop_server";
     56 
     57     // Log tag for this class
     58     private static final String TAG = "KeyChainApiActivity";
     59 
     60     // Alias for certificate
     61     private static final String DEFAULT_ALIAS = "My Key Chain";
     62 
     63     // Name of the application preference
     64     private static final String KEYCHAIN_PREF = "keychain";
     65 
     66     // Name of preference name that saves the alias
     67     private static final String KEYCHAIN_PREF_ALIAS = "alias";
     68 
     69     // Request code used when starting the activity using the KeyChain install
     70     // intent
     71     private static final int INSTALL_KEYCHAIN_CODE = 1;
     72 
     73     // Test SSL URL
     74     private static final String TEST_SSL_URL = "https://localhost:8080";
     75 
     76     // Button to start/stop the simple SSL web server
     77     private Button serverButton;
     78 
     79     // Button to install the key chain
     80     private Button keyChainButton;
     81 
     82     // Button to launch the browser for testing https://localhost:8080
     83     private Button testSslButton;
     84 
     85     /** Called when the activity is first created. */
     86     @Override
     87     public void onCreate(Bundle savedInstanceState) {
     88         super.onCreate(savedInstanceState);
     89 
     90         // Set the view using the main.xml layout
     91         setContentView(R.layout.main);
     92 
     93         // Check whether the key chain is installed or not. This takes time and
     94         // should be done in another thread other than the main thread.
     95         new Thread(new Runnable() {
     96             @Override
     97             public void run() {
     98                 if (isKeyChainAccessible()) {
     99                     // Key chain installed. Disable the install button and print
    100                     // the key chain information
    101                     disableKeyChainButton();
    102                     printInfo();
    103                 } else {
    104                     Log.d(TAG, "Key Chain is not accessible");
    105                 }
    106             }
    107         }).start();
    108 
    109         // Setup the key chain installation button
    110         keyChainButton = (Button) findViewById(R.id.keychain_button);
    111         keyChainButton.setOnClickListener(new OnClickListener() {
    112             @Override
    113             public void onClick(View v) {
    114                 installPkcs12();
    115             }
    116         });
    117 
    118         // Setup the simple SSL web server start/stop button
    119         serverButton = (Button) findViewById(R.id.server_button);
    120         serverButton.setOnClickListener(new OnClickListener() {
    121             @Override
    122             public void onClick(View v) {
    123                 if (serverButton.getText().equals(
    124                         getResources().getString(R.string.server_start))) {
    125                     serverButton.setText(R.string.server_stop);
    126                     startServer();
    127                 } else {
    128                     serverButton.setText(R.string.server_start);
    129                     stopServer();
    130                 }
    131             }
    132         });
    133 
    134         // Setup the test SSL page button
    135         testSslButton = (Button) findViewById(R.id.test_ssl_button);
    136         testSslButton.setOnClickListener(new OnClickListener() {
    137             @Override
    138             public void onClick(View v) {
    139                 Intent i = new Intent(Intent.ACTION_VIEW, Uri
    140                         .parse(TEST_SSL_URL));
    141                 startActivity(i);
    142             }
    143         });
    144     }
    145 
    146     /**
    147      * This will be called when the user click on the notification to stop the
    148      * SSL server
    149      */
    150     @Override
    151     protected void onNewIntent(Intent intent) {
    152         Log.d(TAG, "In onNewIntent()");
    153         super.onNewIntent(intent);
    154         boolean isStopServer = intent.getBooleanExtra(EXTRA_STOP_SERVER, false);
    155         if (isStopServer) {
    156             serverButton.setText(R.string.server_start);
    157             stopServer();
    158         }
    159     }
    160 
    161     /**
    162      * This implements the KeyChainAliasCallback
    163      */
    164     @Override
    165     public void alias(String alias) {
    166         if (alias != null) {
    167             setAlias(alias); // Set the alias in the application preference
    168             disableKeyChainButton();
    169             printInfo();
    170         } else {
    171             Log.d(TAG, "User hit Disallow");
    172         }
    173     }
    174 
    175     /**
    176      * This method returns the alias of the key chain from the application
    177      * preference
    178      *
    179      * @return The alias of the key chain
    180      */
    181     private String getAlias() {
    182         SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
    183                 MODE_PRIVATE);
    184         return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
    185     }
    186 
    187     /**
    188      * This method sets the alias of the key chain to the application preference
    189      */
    190     private void setAlias(String alias) {
    191         SharedPreferences pref = getSharedPreferences(KEYCHAIN_PREF,
    192                 MODE_PRIVATE);
    193         Editor editor = pref.edit();
    194         editor.putString(KEYCHAIN_PREF_ALIAS, alias);
    195         editor.commit();
    196     }
    197 
    198     /**
    199      * This method prints the key chain information.
    200      */
    201     private void printInfo() {
    202         String alias = getAlias();
    203         X509Certificate[] certs = getCertificateChain(alias);
    204         final PrivateKey privateKey = getPrivateKey(alias);
    205         final StringBuffer sb = new StringBuffer();
    206         for (X509Certificate cert : certs) {
    207             sb.append(cert.getIssuerDN());
    208             sb.append("\n");
    209         }
    210         runOnUiThread(new Runnable() {
    211             @Override
    212             public void run() {
    213                 TextView certTv = (TextView) findViewById(R.id.cert);
    214                 TextView privateKeyTv = (TextView) findViewById(R.id.private_key);
    215                 certTv.setText(sb.toString());
    216                 privateKeyTv.setText(privateKey.getFormat() + ":" + privateKey);
    217             }
    218         });
    219     }
    220 
    221     /**
    222      * This method will launch an intent to install the key chain
    223      */
    224     private void installPkcs12() {
    225         try {
    226             BufferedInputStream bis = new BufferedInputStream(getAssets().open(
    227                     PKCS12_FILENAME));
    228             byte[] keychain = new byte[bis.available()];
    229             bis.read(keychain);
    230 
    231             Intent installIntent = KeyChain.createInstallIntent();
    232             installIntent.putExtra(KeyChain.EXTRA_PKCS12, keychain);
    233             installIntent.putExtra(KeyChain.EXTRA_NAME, DEFAULT_ALIAS);
    234             startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
    235         } catch (IOException e) {
    236             e.printStackTrace();
    237         }
    238     }
    239 
    240     @Override
    241     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    242         if (requestCode == INSTALL_KEYCHAIN_CODE) {
    243             switch (resultCode) {
    244                 case Activity.RESULT_OK:
    245                     chooseCert();
    246                     break;
    247                 default:
    248                     super.onActivityResult(requestCode, resultCode, data);
    249             }
    250         }
    251     }
    252 
    253     private void chooseCert() {
    254         KeyChain.choosePrivateKeyAlias(this, this, // Callback
    255                 new String[] {}, // Any key types.
    256                 null, // Any issuers.
    257                 "localhost", // Any host
    258                 -1, // Any port
    259                 DEFAULT_ALIAS);
    260     }
    261 
    262     private X509Certificate[] getCertificateChain(String alias) {
    263         try {
    264             return KeyChain.getCertificateChain(this, alias);
    265         } catch (KeyChainException e) {
    266             e.printStackTrace();
    267         } catch (InterruptedException e) {
    268             e.printStackTrace();
    269         }
    270         return null;
    271     }
    272 
    273     private PrivateKey getPrivateKey(String alias) {
    274         try {
    275             return KeyChain.getPrivateKey(this, alias);
    276         } catch (KeyChainException e) {
    277             e.printStackTrace();
    278         } catch (InterruptedException e) {
    279             e.printStackTrace();
    280         }
    281         return null;
    282     }
    283 
    284     /**
    285      * This method checks if the key chain is installed
    286      *
    287      * @return true if the key chain is not installed or allowed
    288      */
    289     private boolean isKeyChainAccessible() {
    290         return getCertificateChain(getAlias()) != null
    291                 && getPrivateKey(getAlias()) != null;
    292     }
    293 
    294     /**
    295      * This method starts the background service of the simple SSL web server
    296      */
    297     private void startServer() {
    298         Intent secureWebServerIntent = new Intent(this,
    299                 SecureWebServerService.class);
    300         startService(secureWebServerIntent);
    301     }
    302 
    303     /**
    304      * This method stops the background service of the simple SSL web server
    305      */
    306     private void stopServer() {
    307         Intent secureWebServerIntent = new Intent(this,
    308                 SecureWebServerService.class);
    309         stopService(secureWebServerIntent);
    310     }
    311 
    312     /**
    313      * This is a convenient method to disable the key chain install button
    314      */
    315     private void disableKeyChainButton() {
    316         runOnUiThread(new Runnable() {
    317             @Override
    318             public void run() {
    319                 keyChainButton.setText(R.string.keychain_installed);
    320                 keyChainButton.setEnabled(false);
    321             }
    322         });
    323     }
    324 
    325 }
    326