1/*
2 * Copyright (C) 2015 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
17package android.security.keystore;
18
19import android.security.Credentials;
20import android.security.KeyStore;
21
22import java.security.InvalidKeyException;
23import java.security.Key;
24import java.security.KeyFactorySpi;
25import java.security.PrivateKey;
26import java.security.PublicKey;
27import java.security.spec.ECPublicKeySpec;
28import java.security.spec.InvalidKeySpecException;
29import java.security.spec.KeySpec;
30import java.security.spec.PKCS8EncodedKeySpec;
31import java.security.spec.RSAPublicKeySpec;
32import java.security.spec.X509EncodedKeySpec;
33
34/**
35 * {@link KeyFactorySpi} backed by Android KeyStore.
36 *
37 * @hide
38 */
39public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
40
41    private final KeyStore mKeyStore = KeyStore.getInstance();
42
43    @Override
44    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
45            throws InvalidKeySpecException {
46        if (key == null) {
47            throw new InvalidKeySpecException("key == null");
48        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
49            && (!(key instanceof AndroidKeyStorePublicKey))) {
50            throw new InvalidKeySpecException(
51                    "Unsupported key type: " + key.getClass().getName()
52                    + ". This KeyFactory supports only Android Keystore asymmetric keys");
53        }
54
55        // key is an Android Keystore private or public key
56
57        if (keySpecClass == null) {
58            throw new InvalidKeySpecException("keySpecClass == null");
59        } else if (KeyInfo.class.equals(keySpecClass)) {
60            if (!(key instanceof AndroidKeyStorePrivateKey)) {
61                throw new InvalidKeySpecException(
62                        "Unsupported key type: " + key.getClass().getName()
63                        + ". KeyInfo can be obtained only for Android Keystore private keys");
64            }
65            AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
66            String keyAliasInKeystore = keystorePrivateKey.getAlias();
67            String entryAlias;
68            if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
69                entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
70            } else {
71                throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
72            }
73            @SuppressWarnings("unchecked")
74            T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
75                    mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid());
76            return result;
77        } else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
78            if (!(key instanceof AndroidKeyStorePublicKey)) {
79                throw new InvalidKeySpecException(
80                        "Unsupported key type: " + key.getClass().getName()
81                        + ". X509EncodedKeySpec can be obtained only for Android Keystore public"
82                        + " keys");
83            }
84            @SuppressWarnings("unchecked")
85            T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded());
86            return result;
87        } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) {
88            if (key instanceof AndroidKeyStorePrivateKey) {
89                throw new InvalidKeySpecException(
90                        "Key material export of Android Keystore private keys is not supported");
91            } else {
92                throw new InvalidKeySpecException(
93                        "Cannot export key material of public key in PKCS#8 format."
94                        + " Only X.509 format (X509EncodedKeySpec) supported for public keys.");
95            }
96        } else if (RSAPublicKeySpec.class.equals(keySpecClass)) {
97            if (key instanceof AndroidKeyStoreRSAPublicKey) {
98                AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key;
99                @SuppressWarnings("unchecked")
100                T result =
101                        (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
102                return result;
103            } else {
104                throw new InvalidKeySpecException(
105                        "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " "
106                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
107                        + " key");
108            }
109        } else if (ECPublicKeySpec.class.equals(keySpecClass)) {
110            if (key instanceof AndroidKeyStoreECPublicKey) {
111                AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key;
112                @SuppressWarnings("unchecked")
113                T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
114                return result;
115            } else {
116                throw new InvalidKeySpecException(
117                        "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " "
118                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
119                        + " key");
120            }
121        } else {
122            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
123        }
124    }
125
126    @Override
127    protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
128        throw new InvalidKeySpecException(
129                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
130                + " " + KeyGenParameterSpec.class.getName());
131    }
132
133    @Override
134    protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
135        throw new InvalidKeySpecException(
136                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
137                + " " + KeyGenParameterSpec.class.getName());
138    }
139
140    @Override
141    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
142        if (key == null) {
143            throw new InvalidKeyException("key == null");
144        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
145                && (!(key instanceof AndroidKeyStorePublicKey))) {
146            throw new InvalidKeyException(
147                    "To import a key into Android Keystore, use KeyStore.setEntry");
148        }
149        return key;
150    }
151}
152