1 /*
2  * Copyright (C) 2023 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 
17 package com.android.server.companion.securechannel;
18 
19 import static android.security.keystore.KeyProperties.DIGEST_SHA256;
20 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC;
21 import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
22 import static android.security.keystore.KeyProperties.PURPOSE_VERIFY;
23 
24 import android.security.keystore.KeyGenParameterSpec;
25 import android.security.keystore2.AndroidKeyStoreSpi;
26 
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.security.GeneralSecurityException;
30 import java.security.KeyPairGenerator;
31 import java.security.KeyStore;
32 import java.security.KeyStoreException;
33 import java.security.cert.Certificate;
34 
35 /**
36  * Utility class to help generate, store, and access key-pair for the secure channel. Uses
37  * Android Keystore.
38  */
39 final class KeyStoreUtils {
40     private static final String TAG = "CDM_SecureChannelKeyStore";
41     private static final String ANDROID_KEYSTORE = AndroidKeyStoreSpi.NAME;
42 
KeyStoreUtils()43     private KeyStoreUtils() {}
44 
45     /**
46      * Load Android keystore to be used by the secure channel.
47      *
48      * @return loaded keystore instance
49      */
loadKeyStore()50     static KeyStore loadKeyStore() throws GeneralSecurityException {
51         KeyStore androidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
52 
53         try {
54             androidKeyStore.load(null);
55         } catch (IOException e) {
56             // Should not happen
57             throw new KeyStoreException("Failed to load Android Keystore.", e);
58         }
59 
60         return androidKeyStore;
61     }
62 
63     /**
64      * Fetch the certificate chain encoded as byte array in the form of concatenated
65      * X509 certificates.
66      *
67      * @param alias unique alias for the key-pair entry
68      * @return a single byte-array containing the entire certificate chain
69      */
getEncodedCertificateChain(String alias)70     static byte[] getEncodedCertificateChain(String alias) throws GeneralSecurityException {
71         KeyStore ks = loadKeyStore();
72 
73         Certificate[] certificateChain = ks.getCertificateChain(alias);
74 
75         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
76         for (Certificate certificate : certificateChain) {
77             buffer.writeBytes(certificate.getEncoded());
78         }
79         return buffer.toByteArray();
80     }
81 
82     /**
83      * Generate a new attestation key-pair.
84      *
85      * @param alias unique alias for the key-pair entry
86      * @param attestationChallenge challenge value to check against for authentication
87      */
generateAttestationKeyPair(String alias, byte[] attestationChallenge)88     static void generateAttestationKeyPair(String alias, byte[] attestationChallenge)
89             throws GeneralSecurityException {
90         KeyGenParameterSpec parameterSpec =
91                 new KeyGenParameterSpec.Builder(alias, PURPOSE_SIGN | PURPOSE_VERIFY)
92                         .setAttestationChallenge(attestationChallenge)
93                         .setDigests(DIGEST_SHA256)
94                         .build();
95 
96         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
97                 /* algorithm */ KEY_ALGORITHM_EC,
98                 /* provider */ ANDROID_KEYSTORE);
99         keyPairGenerator.initialize(parameterSpec);
100         keyPairGenerator.generateKeyPair();
101     }
102 
103     /**
104      * Check if alias exists.
105      *
106      * @param alias unique alias for the key-pair entry
107      * @return true if given alias already exists in the keystore
108      */
aliasExists(String alias)109     static boolean aliasExists(String alias) {
110         try {
111             KeyStore ks = loadKeyStore();
112             return ks.containsAlias(alias);
113         } catch (GeneralSecurityException e) {
114             return false;
115         }
116 
117     }
118 
cleanUp(String alias)119     static void cleanUp(String alias) {
120         try {
121             KeyStore ks = loadKeyStore();
122 
123             if (ks.containsAlias(alias)) {
124                 ks.deleteEntry(alias);
125             }
126         } catch (Exception ignored) {
127             // Do nothing;
128         }
129     }
130 }
131