Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b90ee2d authored by Kenny Root's avatar Kenny Root Committed by Android (Google) Code Review
Browse files

Merge "Add KeyPairGenerator for Android keystore" into jb-mr1-dev

parents 2bd65b61 db026710
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -19988,6 +19988,10 @@ package android.sax {
package android.security {
  public class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
    ctor public AndroidKeyPairGeneratorSpec(android.content.Context, java.lang.String, javax.security.auth.x500.X500Principal, java.math.BigInteger, java.util.Date, java.util.Date);
  }
  public final class KeyChain {
    ctor public KeyChain();
    method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
+163 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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 android.security;

import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;

import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyPairGeneratorSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

/**
 * Provides a way to create instances of a KeyPair which will be placed in the
 * Android keystore service usable only by the application that called it. This
 * can be used in conjunction with
 * {@link java.security.KeyStore#getInstance(String)} using the
 * {@code "AndroidKeyStore"} type.
 * <p>
 * This class can not be directly instantiated and must instead be used via the
 * {@link KeyPairGenerator#getInstance(String)
 * KeyPairGenerator.getInstance("AndroidKeyPairGenerator")} API.
 *
 * {@hide}
 */
@SuppressWarnings("deprecation")
public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
    public static final String NAME = "AndroidKeyPairGenerator";

    private android.security.KeyStore mKeyStore;

    private AndroidKeyPairGeneratorSpec mSpec;

    /**
     * Generate a KeyPair which is backed by the Android keystore service. You
     * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
     * with an {@link AndroidKeyPairGeneratorSpec} as the {@code params}
     * argument before calling this otherwise an {@code IllegalStateException}
     * will be thrown.
     * <p>
     * This will create an entry in the Android keystore service with a
     * self-signed certificate using the {@code params} specified in the
     * {@code initialize(params)} call.
     *
     * @throws IllegalStateException when called before calling
     *             {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
     * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
     */
    @Override
    public KeyPair generateKeyPair() {
        if (mKeyStore == null || mSpec == null) {
            throw new IllegalStateException(
                    "Must call initialize with an AndroidKeyPairGeneratorSpec first");
        }

        final String alias = mSpec.getKeystoreAlias();

        Credentials.deleteAllTypesForAlias(mKeyStore, alias);

        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
        mKeyStore.generate(privateKeyAlias);

        final PrivateKey privKey;
        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
        try {
            privKey = engine.getPrivateKeyById(privateKeyAlias);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("Can't get key", e);
        }

        final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);

        final PublicKey pubKey;
        try {
            final KeyFactory keyFact = KeyFactory.getInstance("RSA");
            pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("Can't instantiate RSA key generator", e);
        } catch (InvalidKeySpecException e) {
            throw new IllegalStateException("keystore returned invalid key encoding", e);
        }

        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
        certGen.setPublicKey(pubKey);
        certGen.setSerialNumber(mSpec.getSerialNumber());
        certGen.setSubjectDN(mSpec.getSubjectDN());
        certGen.setIssuerDN(mSpec.getSubjectDN());
        certGen.setNotBefore(mSpec.getStartDate());
        certGen.setNotAfter(mSpec.getEndDate());
        certGen.setSignatureAlgorithm("sha1WithRSA");

        final X509Certificate cert;
        try {
            cert = certGen.generate(privKey);
        } catch (Exception e) {
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new IllegalStateException("Can't generate certificate", e);
        }

        byte[] certBytes;
        try {
            certBytes = cert.getEncoded();
        } catch (CertificateEncodingException e) {
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new IllegalStateException("Can't get encoding of certificate", e);
        }

        if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) {
            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
        }

        return new KeyPair(pubKey, privKey);
    }

    @Override
    public void initialize(int keysize, SecureRandom random) {
        throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
    }

    @Override
    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
            throws InvalidAlgorithmParameterException {
        if (params == null) {
            throw new InvalidAlgorithmParameterException(
                    "must supply params of type AndroidKeyPairGenericSpec");
        } else if (!(params instanceof AndroidKeyPairGeneratorSpec)) {
            throw new InvalidAlgorithmParameterException(
                    "params must be of type AndroidKeyPairGeneratorSpec");
        }

        AndroidKeyPairGeneratorSpec spec = (AndroidKeyPairGeneratorSpec) params;

        mSpec = spec;
        mKeyStore = android.security.KeyStore.getInstance();
    }
}
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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 android.security;

import android.content.Context;
import android.text.TextUtils;

import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;

import javax.security.auth.x500.X500Principal;

/**
 * This provides the required parameters needed for initializing the KeyPair
 * generator that works with
 * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
 * facility</a>.
 */
