AndroidKeyStoreSecretKeyFactorySpi.java revision ae6cb7aad56bb006769cd8a69b92af7236644fc1
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        String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias();
63        String entryAlias;
64        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
65            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
66        } else {
67            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
68        }
69
70        return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore);
71    }
72
73    static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) {
74        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
75        int errorCode =
76                keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
77        if (errorCode != KeyStore.NO_ERROR) {
78            throw new ProviderException("Failed to obtain information about key."
79                    + " Keystore error: " + errorCode);
80        }
81
82        boolean insideSecureHardware;
83        @KeyProperties.OriginEnum int origin;
84        int keySize;
85        @KeyProperties.PurposeEnum int purposes;
86        String[] encryptionPaddings;
87        String[] signaturePaddings;
88        @KeyProperties.DigestEnum String[] digests;
89        @KeyProperties.BlockModeEnum String[] blockModes;
90        int keymasterSwEnforcedUserAuthenticators;
91        int keymasterHwEnforcedUserAuthenticators;
92        try {
93            if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
94                insideSecureHardware = true;
95                origin = KeyProperties.Origin.fromKeymaster(
96                        keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
97            } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
98                insideSecureHardware = false;
99                origin = KeyProperties.Origin.fromKeymaster(
100                        keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
101            } else {
102                throw new ProviderException("Key origin not available");
103            }
104            long keySizeUnsigned =
105                    keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
106            if (keySizeUnsigned == -1) {
107                throw new ProviderException("Key size not available");
108            } else if (keySizeUnsigned > Integer.MAX_VALUE) {
109                throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
110            }
111            keySize = (int) keySizeUnsigned;
112            purposes = KeyProperties.Purpose.allFromKeymaster(
113                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
114
115            List<String> encryptionPaddingsList = new ArrayList<String>();
116            List<String> signaturePaddingsList = new ArrayList<String>();
117            // Keymaster stores both types of paddings in the same array -- we split it into two.
118            for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
119                try {
120                    @KeyProperties.EncryptionPaddingEnum String jcaPadding =
121                            KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
122                    encryptionPaddingsList.add(jcaPadding);
123                } catch (IllegalArgumentException e) {
124                    try {
125                        @KeyProperties.SignaturePaddingEnum String padding =
126                                KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
127                        signaturePaddingsList.add(padding);
128                    } catch (IllegalArgumentException e2) {
129                        throw new ProviderException(
130                                "Unsupported encryption padding: " + keymasterPadding);
131                    }
132                }
133
134            }
135            encryptionPaddings =
136                    encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
137            signaturePaddings =
138                    signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
139
140            digests = KeyProperties.Digest.allFromKeymaster(
141                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
142            blockModes = KeyProperties.BlockMode.allFromKeymaster(
143                    keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
144            keymasterSwEnforcedUserAuthenticators =
145                    keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
146            keymasterHwEnforcedUserAuthenticators =
147                    keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
148        } catch (IllegalArgumentException e) {
149            throw new ProviderException("Unsupported key characteristic", e);
150        }
151
152        Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
153        Date keyValidityForOriginationEnd =
154                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
155        Date keyValidityForConsumptionEnd =
156                keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
157        boolean userAuthenticationRequired =
158                !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
159        long userAuthenticationValidityDurationSeconds =
160                keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
161        if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
162            throw new ProviderException("User authentication timeout validity too long: "
163                    + userAuthenticationValidityDurationSeconds + " seconds");
164        }
165        boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
166                && (keymasterHwEnforcedUserAuthenticators != 0)
167                && (keymasterSwEnforcedUserAuthenticators == 0);
168
169        return new KeyInfo(entryAlias,
170                insideSecureHardware,
171                origin,
172                keySize,
173                keyValidityStart,
174                keyValidityForOriginationEnd,
175                keyValidityForConsumptionEnd,
176                purposes,
177                encryptionPaddings,
178                signaturePaddings,
179                digests,
180                blockModes,
181                userAuthenticationRequired,
182                (int) userAuthenticationValidityDurationSeconds,
183                userAuthenticationRequirementEnforcedBySecureHardware);
184    }
185
186    @Override
187    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
188        throw new UnsupportedOperationException(
189                "To generate secret key in Android KeyStore, use KeyGenerator initialized with "
190                        + KeyGenParameterSpec.class.getName());
191    }
192
193    @Override
194    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
195        throw new UnsupportedOperationException(
196                "To import a secret key into Android KeyStore, use KeyStore.setEntry with "
197                + KeyProtection.class.getName());
198    }
199}
200