• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)69     public void onCreate(Bundle savedInstanceState) {
70         super.onCreate(savedInstanceState);
71         setHasOptionsMenu(true);
72         setAlias(SAMPLE_ALIAS);
73     }
74 
75     @Override
onActivityCreated(Bundle savedInstanceState)76     public void onActivityCreated(Bundle savedInstanceState) {
77         super.onActivityCreated(savedInstanceState);
78     }
79 
80     @Override
onOptionsItemSelected(MenuItem item)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      */
createKeys(Context context)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      */
signData(String inputStr)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      */
verifyData(String input, String signatureStr)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 
setAlias(String alias)324     public void setAlias(String alias) {
325         mAlias = alias;
326     }
327 }
328