Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)
BasicAndroidKeyStore / src / com.example.android.basicandroidkeystore /

BasicAndroidKeyStoreFragment.java

       
        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
       
       
       
       
        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
       
       
       
       
        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 {
       
       
        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);
       
       
        161
       
       
       
       
        162
       
       
       
       
        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();
       
       
        181
       
       
       
       
        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());
       
       
        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
       
       
       
       
        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
       
       
        }
       
       
        238
       
       
       
       
        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);
       
       
        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;
       
       
        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
       
       
        }
       
       
        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
       
       
       
       
        316
       
       
        // Verify the data.
       
       
        317
       
       
        s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
       
       
        318
       
       
        s.update(data);
       
       
        319
       
       
        boolean valid = s.verify(signature);
       
       
        320
       
       
        return valid;
       
       
        322
       
       
        }
       
       
        323
       
       
       
       
        324
       
       
        public void setAlias(String alias) {
       
       
        325
       
       
        mAlias = alias;
       
       
        326
       
       
        }
       
       
        327
       
       
        }