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