aboutsummaryrefslogtreecommitdiff
path: root/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java
blob: 14020e0f7eccbb94704343a85c60fb9710a2b5a9 (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
 * 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.keystore.cts.util;

import android.content.Context;
import android.security.keystore.KeyProtection;
import android.keystore.cts.util.TestUtils;
import androidx.test.core.app.ApplicationProvider;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;

/** Keystore utilities */
public class KeyStoreUtil {
    // Known KeyMaster/KeyMint versions. This is the version number
    // which appear in the keymasterVersion field.
    public static final int KM_VERSION_KEYMASTER_1 = 10;
    public static final int KM_VERSION_KEYMASTER_1_1 = 11;
    public static final int KM_VERSION_KEYMASTER_2 = 20;
    public static final int KM_VERSION_KEYMASTER_3 = 30;
    public static final int KM_VERSION_KEYMASTER_4 = 40;
    public static final int KM_VERSION_KEYMASTER_4_1 = 41;
    public static final int KM_VERSION_KEYMINT_1 = 100;

    private static final List kmSupportedDigests = List.of("md5","sha-1","sha-224","sha-384",
                                                        "sha-256","sha-512");

    public static KeyStore saveKeysToKeystore(String alias, PublicKey pubKey, PrivateKey privKey,
            KeyProtection keyProtection) throws Exception {
        KeyPair keyPair = new KeyPair(pubKey, privKey);
        X509Certificate certificate = createCertificate(keyPair,
                                                        new X500Principal("CN=Test1"),
                                                        new X500Principal("CN=Test1"));
        Certificate[] certChain = new Certificate[]{certificate};
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        keyStore.setEntry(alias,
                        new KeyStore.PrivateKeyEntry(privKey, certChain),
                        keyProtection);
        return keyStore;
    }

    public static KeyStore saveSecretKeyToKeystore(String alias, SecretKeySpec keySpec,
            KeyProtection keyProtection) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        keyStore.setEntry(alias,
                        new KeyStore.SecretKeyEntry(keySpec),
                        keyProtection);
         return keyStore;
    }

    public static void cleanUpKeyStore() throws Exception {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        for (Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements();) {
            String alias = aliases.nextElement();
            keyStore.deleteEntry(alias);
        }
    }

    public static int getFeatureVersionKeystore(boolean isStrongBox) {
        if (isStrongBox) {
            return TestUtils.getFeatureVersionKeystoreStrongBox(
            ApplicationProvider.getApplicationContext());
        }
        return TestUtils.getFeatureVersionKeystore(ApplicationProvider.getApplicationContext());
    }

    public static boolean hasStrongBox() {
        Context context = ApplicationProvider.getApplicationContext();
        return TestUtils.hasStrongBox(context);
    }

    public static void assumeStrongBox() {
        TestUtils.assumeStrongBox();
    }

    public static boolean isSupportedDigest(String digest, boolean isStrongBox) {
        if (isStrongBox) {
            return digest.equalsIgnoreCase("sha-256");
        }
        return kmSupportedDigests.contains(digest.toLowerCase());
    }

    public static boolean isSupportedMgfDigest(String digest, boolean isStrongBox) {
        if (isStrongBox) {
            return digest.equalsIgnoreCase("sha-1")
                    || digest.equalsIgnoreCase("sha-256");
        }
        return kmSupportedDigests.contains(digest.toLowerCase());
    }

    public static boolean isSupportedRsaKeySize(int keySize, boolean isStrongBox) {
        if (isStrongBox) {
            return keySize == 2048;
        }
        return keySize == 2048 || keySize == 3072 || keySize == 4096;
    }

    public static X509Certificate createCertificate(
            KeyPair keyPair, X500Principal subject, X500Principal issuer)
            throws OperatorCreationException, CertificateException, IOException {
        // Make the certificate valid for two days.
        long millisPerDay = 24 * 60 * 60 * 1000;
        long now = System.currentTimeMillis();
        Date start = new Date(now - millisPerDay);
        Date end = new Date(now + millisPerDay);

        // Assign a random serial number.
        byte[] serialBytes = new byte[16];
        new SecureRandom().nextBytes(serialBytes);
        BigInteger serialNumber = new BigInteger(1, serialBytes);

        // Create the certificate builder
        X509v3CertificateBuilder x509cg =
                new X509v3CertificateBuilder(
                        X500Name.getInstance(issuer.getEncoded()),
                        serialNumber,
                        start,
                        end,
                        X500Name.getInstance(subject.getEncoded()),
                        SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));

        // Choose a signature algorithm matching the key format.
        String keyAlgorithm = keyPair.getPrivate().getAlgorithm();
        String signatureAlgorithm;
        if (keyAlgorithm.equals("RSA")) {
            signatureAlgorithm = "SHA256withRSA";
        } else if (keyAlgorithm.equals("EC")) {
            signatureAlgorithm = "SHA256withECDSA";
        } else {
            throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
        }

        // Sign the certificate and generate it.
        X509CertificateHolder x509holder =
                x509cg.build(
                        new JcaContentSignerBuilder(signatureAlgorithm)
                                .build(keyPair.getPrivate()));
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        X509Certificate x509c =
                (X509Certificate)
                        certFactory.generateCertificate(
                                new ByteArrayInputStream(x509holder.getEncoded()));
        return x509c;
    }
}