public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
    private final String mKeystoreAlias;

    private final Context mContext;

    private final X500Principal mSubjectDN;

    private final BigInteger mSerialNumber;

    private final Date mStartDate;

    private final Date mEndDate;

    /**
     * Parameter specification for the "{@code AndroidKeyPairGenerator}"
     * instance of the {@link java.security.KeyPairGenerator} API. The
     * {@code context} passed in may be used to pop up some UI to ask the user
     * to unlock or initialize the Android keystore facility.
     * <p>
     * After generation, the {@code keyStoreAlias} is used with the
     * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
     * interface to retrieve the {@link PrivateKey} and its associated
     * {@link Certificate} chain.
     * <p>
     * The KeyPair generator will create a self-signed certificate with the
     * properties of {@code subjectDN} as its X.509v3 Subject Distinguished Name
     * and as its X.509v3 Issuer Distinguished Name, using the specified
     * {@code serialNumber}, and the validity date starting at {@code startDate}
     * and ending at {@code endDate}.
     *
     * @param context Android context for the activity
     * @param keyStoreAlias name to use for the generated key in the Android
     *            keystore
     * @param subjectDN X.509 v3 Subject Distinguished Name
     * @param serialNumber X509 v3 certificate serial number
     * @param startDate the start of the self-signed certificate validity period
     * @param endDate the end date of the self-signed certificate validity
     *            period
     * @throws IllegalArgumentException when any argument is {@code null} or
     *             {@code endDate} is before {@code startDate}.
     */
    public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
            X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
        if (context == null) {
            throw new IllegalArgumentException("context == null");
        } else if (TextUtils.isEmpty(keyStoreAlias)) {
            throw new IllegalArgumentException("keyStoreAlias must not be empty");
        } else if (subjectDN == null) {
            throw new IllegalArgumentException("subjectDN == null");
        } else if (serialNumber == null) {
            throw new IllegalArgumentException("serialNumber == null");
        } else if (startDate == null) {
            throw new IllegalArgumentException("startDate == null");
        } else if (endDate == null) {
            throw new IllegalArgumentException("endDate == null");
        } else if (endDate.before(startDate)) {
            throw new IllegalArgumentException("endDate < startDate");
        }

        mContext = context;
        mKeystoreAlias = keyStoreAlias;
        mSubjectDN = subjectDN;
        mSerialNumber = serialNumber;
        mStartDate = startDate;
        mEndDate = endDate;
    }

    /**
     * @hide
     */
    String getKeystoreAlias() {
        return mKeystoreAlias;
    }

    /**
     * @hide
     */
    Context getContext() {
        return mContext;
    }

    /**
     * @hide
     */
    X500Principal getSubjectDN() {
        return mSubjectDN;
    }

    /**
     * @hide
     */
    BigInteger getSerialNumber() {
        return mSerialNumber;
    }

    /**
     * @hide
     */
    Date getStartDate() {
        return mStartDate;
    }

    /**
     * @hide
     */
    Date getEndDate() {
        return mEndDate;
    }
}
+4 −20
Original line number Diff line number Diff line
@@ -46,9 +46,8 @@ import java.util.Iterator;
import java.util.Set;

/**
 * A java.security.KeyStore interface for the Android KeyStore. This class is
 * hidden from the Android API, but an instance of it can be created via the
 * {@link java.security.KeyStore#getInstance(String)
 * A java.security.KeyStore interface for the Android KeyStore. An instance of
 * it can be created via the {@link java.security.KeyStore#getInstance(String)
 * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
 * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
 * <p>
@@ -277,7 +276,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
         * Make sure we clear out all the types we know about before trying to
         * write.
         */
        deleteAllTypesForAlias(alias);
        Credentials.deleteAllTypesForAlias(mKeyStore, alias);

        if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
            throw new KeyStoreException("Couldn't put private key in keystore");
@@ -315,26 +314,11 @@ public class AndroidKeyStore extends KeyStoreSpi {

    @Override
    public void engineDeleteEntry(String alias) throws KeyStoreException {
        if (!deleteAllTypesForAlias(alias)) {
        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
            throw new KeyStoreException("No such entry " + alias);
        }
    }

    /**
     * Delete all types (private key, certificate, CA certificate) for a
     * particular {@code alias}. All three can exist for any given alias.
     * Returns {@code true} if there was at least one of those types.
     */
    private boolean deleteAllTypesForAlias(String alias) {
        /*
         * Make sure every type is deleted. There can be all three types, so
         * don't use a conditional here.
         */
        return mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + alias)
                | mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)
                | mKeyStore.delete(Credentials.CA_CERTIFICATE + alias);
    }

    private Set<String> getUniqueAliases() {
        final String[] rawAliases = mKeyStore.saw("");
        if (rawAliases == null) {
+5 −0
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@ public class AndroidKeyStoreProvider extends Provider {
    public AndroidKeyStoreProvider() {
        super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");

        // java.security.KeyStore
        put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());

        // java.security.KeyPairGenerator
        put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME,
                AndroidKeyPairGenerator.class.getName());
    }
}
Loading