AndroidKeyStoreSecretKeyFactorySpi.java revision 512c132f49fc6e8e4fc119f4cf167d33b2393509
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;
21import android.security.keymaster.KeyCharacteristics;
22import android.security.keymaster.KeymasterDefs;
23
24import java.security.InvalidKeyException;
25import java.security.ProviderException;
26import java.security.spec.InvalidKeySpecException;
27import java.security.spec.KeySpec;
28import java.util.ArrayList;
29import java.util.Date;
30import java.util.List;
31
32import javax.crypto.SecretKey;
33import javax.crypto.SecretKeyFactorySpi;
34import javax.crypto.spec.SecretKeySpec;
35
36/**
37 * {@link SecretKeyFactorySpi} backed by Android Keystore.
38 *
39 * @hide
40 */
41public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
42
43    private final KeyStore mKeyStore = KeyStore.getInstance();
44
45    @Override
46    protected KeySpec engineGetKeySpec(SecretKey key,
47            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
48        if (keySpecClass == null) {
49            throw new InvalidKeySpecException("keySpecClass == null");
50        }
51        if (!(key instanceof AndroidKeyStoreSecretKey)) {
52            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
53                    ((key != null) ? key.getClass().getName() : "null"));
54        }
55        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
56            throw new InvalidKeySpecException(
57                    "Key material export of Android KeyStore keys is not supported");
58        }
59        if (!KeyInfo.class.equals(keySpecClass)) {
60            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
61        }
62        AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
63        String keyAliasInKeystore = keystoreKey.getAlias();
64        String entryAlias;
65        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
66            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
67        } else {
68            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
69        }
70
71        return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
72    }
73
74    static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
75            int keyUid) {
76        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
77        int errorCode = keyStore.getKeyCharacteristics(
78                keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
79        if (errorCode != KeyStore.NO_ERROR) {
80            throw new ProviderException("Failed to obtain information about key."
81                    + " Keystore error: " + errorCode);
82        }
83
84        boolean insideSecureHardware;
85        @KeyProperties.OriginEnum int origin;
86        int keySize;
87        @KeyProperties.PurposeEnum int purposes;
88        String[] encryptionPaddings;
89        String[] signaturePaddings;
90        @KeyProperties.DigestEnum String[] digests;
91        @KeyProperties.BlockModeEnum String[] blockModes;
92        int keymasterSwEnforcedUserAuthenticators;
93        int keymasterHwEnforcedUserAuthenticators;
94        try {
95            if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
96                insideSecureHardware = true;
97                origin = KeyProperties.Origin.fromKeymaster(
98                        keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
99            } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
100                insideSecureHardware = false;
101                origin = KeyProperties.Origin.fromKeymaster(
102                        keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
103            } else {
104                throw new ProviderException("Key origin not available");
105            }
106            long keySizeUnsigned =
107                    keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
108            if (keySizeUnsigned == -1) {
109                throw new ProviderException("Key size not available");
110            } else if (keySizeUnsigned > Integer.MAX_VALUE) {
111                throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
112            }
113            keySize = (int) keySizeUnsigned;
114            purposes = KeyProperties.Purpose.allFromKeymaster(
115                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
116
117            List<String> encryptionPaddingsList = new ArrayList<String>();
118            List<String> signaturePaddingsList = new ArrayList<String>();
119            // Keymaster stores both types of paddings in the same array -- we split it into two.
120            for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
121                try {
122                    @KeyProperties.EncryptionPaddingEnum String jcaPadding =
123                            KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
124                    encryptionPaddingsList.add(jcaPadding);
125                } catch (IllegalArgumentException e) {
126                    try {
127                        @KeyProperties.SignaturePaddingEnum String padding =
128                                KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
129                        signaturePaddingsList.add(padding);
130                    } catch (IllegalArgumentException e2) {
131                        throw new ProviderException(
132                                "Unsupported encryption padding: " + keymasterPadding);
133                    }
134                }
135
136            }
137            encryptionPaddings =
138                    encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
139            signaturePaddings =
140                    signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
141
142            digests = KeyProperties.Digest.allFromKeymaster(
143                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
144            blockModes = KeyProperties.BlockMode.allFromKeymaster(
145                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
146            keymasterSwEnforcedUserAuthenticators =
147                    keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
148            keymasterHwEnforcedUserAuthenticators =
149                    keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
150        } catch (IllegalArgumentException e) {
151            throw new ProviderException("Unsupported key characteristic", e);
152        }
153
154        Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
155        Date keyValidityForOriginationEnd =
156                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
157        Date keyValidityForConsumptionEnd =
158                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
159        boolean userAuthenticationRequired =
160                !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
161        long userAuthenticationValidityDurationSeconds =
162                keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
163        if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
164            throw new ProviderException("User authentication timeout validity too long: "
165                    + userAuthenticationValidityDurationSeconds + " seconds");
166        }
167        boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
168                && (keymasterHwEnforcedUserAuthenticators != 0)
169                && (keymasterSwEnforcedUserAuthenticators == 0);
170        boolean userAuthenticationValidWhileOnBody =
171                keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
172
173        return new KeyInfo(entryAlias,
174                insideSecureHardware,
175                origin,
176                keySize,
177                keyValidityStart,
178                keyValidityForOriginationEnd,
179                keyValidityForConsumptionEnd,
180                purposes,
181                encryptionPaddings,
182                signaturePaddings,
183                digests,
184                blockModes,
185                userAuthenticationRequired,
186                (int) userAuthenticationValidityDurationSeconds,
187                userAuthenticationRequirementEnforcedBySecureHardware,
188                userAuthenticationValidWhileOnBody);
189    }
190
191    @Override
192    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
193        throw new InvalidKeySpecException(
194                "To generate secret key in Android Keystore, use KeyGenerator initialized with "
195                        + KeyGenParameterSpec.class.getName());
196    }
197
198    @Override
199    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
200        if (key == null) {
201            throw new InvalidKeyException("key == null");
202        } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
203            throw new InvalidKeyException(
204                    "To import a secret key into Android Keystore, use KeyStore.setEntry");
205        }
206
207        return key;
208    }
209}
210