summaryrefslogtreecommitdiffstats
path: root/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
blob: 5ce4fd2cde2dcf274026b47eae7ddbaf27416cdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * Copyright (C) 2015 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.keystore;

import android.security.Credentials;
import android.security.KeyStore;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactorySpi;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * {@link KeyFactorySpi} backed by Android KeyStore.
 *
 * @hide
 */
public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {

    private final KeyStore mKeyStore = KeyStore.getInstance();

    @Override
    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
            throws InvalidKeySpecException {
        if (key == null) {
            throw new InvalidKeySpecException("key == null");
        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
            && (!(key instanceof AndroidKeyStorePublicKey))) {
            throw new InvalidKeySpecException(
                    "Unsupported key type: " + key.getClass().getName()
                    + ". This KeyFactory supports only Android Keystore asymmetric keys");
        }

        // key is an Android Keystore private or public key

        if (keySpecClass == null) {
            throw new InvalidKeySpecException("keySpecClass == null");
        } else if (KeyInfo.class.equals(keySpecClass)) {
            if (!(key instanceof AndroidKeyStorePrivateKey)) {
                throw new InvalidKeySpecException(
                        "Unsupported key type: " + key.getClass().getName()
                        + ". KeyInfo can be obtained only for Android Keystore private keys");
            }
            String keyAliasInKeystore = ((AndroidKeyStorePrivateKey) key).getAlias();
            String entryAlias;
            if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
                entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
            } else {
                throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
            }
            @SuppressWarnings("unchecked")
            T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
                    mKeyStore, entryAlias, keyAliasInKeystore);
            return result;
        } else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
            if (!(key instanceof AndroidKeyStorePublicKey)) {
                throw new InvalidKeySpecException(
                        "Unsupported key type: " + key.getClass().getName()
                        + ". X509EncodedKeySpec can be obtained only for Android Keystore public"
                        + " keys");
            }
            @SuppressWarnings("unchecked")
            T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded());
            return result;
        } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) {
            if (key instanceof AndroidKeyStorePrivateKey) {
                throw new InvalidKeySpecException(
                        "Key material export of Android Keystore private keys is not supported");
            } else {
                throw new InvalidKeySpecException(
                        "Cannot export key material of public key in PKCS#8 format."
                        + " Only X.509 format (X509EncodedKeySpec) supported for public keys.");
            }
        } else if (RSAPublicKeySpec.class.equals(keySpecClass)) {
            if (key instanceof AndroidKeyStoreRSAPublicKey) {
                AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key;
                @SuppressWarnings("unchecked")
                T result =
                        (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
                return result;
            } else {
                throw new InvalidKeySpecException(
                        "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " "
                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
                        + " key");
            }
        } else if (ECPublicKeySpec.class.equals(keySpecClass)) {
            if (key instanceof AndroidKeyStoreECPublicKey) {
                AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key;
                @SuppressWarnings("unchecked")
                T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
                return result;
            } else {
                throw new InvalidKeySpecException(
                        "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " "
                        + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
                        + " key");
            }
        } else {
            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
        }
    }

    @Override
    protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
        throw new InvalidKeySpecException(
                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
                + " " + KeyGenParameterSpec.class.getName());
    }

    @Override
    protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
        throw new InvalidKeySpecException(
                "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
                + " " + KeyGenParameterSpec.class.getName());
    }

    @Override
    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
        if (key == null) {
            throw new InvalidKeyException("key == null");
        } else if ((!(key instanceof AndroidKeyStorePrivateKey))
                && (!(key instanceof AndroidKeyStorePublicKey))) {
            throw new InvalidKeyException(
                    "To import a key into Android Keystore, use KeyStore.setEntry");
        }
        return key;
    }
}