1 /* 2 * Copyright 2017 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 android.keystore.cts; 18 19 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_3DES; 20 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_AES; 21 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_EC; 22 import static android.security.keymaster.KeymasterDefs.KM_ALGORITHM_RSA; 23 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_MD5; 24 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_NONE; 25 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA1; 26 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_224; 27 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_256; 28 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_384; 29 import static android.security.keymaster.KeymasterDefs.KM_DIGEST_SHA_2_512; 30 import static android.security.keymaster.KeymasterDefs.KM_KEY_FORMAT_PKCS8; 31 import static android.security.keymaster.KeymasterDefs.KM_KEY_FORMAT_RAW; 32 import static android.security.keymaster.KeymasterDefs.KM_MODE_CBC; 33 import static android.security.keymaster.KeymasterDefs.KM_MODE_ECB; 34 import static android.security.keymaster.KeymasterDefs.KM_PAD_NONE; 35 import static android.security.keymaster.KeymasterDefs.KM_PAD_PKCS7; 36 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_OAEP; 37 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT; 38 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; 39 import static android.security.keymaster.KeymasterDefs.KM_PAD_RSA_PSS; 40 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_DECRYPT; 41 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_ENCRYPT; 42 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_SIGN; 43 import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_VERIFY; 44 import static android.security.keymaster.KeymasterDefs.KM_TAG_ALGORITHM; 45 import static android.security.keymaster.KeymasterDefs.KM_TAG_BLOCK_MODE; 46 import static android.security.keymaster.KeymasterDefs.KM_TAG_DIGEST; 47 import static android.security.keymaster.KeymasterDefs.KM_TAG_KEY_SIZE; 48 import static android.security.keymaster.KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED; 49 import static android.security.keymaster.KeymasterDefs.KM_TAG_PADDING; 50 import static android.security.keymaster.KeymasterDefs.KM_TAG_PURPOSE; 51 52 import static com.google.common.truth.Truth.assertThat; 53 import static com.google.common.truth.Truth.assertWithMessage; 54 55 import static org.junit.Assert.assertEquals; 56 import static org.junit.Assert.assertFalse; 57 import static org.junit.Assert.assertTrue; 58 import static org.junit.Assume.assumeNoException; 59 import static org.junit.Assume.assumeTrue; 60 61 import android.content.Context; 62 import android.keystore.cts.util.TestUtils; 63 import android.security.keystore.KeyGenParameterSpec; 64 import android.security.keystore.KeyProperties; 65 import android.security.keystore.SecureKeyImportUnavailableException; 66 import android.security.keystore.WrappedKeyEntry; 67 68 import androidx.test.InstrumentationRegistry; 69 import androidx.test.runner.AndroidJUnit4; 70 71 import org.bouncycastle.asn1.ASN1Encoding; 72 import org.bouncycastle.asn1.ASN1EncodableVector; 73 import org.bouncycastle.asn1.ASN1Integer; 74 import org.bouncycastle.asn1.DERNull; 75 import org.bouncycastle.asn1.DEROctetString; 76 import org.bouncycastle.asn1.DERSequence; 77 import org.bouncycastle.asn1.DERSet; 78 import org.bouncycastle.asn1.DERTaggedObject; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 82 import java.security.Key; 83 import java.security.KeyPair; 84 import java.security.KeyPairGenerator; 85 import java.security.KeyStore; 86 import java.security.KeyStore.Entry; 87 import java.security.KeyStoreException; 88 import java.security.PrivateKey; 89 import java.security.PublicKey; 90 import java.security.SecureRandom; 91 import java.security.Signature; 92 import java.security.spec.AlgorithmParameterSpec; 93 import java.security.spec.MGF1ParameterSpec; 94 import java.util.Arrays; 95 96 import javax.crypto.Cipher; 97 import javax.crypto.KeyGenerator; 98 import javax.crypto.spec.GCMParameterSpec; 99 import javax.crypto.spec.IvParameterSpec; 100 import javax.crypto.spec.OAEPParameterSpec; 101 import javax.crypto.spec.PSource; 102 import javax.crypto.spec.SecretKeySpec; 103 104 @RunWith(AndroidJUnit4.class) 105 public class ImportWrappedKeyTest { 106 private static final String TAG = "ImportWrappedKeyTest"; 107 108 private static final String ALIAS = "my key"; 109 private static final String WRAPPING_KEY_ALIAS = "my_favorite_wrapping_key"; 110 111 private static final int WRAPPED_FORMAT_VERSION = 0; 112 private static final int GCM_TAG_SIZE = 128; 113 114 SecureRandom random = new SecureRandom(); 115 getContext()116 private Context getContext() { 117 return InstrumentationRegistry.getInstrumentation().getTargetContext(); 118 } 119 removeTagType(int tag)120 private int removeTagType(int tag) { 121 int kmTagTypeMask = 0x0FFFFFFF; 122 return tag & kmTagTypeMask; 123 } 124 125 @Test testKeyStore_ImportWrappedKey_AES()126 public void testKeyStore_ImportWrappedKey_AES() throws Exception { 127 testKeyStore_ImportWrappedKey_AES(false); 128 } 129 130 @Test testKeyStore_ImportWrappedKey_AES_StrongBox()131 public void testKeyStore_ImportWrappedKey_AES_StrongBox() throws Exception { 132 testKeyStore_ImportWrappedKey_AES(true); 133 } 134 testKeyStore_ImportWrappedKey_AES(boolean isStrongBox)135 public void testKeyStore_ImportWrappedKey_AES(boolean isStrongBox) throws Exception { 136 if (isStrongBox) { 137 TestUtils.assumeStrongBox(); 138 } 139 140 KeyGenerator kg = KeyGenerator.getInstance("AES"); 141 kg.init(256); 142 Key swKey = kg.generateKey(); 143 144 byte[] keyMaterial = swKey.getEncoded(); 145 byte[] mask = new byte[32]; // Zero mask 146 147 try { 148 importWrappedKey(wrapKey( 149 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 150 keyMaterial, 151 mask, 152 KM_KEY_FORMAT_RAW, 153 makeAesAuthList(keyMaterial.length * 8))); 154 } catch (SecureKeyImportUnavailableException e) { 155 assumeNoException("Can only test if secure key import is available", e); 156 } 157 158 // Use Key 159 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 160 keyStore.load(null, null); 161 162 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 163 164 Key importedKey = keyStore.getKey(ALIAS, null); 165 String plaintext = "hello, world"; 166 167 Cipher c = Cipher.getInstance("AES/ECB/PKCS7Padding"); 168 c.init(Cipher.ENCRYPT_MODE, importedKey); 169 byte[] encrypted = c.doFinal(plaintext.getBytes()); 170 171 // Decrypt using key imported into keystore. 172 c = Cipher.getInstance("AES/ECB/PKCS7Padding"); 173 c.init(Cipher.DECRYPT_MODE, importedKey); 174 assertEquals(new String(c.doFinal(encrypted)), plaintext); 175 176 // Decrypt using local software copy of the key. 177 c = Cipher.getInstance("AES/ECB/PKCS7Padding"); 178 c.init(Cipher.DECRYPT_MODE, swKey); 179 assertEquals(new String(c.doFinal(encrypted)), plaintext); 180 } 181 182 @Test testKeyStore_ImportIncorrectWrappedKey()183 public void testKeyStore_ImportIncorrectWrappedKey() throws Exception { 184 testKeyStore_ImportIncorrectWrappedKey(false); 185 } 186 187 @Test testKeyStore_ImportIncorrectWrappedKey_StrongBox()188 public void testKeyStore_ImportIncorrectWrappedKey_StrongBox() throws Exception { 189 testKeyStore_ImportIncorrectWrappedKey(true); 190 } 191 testKeyStore_ImportIncorrectWrappedKey(boolean isStrongBox)192 private void testKeyStore_ImportIncorrectWrappedKey(boolean isStrongBox) throws Exception { 193 if (isStrongBox) { 194 TestUtils.assumeStrongBox(); 195 } 196 random.setSeed(0); 197 198 byte[] keyMaterial = new byte[32]; 199 random.nextBytes(keyMaterial); 200 byte[] mask = new byte[32]; // Zero mask 201 202 KeyStoreException exception = null; 203 try { 204 importWrappedKey( 205 wrapKey( 206 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 207 keyMaterial, 208 mask, 209 KM_KEY_FORMAT_RAW, 210 makeAesAuthList(keyMaterial.length * 8), 211 /* correctWrappingRequired= */ false)); 212 } catch (SecureKeyImportUnavailableException e) { 213 assumeNoException("Can only test if secure key import is available", e); 214 } catch (KeyStoreException e) { 215 exception = e; 216 } 217 assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull(); 218 assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class); 219 android.security.KeyStoreException ksException = 220 (android.security.KeyStoreException) exception.getCause(); 221 assertFalse("Importing incorrectly wrapped key should not cause transient failure in" 222 + " Key{Mint/Master}. That means performing same operation will fail always.", 223 ksException.isTransientFailure()); 224 } 225 226 @Test testKeyStore_ImportWrappedKeyWrappingKeyMissing()227 public void testKeyStore_ImportWrappedKeyWrappingKeyMissing() throws Exception { 228 final String EXPECTED_FAILURE = "Failed to import wrapped key. Keystore error code: 7"; 229 KeyStoreException exception = null; 230 231 try { 232 byte [] fakeWrappedKey = new byte[1]; 233 importWrappedKey(fakeWrappedKey, WRAPPING_KEY_ALIAS + "_Missing"); 234 } catch (KeyStoreException e) { 235 exception = e; 236 } 237 238 assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull(); 239 240 assertThat(exception.getMessage()).isEqualTo(EXPECTED_FAILURE); 241 assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class); 242 android.security.KeyStoreException ksException = 243 (android.security.KeyStoreException) exception.getCause(); 244 assertThat(ksException.getNumericErrorCode()).isEqualTo( 245 android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST); 246 } 247 248 @Test testKeyStore_ImportWrappedKey_3DES()249 public void testKeyStore_ImportWrappedKey_3DES() throws Exception { 250 testKeyStore_ImportWrappedKey_3DES(false); 251 } 252 253 @Test testKeyStore_ImportWrappedKey_3DES_StrongBox()254 public void testKeyStore_ImportWrappedKey_3DES_StrongBox() throws Exception { 255 testKeyStore_ImportWrappedKey_3DES(true); 256 } 257 testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox)258 public void testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox) throws Exception { 259 if (isStrongBox) { 260 TestUtils.assumeStrongBox(); 261 } 262 263 assumeTrue("Can only test if device supports 3DES", TestUtils.supports3DES()); 264 265 KeyGenerator kg = KeyGenerator.getInstance("DESEDE"); 266 kg.init(168); 267 Key swKey = kg.generateKey(); 268 269 byte[] keyMaterial = swKey.getEncoded(); 270 byte[] mask = new byte[24]; // Zero mask 271 272 try { 273 importWrappedKey(wrapKey( 274 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 275 keyMaterial, 276 mask, 277 KM_KEY_FORMAT_RAW, 278 make3desAuthList(168))); 279 } catch (SecureKeyImportUnavailableException e) { 280 assumeNoException("Can only test if secure key import is available", e); 281 } 282 283 // Use Key 284 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 285 keyStore.load(null, null); 286 287 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 288 289 Key importedKey = keyStore.getKey(ALIAS, null); 290 String plaintext = "hello, world"; 291 292 Cipher c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 293 c.init(Cipher.ENCRYPT_MODE, importedKey); 294 IvParameterSpec paramSpec = new IvParameterSpec(c.getIV()); 295 byte[] encrypted = c.doFinal(plaintext.getBytes()); 296 297 // Decrypt using key imported into keystore. 298 c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 299 c.init(Cipher.DECRYPT_MODE, importedKey, paramSpec); 300 assertEquals(new String(c.doFinal(encrypted)), plaintext); 301 302 // Decrypt using local software copy of the key. 303 c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 304 c.init(Cipher.DECRYPT_MODE, swKey, paramSpec); 305 assertEquals(new String(c.doFinal(encrypted)), plaintext); 306 } 307 308 @Test testKeyStore_ImportWrappedKey_RSA()309 public void testKeyStore_ImportWrappedKey_RSA() throws Exception { 310 testKeyStore_ImportWrappedKey_RSA(false); 311 } 312 313 @Test testKeyStore_ImportWrappedKey_RSA_StrongBox()314 public void testKeyStore_ImportWrappedKey_RSA_StrongBox() throws Exception { 315 testKeyStore_ImportWrappedKey_RSA(true); 316 } 317 testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox)318 public void testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox) throws Exception { 319 assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.", 320 TestUtils.getVendorApiLevel() >= 35); 321 322 if (isStrongBox) { 323 TestUtils.assumeStrongBox(); 324 } 325 326 KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 327 // Both TEE and Strongbox must support 2048-bit keys. 328 int keySize = 2048; 329 kpg.initialize(keySize); 330 KeyPair kp = kpg.generateKeyPair(); 331 PublicKey publicKey = kp.getPublic(); 332 PrivateKey privateKey = kp.getPrivate(); 333 334 assertEquals(privateKey.getFormat(), "PKCS#8"); 335 336 byte[] keyMaterial = privateKey.getEncoded(); 337 byte[] mask = new byte[32]; // Zero mask 338 339 try { 340 importWrappedKey(wrapKey( 341 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 342 keyMaterial, 343 mask, 344 KM_KEY_FORMAT_PKCS8, 345 makeRsaAuthList(keySize))); 346 } catch (SecureKeyImportUnavailableException e) { 347 assumeNoException("Can only test if secure key import is available", e); 348 } 349 350 // Use Key 351 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 352 keyStore.load(null, null); 353 354 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 355 356 String plaintext = "hello, world"; 357 358 Key importedKey = keyStore.getKey(ALIAS, null); 359 assertTrue(importedKey instanceof PrivateKey); 360 361 // Encrypt with KS private key, then decrypt with local public key. 362 Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 363 c.init(Cipher.ENCRYPT_MODE, importedKey); 364 byte[] encrypted = c.doFinal(plaintext.getBytes()); 365 366 c.init(Cipher.DECRYPT_MODE, publicKey); 367 assertEquals(new String(c.doFinal(encrypted)), plaintext); 368 369 // Encrypt with local public key, then decrypt with KS private key. 370 c.init(Cipher.ENCRYPT_MODE, publicKey); 371 encrypted = c.doFinal(plaintext.getBytes()); 372 373 c.init(Cipher.DECRYPT_MODE, importedKey); 374 assertEquals(new String(c.doFinal(encrypted)), plaintext); 375 376 // Sign with KS private key, then verify with local public key. 377 Signature s = Signature.getInstance("SHA256withRSA"); 378 s.initSign((PrivateKey) importedKey); 379 s.update(plaintext.getBytes()); 380 byte[] signature = s.sign(); 381 382 s.initVerify(publicKey); 383 s.update(plaintext.getBytes()); 384 assertTrue(s.verify(signature)); 385 } 386 387 @Test testKeyStore_ImportWrappedKey_EC()388 public void testKeyStore_ImportWrappedKey_EC() throws Exception { 389 testKeyStore_ImportWrappedKey_EC(false); 390 } 391 392 @Test testKeyStore_ImportWrappedKey_EC_StrongBox()393 public void testKeyStore_ImportWrappedKey_EC_StrongBox() throws Exception { 394 testKeyStore_ImportWrappedKey_EC(true); 395 } 396 testKeyStore_ImportWrappedKey_EC(boolean isStrongBox)397 public void testKeyStore_ImportWrappedKey_EC(boolean isStrongBox) throws Exception { 398 assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.", 399 TestUtils.getVendorApiLevel() >= 35); 400 401 if (isStrongBox) { 402 TestUtils.assumeStrongBox(); 403 } 404 405 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); 406 // Both TEE and Strongbox must support P256 curve. 407 int keySize = 256; 408 kpg.initialize(keySize); 409 KeyPair kp = kpg.generateKeyPair(); 410 PublicKey publicKey = kp.getPublic(); 411 PrivateKey privateKey = kp.getPrivate(); 412 413 assertEquals(privateKey.getFormat(), "PKCS#8"); 414 415 byte[] keyMaterial = privateKey.getEncoded(); 416 byte[] mask = new byte[32]; // Zero mask 417 418 try { 419 importWrappedKey(wrapKey( 420 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 421 keyMaterial, 422 mask, 423 KM_KEY_FORMAT_PKCS8, 424 makeEcAuthList(keySize))); 425 } catch (SecureKeyImportUnavailableException e) { 426 assumeNoException("Can only test if secure key import is available", e); 427 } 428 429 // Use Key 430 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 431 keyStore.load(null, null); 432 433 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 434 435 String plaintext = "hello, world"; 436 437 Key importedKey = keyStore.getKey(ALIAS, null); 438 assertTrue(importedKey instanceof PrivateKey); 439 440 // Sign with KS private key, then verify with local public key. 441 Signature s = Signature.getInstance("SHA256withECDSA"); 442 s.initSign((PrivateKey) importedKey); 443 s.update(plaintext.getBytes()); 444 byte[] signature = s.sign(); 445 446 s.initVerify(publicKey); 447 s.update(plaintext.getBytes()); 448 assertTrue(s.verify(signature)); 449 } 450 importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias)451 public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception { 452 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 453 keyStore.load(null, null); 454 455 AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias, 456 KeyProperties.PURPOSE_WRAP_KEY) 457 .setDigests(KeyProperties.DIGEST_SHA256) 458 .build(); 459 Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias, 460 "RSA/ECB/OAEPPadding", spec); 461 keyStore.setEntry(ALIAS, wrappedKeyEntry, null); 462 } 463 importWrappedKey(byte[] wrappedKey)464 public void importWrappedKey(byte[] wrappedKey) throws Exception { 465 importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS); 466 } 467 wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList)468 public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, 469 int keyFormat, DERSequence authorizationList) throws Exception { 470 return wrapKey(publicKey, keyMaterial, mask, keyFormat, authorizationList, true); 471 } 472 wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)473 public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, 474 int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired) 475 throws Exception { 476 // Build description 477 ASN1EncodableVector descriptionItems = new ASN1EncodableVector(); 478 descriptionItems.add(new ASN1Integer(keyFormat)); 479 descriptionItems.add(authorizationList); 480 DERSequence wrappedKeyDescription = new DERSequence(descriptionItems); 481 482 // Generate 12 byte initialization vector 483 byte[] iv = new byte[12]; 484 random.nextBytes(iv); 485 486 // Generate 256 bit AES key. This is the ephemeral key used to encrypt the secure key. 487 byte[] aesKeyBytes = new byte[32]; 488 random.nextBytes(aesKeyBytes); 489 490 // Encrypt ephemeral keys 491 OAEPParameterSpec spec = 492 new OAEPParameterSpec( 493 "SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); 494 Cipher pkCipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); 495 if (correctWrappingRequired) { 496 pkCipher.init(Cipher.ENCRYPT_MODE, publicKey, spec); 497 } else { 498 // Use incorrect OAEPParameters while initializing cipher. By default, main digest and 499 // MGF1 digest are SHA-1 here. 500 pkCipher.init(Cipher.ENCRYPT_MODE, publicKey); 501 } 502 byte[] encryptedEphemeralKeys = pkCipher.doFinal(aesKeyBytes); 503 504 // Encrypt secure key 505 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); 506 SecretKeySpec secretKeySpec = new SecretKeySpec(aesKeyBytes, "AES"); 507 GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE, iv); 508 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec); 509 byte[] aad = wrappedKeyDescription.getEncoded(); 510 511 cipher.updateAAD(aad); 512 byte[] encryptedSecureKey = cipher.doFinal(keyMaterial); 513 // Get GCM tag. Java puts the tag at the end of the ciphertext data :( 514 int len = encryptedSecureKey.length; 515 int tagSize = (GCM_TAG_SIZE / 8); 516 byte[] tag = Arrays.copyOfRange(encryptedSecureKey, len - tagSize, len); 517 518 // Remove GCM tag from end of output 519 encryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize); 520 521 // Build ASN.1 encoded sequence WrappedKeyWrapper 522 ASN1EncodableVector items = new ASN1EncodableVector(); 523 items.add(new ASN1Integer(WRAPPED_FORMAT_VERSION)); 524 items.add(new DEROctetString(encryptedEphemeralKeys)); 525 items.add(new DEROctetString(iv)); 526 items.add(wrappedKeyDescription); 527 items.add(new DEROctetString(encryptedSecureKey)); 528 items.add(new DEROctetString(tag)); 529 return new DERSequence(items).getEncoded(ASN1Encoding.DER); 530 } 531 makeSymKeyAuthList(int size, int algo)532 private DERSequence makeSymKeyAuthList(int size, int algo) { 533 ASN1EncodableVector allPurposes = new ASN1EncodableVector(); 534 allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT)); 535 allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT)); 536 DERSet purposeSet = new DERSet(allPurposes); 537 DERTaggedObject purpose = 538 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 539 DERTaggedObject algorithm = 540 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new ASN1Integer(algo)); 541 DERTaggedObject keySize = 542 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size)); 543 544 ASN1EncodableVector allBlockModes = new ASN1EncodableVector(); 545 allBlockModes.add(new ASN1Integer(KM_MODE_ECB)); 546 allBlockModes.add(new ASN1Integer(KM_MODE_CBC)); 547 DERSet blockModeSet = new DERSet(allBlockModes); 548 DERTaggedObject blockMode = 549 new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet); 550 551 ASN1EncodableVector allPaddings = new ASN1EncodableVector(); 552 allPaddings.add(new ASN1Integer(KM_PAD_PKCS7)); 553 allPaddings.add(new ASN1Integer(KM_PAD_NONE)); 554 DERSet paddingSet = new DERSet(allPaddings); 555 DERTaggedObject padding = 556 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet); 557 558 DERTaggedObject noAuthRequired = 559 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 560 561 // Build sequence 562 ASN1EncodableVector allItems = new ASN1EncodableVector(); 563 allItems.add(purpose); 564 allItems.add(algorithm); 565 allItems.add(keySize); 566 allItems.add(blockMode); 567 allItems.add(padding); 568 allItems.add(noAuthRequired); 569 570 return new DERSequence(allItems); 571 } 572 make3desAuthList(int size)573 private DERSequence make3desAuthList(int size) { 574 return makeSymKeyAuthList(size, KM_ALGORITHM_3DES); 575 } 576 makeAesAuthList(int size)577 private DERSequence makeAesAuthList(int size) { 578 return makeSymKeyAuthList(size, KM_ALGORITHM_AES); 579 } 580 makeRsaAuthList(int size)581 private DERSequence makeRsaAuthList(int size) { 582 ASN1EncodableVector allPurposes = new ASN1EncodableVector(); 583 allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT)); 584 allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT)); 585 allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN)); 586 allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY)); 587 DERSet purposeSet = new DERSet(allPurposes); 588 DERTaggedObject purpose = 589 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 590 591 DERTaggedObject algorithm = 592 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), 593 new ASN1Integer(KM_ALGORITHM_RSA)); 594 DERTaggedObject keySize = 595 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size)); 596 597 ASN1EncodableVector allDigests = new ASN1EncodableVector(); 598 allDigests.add(new ASN1Integer(KM_DIGEST_NONE)); 599 allDigests.add(new ASN1Integer(KM_DIGEST_MD5)); 600 allDigests.add(new ASN1Integer(KM_DIGEST_SHA1)); 601 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224)); 602 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256)); 603 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384)); 604 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512)); 605 DERSet digestSet = new DERSet(allDigests); 606 DERTaggedObject digest = 607 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet); 608 609 ASN1EncodableVector allPaddings = new ASN1EncodableVector(); 610 allPaddings.add(new ASN1Integer(KM_PAD_PKCS7)); 611 allPaddings.add(new ASN1Integer(KM_PAD_NONE)); 612 allPaddings.add(new ASN1Integer(KM_PAD_RSA_OAEP)); 613 allPaddings.add(new ASN1Integer(KM_PAD_RSA_PSS)); 614 allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_ENCRYPT)); 615 allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_SIGN)); 616 DERSet paddingSet = new DERSet(allPaddings); 617 DERTaggedObject padding = 618 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet); 619 620 DERTaggedObject noAuthRequired = 621 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 622 623 // Build sequence 624 ASN1EncodableVector allItems = new ASN1EncodableVector(); 625 allItems.add(purpose); 626 allItems.add(algorithm); 627 allItems.add(keySize); 628 allItems.add(digest); 629 allItems.add(padding); 630 allItems.add(noAuthRequired); 631 632 return new DERSequence(allItems); 633 } 634 makeEcAuthList(int size)635 private DERSequence makeEcAuthList(int size) { 636 ASN1EncodableVector allPurposes = new ASN1EncodableVector(); 637 allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN)); 638 allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY)); 639 DERSet purposeSet = new DERSet(allPurposes); 640 DERTaggedObject purpose = 641 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 642 643 DERTaggedObject algorithm = 644 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), 645 new ASN1Integer(KM_ALGORITHM_EC)); 646 DERTaggedObject keySize = 647 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size)); 648 649 ASN1EncodableVector allDigests = new ASN1EncodableVector(); 650 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224)); 651 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256)); 652 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384)); 653 allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512)); 654 DERSet digestSet = new DERSet(allDigests); 655 DERTaggedObject digest = 656 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet); 657 658 DERTaggedObject noAuthRequired = 659 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 660 661 // Build sequence 662 ASN1EncodableVector allItems = new ASN1EncodableVector(); 663 allItems.add(purpose); 664 allItems.add(algorithm); 665 allItems.add(keySize); 666 allItems.add(digest); 667 allItems.add(noAuthRequired); 668 669 return new DERSequence(allItems); 670 } 671 genKeyPair(String alias, boolean isStrongBoxBacked)672 private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception { 673 KeyPairGenerator kpg = 674 KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); 675 kpg.initialize( 676 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY) 677 .setDigests(KeyProperties.DIGEST_SHA256) 678 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) 679 .setBlockModes(KeyProperties.BLOCK_MODE_ECB) 680 .setIsStrongBoxBacked(isStrongBoxBacked) 681 .build()); 682 return kpg.generateKeyPair(); 683 } 684 } 685