Home | History | Annotate | Download | only in basicandroidkeystore
      1 /*
      2 * Copyright (C) 2013 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.basicandroidkeystore;
     18 
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.security.KeyPairGeneratorSpec;
     22 import android.support.v4.app.Fragment;
     23 import android.util.Base64;
     24 import android.view.MenuItem;
     25 
     26 import com.example.android.common.logger.Log;
     27 
     28 import java.io.IOException;
     29 import java.math.BigInteger;
     30 import java.security.InvalidAlgorithmParameterException;
     31 import java.security.InvalidKeyException;
     32 import java.security.KeyPair;
     33 import java.security.KeyPairGenerator;
     34 import java.security.KeyStore;
     35 import java.security.KeyStoreException;
     36 import java.security.NoSuchAlgorithmException;
     37 import java.security.NoSuchProviderException;
     38 import java.security.Signature;
     39 import java.security.SignatureException;
     40 import java.security.UnrecoverableEntryException;
     41 import java.security.cert.CertificateException;
     42 import java.util.Calendar;
     43 import java.util.GregorianCalendar;
     44 
     45 import javax.security.auth.x500.X500Principal;
     46 
     47 public class BasicAndroidKeyStoreFragment extends Fragment {
     48 
     49     public static final String TAG = "BasicAndroidKeyStoreFragment";
     50 
     51     // BEGIN_INCLUDE(values)
     52 
     53     public static final String SAMPLE_ALIAS = "myKey";
     54 
     55     // Some sample data to sign, and later verify using the generated signature.
     56     public static final String SAMPLE_INPUT="Hello, Android!";
     57 
     58     // Just a handy place to store the signature in between signing and verifying.
     59     public String mSignatureStr = null;
     60 
     61     // You can store multiple key pairs in the Key Store.  The string used to refer to the Key you
     62     // want to store, or later pull, is referred to as an "alias" in this case, because calling it
     63     // a key, when you use it to retrieve a key, would just be irritating.
     64     private String mAlias = null;
     65 
     66     // END_INCLUDE(values)
     67 
     68     @Override
     69     public void onCreate(Bundle savedInstanceState) {
     70         super.onCreate(savedInstanceState);
     71         setHasOptionsMenu(true);
     72         setAlias(SAMPLE_ALIAS);
     73     }
     74 
     75     @Override
     76     public void onActivityCreated(Bundle savedInstanceState) {
     77         super.onActivityCreated(savedInstanceState);
     78     }
     79 
     80     @Override
     81     public boolean onOptionsItemSelected(MenuItem item) {
     82         switch (item.getItemId()) {
     83             case R.id.btn_create_keys:
     84                 try {
     85                     createKeys(getActivity());
     86                     Log.d(TAG, "Keys created");
     87                     return true;
     88                 } catch (NoSuchAlgorithmException e) {
     89                     Log.w(TAG, "RSA not supported", e);
     90                 } catch (InvalidAlgorithmParameterException e) {
     91                     Log.w(TAG, "No such provider: AndroidKeyStore");
     92                 } catch (NoSuchProviderException e) {
     93                     Log.w(TAG, "Invalid Algorithm Parameter Exception", e);
     94                 }
     95                 return true;
     96             case R.id.btn_sign_data:
     97                 try {
     98                     mSignatureStr = signData(SAMPLE_INPUT);
     99                 } catch (KeyStoreException e) {
    100                     Log.w(TAG, "KeyStore not Initialized", e);
    101                 } catch (UnrecoverableEntryException e) {
    102                     Log.w(TAG, "KeyPair not recovered", e);
    103                 } catch (NoSuchAlgorithmException e) {
    104                     Log.w(TAG, "RSA not supported", e);
    105                 } catch (InvalidKeyException e) {
    106                     Log.w(TAG, "Invalid Key", e);
    107                 } catch (SignatureException e) {
    108                     Log.w(TAG, "Invalid Signature", e);
    109                 } catch (IOException e) {
    110                     Log.w(TAG, "IO Exception", e);
    111                 } catch (CertificateException e) {
    112                     Log.w(TAG, "Error occurred while loading certificates", e);
    113                 }
    114                 Log.d(TAG, "Signature: " + mSignatureStr);
    115                 return true;
    116 
    117             case R.id.btn_verify_data:
    118                 boolean verified = false;
    119                 try {
    120                     if (mSignatureStr != null) {
    121                         verified = verifyData(SAMPLE_INPUT, mSignatureStr);
    122                     }
    123                 } catch (KeyStoreException e) {
    124                     Log.w(TAG, "KeyStore not Initialized", e);
    125                 } catch (CertificateException e) {
    126                     Log.w(TAG, "Error occurred while loading certificates", e);
    127                 } catch (NoSuchAlgorithmException e) {
    128                     Log.w(TAG, "RSA not supported", e);
    129                 } catch (IOException e) {
    130                     Log.w(TAG, "IO Exception", e);
    131                 } catch (UnrecoverableEntryException e) {
    132                     Log.w(TAG, "KeyPair not recovered", e);
    133                 } catch (InvalidKeyException e) {
    134                     Log.w(TAG, "Invalid Key", e);
    135                 } catch (SignatureException e) {
    136                     Log.w(TAG, "Invalid Signature", e);
    137                 }
    138                 if (verified) {
    139                     Log.d(TAG, "Data Signature Verified");
    140                 } else {
    141                     Log.d(TAG, "Data not verified.");
    142                 }
    143                 return true;
    144         }
    145         return false;
    146     }
    147 
    148     /**
    149      * Creates a public and private key and stores it using the Android Key Store, so that only
    150      * this application will be able to access the keys.
    151      */
    152     public void createKeys(Context context) throws NoSuchProviderException,
    153             NoSuchAlgorithmException, InvalidAlgorithmParameterException {
    154         // BEGIN_INCLUDE(create_valid_dates)
    155         // Create a start and end time, for the validity range of the key pair that's about to be
    156         // generated.
    157         Calendar start = new GregorianCalendar();
    158         Calendar end = new GregorianCalendar();
    159         end.add(1, Calendar.YEAR);
    160         //END_INCLUDE(create_valid_dates)
    161 
    162 
    163         // BEGIN_INCLUDE(create_spec)
    164         // The KeyPairGeneratorSpec object is how parameters for your key pair are passed
    165         // to the KeyPairGenerator.  For a fun home game, count how many classes in this sample
    166         // start with the phrase "KeyPair".
    167         KeyPairGeneratorSpec spec =
    168                 new KeyPairGeneratorSpec.Builder(context)
    169                         // You'll use the alias later to retrieve the key.  It's a key for the key!
    170                         .setAlias(mAlias)
    171                                 // The subject used for the self-signed certificate of the generated pair
    172                         .setSubject(new X500Principal("CN=" + mAlias))
    173                                 // The serial number used for the self-signed certificate of the
    174                                 // generated pair.
    175                         .setSerialNumber(BigInteger.valueOf(1337))
    176                                 // Date range of validity for the generated pair.
    177                         .setStartDate(start.getTime())
    178                         .setEndDate(end.getTime())
    179                         .build();
    180         // END_INCLUDE(create_spec)
    181 
    182         // BEGIN_INCLUDE(create_keypair)
    183         // Initialize a KeyPair generator using the the intended algorithm (in this example, RSA
    184         // and the KeyStore.  This example uses the AndroidKeyStore.
    185         KeyPairGenerator kpGenerator = KeyPairGenerator
    186                 .getInstance(SecurityConstants.TYPE_RSA,
    187                         SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
    188         kpGenerator.initialize(spec);
    189         KeyPair kp = kpGenerator.generateKeyPair();
    190         Log.d(TAG, "Public Key is: " + kp.getPublic().toString());
    191         // END_INCLUDE(create_keypair)
    192     }
    193 
    194     /**
    195      * Signs the data using the key pair stored in the Android Key Store.  This signature can be
    196      * used with the data later to verify it was signed by this application.
    197      * @return A string encoding of the data signature generated
    198      */
    199     public String signData(String inputStr) throws KeyStoreException,
    200             UnrecoverableEntryException, NoSuchAlgorithmException, InvalidKeyException,
    201             SignatureException, IOException, CertificateException {
    202         byte[] data = inputStr.getBytes();
    203 
    204         // BEGIN_INCLUDE(sign_load_keystore)
    205         KeyStore ks = KeyStore.getInstance(SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
    206 
    207         // Weird artifact of Java API.  If you don't have an InputStream to load, you still need
    208         // to call "load", or it'll crash.
    209         ks.load(null);
    210 
    211         // Load the key pair from the Android Key Store
    212         KeyStore.Entry entry = ks.getEntry(mAlias, null);
    213 
    214         /* If the entry is null, keys were never stored under this alias.
    215          * Debug steps in this situation would be:
    216          * -Check the list of aliases by iterating over Keystore.aliases(), be sure the alias
    217          *   exists.
    218          * -If that's empty, verify they were both stored and pulled from the same keystore
    219          *   "AndroidKeyStore"
    220          */
    221         if (entry == null) {
    222             Log.w(TAG, "No key found under alias: " + mAlias);
    223             Log.w(TAG, "Exiting signData()...");
    224             return null;
    225         }
    226 
    227         /* If entry is not a KeyStore.PrivateKeyEntry, it might have gotten stored in a previous
    228          * iteration of your application that was using some other mechanism, or been overwritten
    229          * by something else using the same keystore with the same alias.
    230          * You can determine the type using entry.getClass() and debug from there.
    231          */
    232         if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
    233             Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    234             Log.w(TAG, "Exiting signData()...");
    235             return null;
    236         }
    237         // END_INCLUDE(sign_data)
    238 
    239         // BEGIN_INCLUDE(sign_create_signature)
    240         // This class doesn't actually represent the signature,
    241         // just the engine for creating/verifying signatures, using
    242         // the specified algorithm.
    243         Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA);
    244 
    245         // Initialize Signature using specified private key
    246         s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey());
    247 
    248         // Sign the data, store the result as a Base64 encoded String.
    249         s.update(data);
    250         byte[] signature = s.sign();
    251         String result = Base64.encodeToString(signature, Base64.DEFAULT);
    252         // END_INCLUDE(sign_data)
    253 
    254         return result;
    255     }
    256 
    257     /**
    258      * Given some data and a signature, uses the key pair stored in the Android Key Store to verify
    259      * that the data was signed by this application, using that key pair.
    260      * @param input The data to be verified.
    261      * @param signatureStr The signature provided for the data.
    262      * @return A boolean value telling you whether the signature is valid or not.
    263      */
    264     public boolean verifyData(String input, String signatureStr) throws KeyStoreException,
    265             CertificateException, NoSuchAlgorithmException, IOException,
    266             UnrecoverableEntryException, InvalidKeyException, SignatureException {
    267         byte[] data = input.getBytes();
    268         byte[] signature;
    269         // BEGIN_INCLUDE(decode_signature)
    270 
    271         // Make sure the signature string exists.  If not, bail out, nothing to do.
    272 
    273         if (signatureStr == null) {
    274             Log.w(TAG, "Invalid signature.");
    275             Log.w(TAG, "Exiting verifyData()...");
    276             return false;
    277         }
    278 
    279         try {
    280             // The signature is going to be examined as a byte array,
    281             // not as a base64 encoded string.
    282             signature = Base64.decode(signatureStr, Base64.DEFAULT);
    283         } catch (IllegalArgumentException e) {
    284             // signatureStr wasn't null, but might not have been encoded properly.
    285             // It's not a valid Base64 string.
    286             return false;
    287         }
    288         // END_INCLUDE(decode_signature)
    289 
    290         KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    291 
    292         // Weird artifact of Java API.  If you don't have an InputStream to load, you still need
    293         // to call "load", or it'll crash.
    294         ks.load(null);
    295 
    296         // Load the key pair from the Android Key Store
    297         KeyStore.Entry entry = ks.getEntry(mAlias, null);
    298 
    299         if (entry == null) {
    300             Log.w(TAG, "No key found under alias: " + mAlias);
    301             Log.w(TAG, "Exiting verifyData()...");
    302             return false;
    303         }
    304 
    305         if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
    306             Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    307             return false;
    308         }
    309 
    310         // This class doesn't actually represent the signature,
    311         // just the engine for creating/verifying signatures, using
    312         // the specified algorithm.
    313         Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA);
    314 
    315         // BEGIN_INCLUDE(verify_data)
    316         // Verify the data.
    317         s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
    318         s.update(data);
    319         boolean valid = s.verify(signature);
    320         return valid;
    321         // END_INCLUDE(verify_data)
    322     }
    323 
    324     public void setAlias(String alias) {
    325         mAlias = alias;
    326     }
    327 }
    328