| /* |
| * 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.vault; |
| |
| import android.content.Context; |
| import android.security.KeyPairGeneratorSpec; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.security.GeneralSecurityException; |
| import java.security.KeyPair; |
| import java.security.KeyPairGenerator; |
| import java.security.KeyStore; |
| import java.util.Calendar; |
| import java.util.GregorianCalendar; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.SecretKey; |
| import javax.security.auth.x500.X500Principal; |
| |
| /** |
| * Wraps {@link SecretKey} instances using a public/private key pair stored in |
| * the platform {@link KeyStore}. This allows us to protect symmetric keys with |
| * hardware-backed crypto, if provided by the device. |
| * <p> |
| * See <a href="http://en.wikipedia.org/wiki/Key_Wrap">key wrapping</a> for more |
| * details. |
| * <p> |
| * Not inherently thread safe. |
| */ |
| public class SecretKeyWrapper { |
| private final Cipher mCipher; |
| private final KeyPair mPair; |
| |
| /** |
| * Create a wrapper using the public/private key pair with the given alias. |
| * If no pair with that alias exists, it will be generated. |
| */ |
| public SecretKeyWrapper(Context context, String alias) |
| throws GeneralSecurityException, IOException { |
| mCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); |
| |
| final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); |
| keyStore.load(null); |
| |
| if (!keyStore.containsAlias(alias)) { |
| generateKeyPair(context, alias); |
| } |
| |
| // Even if we just generated the key, always read it back to ensure we |
| // can read it successfully. |
| final KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry( |
| alias, null); |
| mPair = new KeyPair(entry.getCertificate().getPublicKey(), entry.getPrivateKey()); |
| } |
| |
| private static void generateKeyPair(Context context, String alias) |
| throws GeneralSecurityException { |
| final Calendar start = new GregorianCalendar(); |
| final Calendar end = new GregorianCalendar(); |
| end.add(Calendar.YEAR, 100); |
| |
| final KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) |
| .setAlias(alias) |
| .setSubject(new X500Principal("CN=" + alias)) |
| .setSerialNumber(BigInteger.ONE) |
| .setStartDate(start.getTime()) |
| .setEndDate(end.getTime()) |
| .build(); |
| |
| final KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); |
| gen.initialize(spec); |
| gen.generateKeyPair(); |
| } |
| |
| /** |
| * Wrap a {@link SecretKey} using the public key assigned to this wrapper. |
| * Use {@link #unwrap(byte[])} to later recover the original |
| * {@link SecretKey}. |
| * |
| * @return a wrapped version of the given {@link SecretKey} that can be |
| * safely stored on untrusted storage. |
| */ |
| public byte[] wrap(SecretKey key) throws GeneralSecurityException { |
| mCipher.init(Cipher.WRAP_MODE, mPair.getPublic()); |
| return mCipher.wrap(key); |
| } |
| |
| /** |
| * Unwrap a {@link SecretKey} using the private key assigned to this |
| * wrapper. |
| * |
| * @param blob a wrapped {@link SecretKey} as previously returned by |
| * {@link #wrap(SecretKey)}. |
| */ |
| public SecretKey unwrap(byte[] blob) throws GeneralSecurityException { |
| mCipher.init(Cipher.UNWRAP_MODE, mPair.getPrivate()); |
| return (SecretKey) mCipher.unwrap(blob, "AES", Cipher.SECRET_KEY); |
| } |
| } |