1 /** 2 * Licensed under the Apache License, Version 2.0 (the "License"); 3 * you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 package android.keystore.cts.util; 15 16 import static org.junit.Assume.assumeTrue; 17 import android.content.Context; 18 import android.security.keystore.KeyProtection; 19 import android.keystore.cts.util.TestUtils; 20 import androidx.test.core.app.ApplicationProvider; 21 import org.bouncycastle.asn1.x500.X500Name; 22 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 23 import org.bouncycastle.cert.X509CertificateHolder; 24 import org.bouncycastle.cert.X509v3CertificateBuilder; 25 import org.bouncycastle.operator.OperatorCreationException; 26 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 27 import java.io.ByteArrayInputStream; 28 import java.io.IOException; 29 import java.math.BigInteger; 30 import java.security.GeneralSecurityException; 31 import java.security.KeyPair; 32 import java.security.KeyStore; 33 import java.security.PrivateKey; 34 import java.security.PublicKey; 35 import java.security.SecureRandom; 36 import java.security.cert.Certificate; 37 import java.security.cert.CertificateException; 38 import java.security.cert.CertificateFactory; 39 import java.security.cert.X509Certificate; 40 import java.util.Date; 41 import java.util.Enumeration; 42 import java.util.List; 43 import javax.crypto.spec.SecretKeySpec; 44 import javax.security.auth.x500.X500Principal; 45 46 /** Keystore utilities */ 47 public class KeyStoreUtil { 48 // Known KeyMaster/KeyMint versions. This is the version number 49 // which appear in the keymasterVersion field. 50 public static final int KM_VERSION_KEYMASTER_1 = 10; 51 public static final int KM_VERSION_KEYMASTER_1_1 = 11; 52 public static final int KM_VERSION_KEYMASTER_2 = 20; 53 public static final int KM_VERSION_KEYMASTER_3 = 30; 54 public static final int KM_VERSION_KEYMASTER_4 = 40; 55 public static final int KM_VERSION_KEYMASTER_4_1 = 41; 56 public static final int KM_VERSION_KEYMINT_1 = 100; 57 public static final int KM_VERSION_KEYMINT_2 = 200; 58 public static final int KM_VERSION_KEYMINT_3 = 300; 59 60 private static final List kmSupportedDigests = List.of("md5","sha-1","sha-224","sha-384", 61 "sha-256","sha-512"); 62 saveKeysToKeystore(String alias, PublicKey pubKey, PrivateKey privKey, KeyProtection keyProtection)63 public static KeyStore saveKeysToKeystore(String alias, PublicKey pubKey, PrivateKey privKey, 64 KeyProtection keyProtection) throws Exception { 65 KeyPair keyPair = new KeyPair(pubKey, privKey); 66 X509Certificate certificate = createCertificate(keyPair, 67 new X500Principal("CN=Test1"), 68 new X500Principal("CN=Test1")); 69 Certificate[] certChain = new Certificate[]{certificate}; 70 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 71 keyStore.load(null); 72 keyStore.setEntry(alias, 73 new KeyStore.PrivateKeyEntry(privKey, certChain), 74 keyProtection); 75 return keyStore; 76 } 77 saveSecretKeyToKeystore(String alias, SecretKeySpec keySpec, KeyProtection keyProtection)78 public static KeyStore saveSecretKeyToKeystore(String alias, SecretKeySpec keySpec, 79 KeyProtection keyProtection) throws Exception { 80 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 81 keyStore.load(null); 82 keyStore.setEntry(alias, 83 new KeyStore.SecretKeyEntry(keySpec), 84 keyProtection); 85 return keyStore; 86 } 87 cleanUpKeyStore()88 public static void cleanUpKeyStore() throws Exception { 89 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 90 keyStore.load(null); 91 for (Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements();) { 92 String alias = aliases.nextElement(); 93 keyStore.deleteEntry(alias); 94 } 95 } 96 getFeatureVersionKeystore(boolean isStrongBox)97 public static int getFeatureVersionKeystore(boolean isStrongBox) { 98 if (isStrongBox) { 99 return TestUtils.getFeatureVersionKeystoreStrongBox( 100 ApplicationProvider.getApplicationContext()); 101 } 102 return TestUtils.getFeatureVersionKeystore(ApplicationProvider.getApplicationContext()); 103 } 104 hasStrongBox()105 public static boolean hasStrongBox() { 106 Context context = ApplicationProvider.getApplicationContext(); 107 return TestUtils.hasStrongBox(context); 108 } 109 assumeStrongBox()110 public static void assumeStrongBox() { 111 TestUtils.assumeStrongBox(); 112 } 113 isSupportedDigest(String digest, boolean isStrongBox)114 public static boolean isSupportedDigest(String digest, boolean isStrongBox) { 115 if (isStrongBox) { 116 return digest.equalsIgnoreCase("sha-256"); 117 } 118 return kmSupportedDigests.contains(digest.toLowerCase()); 119 } 120 isSupportedMgfDigest(String digest, boolean isStrongBox)121 public static boolean isSupportedMgfDigest(String digest, boolean isStrongBox) { 122 if (isStrongBox) { 123 return digest.equalsIgnoreCase("sha-1") 124 || digest.equalsIgnoreCase("sha-256"); 125 } 126 return kmSupportedDigests.contains(digest.toLowerCase()); 127 } 128 isSupportedRsaKeySize(int keySize, boolean isStrongBox)129 public static boolean isSupportedRsaKeySize(int keySize, boolean isStrongBox) { 130 if (isStrongBox) { 131 return keySize == 2048; 132 } 133 return keySize == 2048 || keySize == 3072 || keySize == 4096; 134 } 135 createCertificate( KeyPair keyPair, X500Principal subject, X500Principal issuer)136 public static X509Certificate createCertificate( 137 KeyPair keyPair, X500Principal subject, X500Principal issuer) 138 throws OperatorCreationException, CertificateException, IOException { 139 // Make the certificate valid for two days. 140 long millisPerDay = 24 * 60 * 60 * 1000; 141 long now = System.currentTimeMillis(); 142 Date start = new Date(now - millisPerDay); 143 Date end = new Date(now + millisPerDay); 144 145 // Assign a random serial number. 146 byte[] serialBytes = new byte[16]; 147 new SecureRandom().nextBytes(serialBytes); 148 BigInteger serialNumber = new BigInteger(1, serialBytes); 149 150 // Create the certificate builder 151 X509v3CertificateBuilder x509cg = 152 new X509v3CertificateBuilder( 153 X500Name.getInstance(issuer.getEncoded()), 154 serialNumber, 155 start, 156 end, 157 X500Name.getInstance(subject.getEncoded()), 158 SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); 159 160 // Choose a signature algorithm matching the key format. 161 String keyAlgorithm = keyPair.getPrivate().getAlgorithm(); 162 String signatureAlgorithm; 163 if (keyAlgorithm.equals("RSA")) { 164 signatureAlgorithm = "SHA256withRSA"; 165 } else if (keyAlgorithm.equals("EC")) { 166 signatureAlgorithm = "SHA256withECDSA"; 167 } else { 168 throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm); 169 } 170 171 // Sign the certificate and generate it. 172 X509CertificateHolder x509holder = 173 x509cg.build( 174 new JcaContentSignerBuilder(signatureAlgorithm) 175 .build(keyPair.getPrivate())); 176 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 177 X509Certificate x509c = 178 (X509Certificate) 179 certFactory.generateCertificate( 180 new ByteArrayInputStream(x509holder.getEncoded())); 181 return x509c; 182 } 183 assumeKeyMintV1OrNewer(boolean isStrongBox)184 public static void assumeKeyMintV1OrNewer(boolean isStrongBox) { 185 assumeTrue("Test can only run on KeyMint v1 and above", 186 KeyStoreUtil.getFeatureVersionKeystore(isStrongBox) >= KM_VERSION_KEYMINT_1); 187 } 188 } 189