Skip to content

Most visited

Recently visited

navigation
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(Calendar.YEAR, 1);
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
        return s.verify(signature);
321
    }
322
 
323
    public void setAlias(String alias) {
324
        mAlias = alias;
325
    }
326
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.