diff options
| author | Dirk Dougherty <ddougherty@google.com> | 2013-10-29 20:56:17 -0700 |
|---|---|---|
| committer | Dirk Dougherty <ddougherty@google.com> | 2013-10-29 20:56:17 -0700 |
| commit | 4b737b695e4e72b26c995400e58d566b0c92f545 (patch) | |
| tree | fcbb54cef59145c8c5fac9b6c2073febe76881a2 /samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java | |
| parent | 7165109e6d189ce518f6d3f0d1e09289c1d056c9 (diff) | |
Add prebuilt browseable samples as static files.
Change-Id: Ifb5382223343400882834d2dd9c182c3df602e34
Diffstat (limited to 'samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java')
| -rw-r--r-- | samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java new file mode 100644 index 000000000..12873e847 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java @@ -0,0 +1,327 @@ +/* +* Copyright (C) 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.basicandroidkeystore; + +import android.content.Context; +import android.os.Bundle; +import android.security.KeyPairGeneratorSpec; +import android.support.v4.app.Fragment; +import android.util.Base64; +import android.view.MenuItem; + +import com.example.android.common.logger.Log; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import javax.security.auth.x500.X500Principal; + +public class BasicAndroidKeyStoreFragment extends Fragment { + + public static final String TAG = "BasicAndroidKeyStoreFragment"; + + // BEGIN_INCLUDE(values) + + public static final String SAMPLE_ALIAS = "myKey"; + + // Some sample data to sign, and later verify using the generated signature. + public static final String SAMPLE_INPUT="Hello, Android!"; + + // Just a handy place to store the signature in between signing and verifying. + public String mSignatureStr = null; + + // You can store multiple key pairs in the Key Store. The string used to refer to the Key you + // want to store, or later pull, is referred to as an "alias" in this case, because calling it + // a key, when you use it to retrieve a key, would just be irritating. + private String mAlias = null; + + // END_INCLUDE(values) + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + setAlias(SAMPLE_ALIAS); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.btn_create_keys: + try { + createKeys(getActivity()); + Log.d(TAG, "Keys created"); + return true; + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (InvalidAlgorithmParameterException e) { + Log.w(TAG, "No such provider: AndroidKeyStore"); + } catch (NoSuchProviderException e) { + Log.w(TAG, "Invalid Algorithm Parameter Exception", e); + } + return true; + case R.id.btn_sign_data: + try { + mSignatureStr = signData(SAMPLE_INPUT); + } catch (KeyStoreException e) { + Log.w(TAG, "KeyStore not Initialized", e); + } catch (UnrecoverableEntryException e) { + Log.w(TAG, "KeyPair not recovered", e); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (InvalidKeyException e) { + Log.w(TAG, "Invalid Key", e); + } catch (SignatureException e) { + Log.w(TAG, "Invalid Signature", e); + } catch (IOException e) { + Log.w(TAG, "IO Exception", e); + } catch (CertificateException e) { + Log.w(TAG, "Error occurred while loading certificates", e); + } + Log.d(TAG, "Signature: " + mSignatureStr); + return true; + + case R.id.btn_verify_data: + boolean verified = false; + try { + if (mSignatureStr != null) { + verified = verifyData(SAMPLE_INPUT, mSignatureStr); + } + } catch (KeyStoreException e) { + Log.w(TAG, "KeyStore not Initialized", e); + } catch (CertificateException e) { + Log.w(TAG, "Error occurred while loading certificates", e); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (IOException e) { + Log.w(TAG, "IO Exception", e); + } catch (UnrecoverableEntryException e) { + Log.w(TAG, "KeyPair not recovered", e); + } catch (InvalidKeyException e) { + Log.w(TAG, "Invalid Key", e); + } catch (SignatureException e) { + Log.w(TAG, "Invalid Signature", e); + } + if (verified) { + Log.d(TAG, "Data Signature Verified"); + } else { + Log.d(TAG, "Data not verified."); + } + return true; + } + return false; + } + + /** + * Creates a public and private key and stores it using the Android Key Store, so that only + * this application will be able to access the keys. + */ + public void createKeys(Context context) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException { + // BEGIN_INCLUDE(create_valid_dates) + // Create a start and end time, for the validity range of the key pair that's about to be + // generated. + Calendar start = new GregorianCalendar(); + Calendar end = new GregorianCalendar(); + end.add(1, Calendar.YEAR); + //END_INCLUDE(create_valid_dates) + + + // BEGIN_INCLUDE(create_spec) + // The KeyPairGeneratorSpec object is how parameters for your key pair are passed + // to the KeyPairGenerator. For a fun home game, count how many classes in this sample + // start with the phrase "KeyPair". + KeyPairGeneratorSpec spec = + new KeyPairGeneratorSpec.Builder(context) + // You'll use the alias later to retrieve the key. It's a key for the key! + .setAlias(mAlias) + // The subject used for the self-signed certificate of the generated pair + .setSubject(new X500Principal("CN=" + mAlias)) + // The serial number used for the self-signed certificate of the + // generated pair. + .setSerialNumber(BigInteger.valueOf(1337)) + // Date range of validity for the generated pair. + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + // END_INCLUDE(create_spec) + + // BEGIN_INCLUDE(create_keypair) + // Initialize a KeyPair generator using the the intended algorithm (in this example, RSA + // and the KeyStore. This example uses the AndroidKeyStore. + KeyPairGenerator kpGenerator = KeyPairGenerator + .getInstance(SecurityConstants.TYPE_RSA, + SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE); + kpGenerator.initialize(spec); + KeyPair kp = kpGenerator.generateKeyPair(); + Log.d(TAG, "Public Key is: " + kp.getPublic().toString()); + // END_INCLUDE(create_keypair) + } + + /** + * Signs the data using the key pair stored in the Android Key Store. This signature can be + * used with the data later to verify it was signed by this application. + * @return A string encoding of the data signature generated + */ + public String signData(String inputStr) throws KeyStoreException, + UnrecoverableEntryException, NoSuchAlgorithmException, InvalidKeyException, + SignatureException, IOException, CertificateException { + byte[] data = inputStr.getBytes(); + + // BEGIN_INCLUDE(sign_load_keystore) + KeyStore ks = KeyStore.getInstance(SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE); + + // Weird artifact of Java API. If you don't have an InputStream to load, you still need + // to call "load", or it'll crash. + ks.load(null); + + // Load the key pair from the Android Key Store + KeyStore.Entry entry = ks.getEntry(mAlias, null); + + /* If the entry is null, keys were never stored under this alias. + * Debug steps in this situation would be: + * -Check the list of aliases by iterating over Keystore.aliases(), be sure the alias + * exists. + * -If that's empty, verify they were both stored and pulled from the same keystore + * "AndroidKeyStore" + */ + if (entry == null) { + Log.w(TAG, "No key found under alias: " + mAlias); + Log.w(TAG, "Exiting signData()..."); + return null; + } + + /* If entry is not a KeyStore.PrivateKeyEntry, it might have gotten stored in a previous + * iteration of your application that was using some other mechanism, or been overwritten + * by something else using the same keystore with the same alias. + * You can determine the type using entry.getClass() and debug from there. + */ + if (!(entry instanceof KeyStore.PrivateKeyEntry)) { + Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + Log.w(TAG, "Exiting signData()..."); + return null; + } + // END_INCLUDE(sign_data) + + // BEGIN_INCLUDE(sign_create_signature) + // This class doesn't actually represent the signature, + // just the engine for creating/verifying signatures, using + // the specified algorithm. + Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA); + + // Initialize Signature using specified private key + s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey()); + + // Sign the data, store the result as a Base64 encoded String. + s.update(data); + byte[] signature = s.sign(); + String result = Base64.encodeToString(signature, Base64.DEFAULT); + // END_INCLUDE(sign_data) + + return result; + } + + /** + * Given some data and a signature, uses the key pair stored in the Android Key Store to verify + * that the data was signed by this application, using that key pair. + * @param input The data to be verified. + * @param signatureStr The signature provided for the data. + * @return A boolean value telling you whether the signature is valid or not. + */ + public boolean verifyData(String input, String signatureStr) throws KeyStoreException, + CertificateException, NoSuchAlgorithmException, IOException, + UnrecoverableEntryException, InvalidKeyException, SignatureException { + byte[] data = input.getBytes(); + byte[] signature; + // BEGIN_INCLUDE(decode_signature) + + // Make sure the signature string exists. If not, bail out, nothing to do. + + if (signatureStr == null) { + Log.w(TAG, "Invalid signature."); + Log.w(TAG, "Exiting verifyData()..."); + return false; + } + + try { + // The signature is going to be examined as a byte array, + // not as a base64 encoded string. + signature = Base64.decode(signatureStr, Base64.DEFAULT); + } catch (IllegalArgumentException e) { + // signatureStr wasn't null, but might not have been encoded properly. + // It's not a valid Base64 string. + return false; + } + // END_INCLUDE(decode_signature) + + KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + + // Weird artifact of Java API. If you don't have an InputStream to load, you still need + // to call "load", or it'll crash. + ks.load(null); + + // Load the key pair from the Android Key Store + KeyStore.Entry entry = ks.getEntry(mAlias, null); + + if (entry == null) { + Log.w(TAG, "No key found under alias: " + mAlias); + Log.w(TAG, "Exiting verifyData()..."); + return false; + } + + if (!(entry instanceof KeyStore.PrivateKeyEntry)) { + Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + return false; + } + + // This class doesn't actually represent the signature, + // just the engine for creating/verifying signatures, using + // the specified algorithm. + Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA); + + // BEGIN_INCLUDE(verify_data) + // Verify the data. + s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate()); + s.update(data); + boolean valid = s.verify(signature); + return valid; + // END_INCLUDE(verify_data) + } + + public void setAlias(String alias) { + mAlias = alias; + } +} |
