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_PURPOSE; 45 import static android.security.keymaster.KeymasterDefs.KM_TAG_ALGORITHM; 46 import static android.security.keymaster.KeymasterDefs.KM_TAG_KEY_SIZE; 47 import static android.security.keymaster.KeymasterDefs.KM_TAG_BLOCK_MODE; 48 import static android.security.keymaster.KeymasterDefs.KM_TAG_DIGEST; 49 import static android.security.keymaster.KeymasterDefs.KM_TAG_PADDING; 50 import static android.security.keymaster.KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED; 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.DEREncodableVector; 73 import org.bouncycastle.asn1.DERInteger; 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(wrapKey( 205 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 206 keyMaterial, 207 mask, 208 KM_KEY_FORMAT_RAW, 209 makeAesAuthList(keyMaterial.length * 8), 210 false /* incorrect wrapping required*/)); 211 } catch (SecureKeyImportUnavailableException e) { 212 assumeNoException("Can only test if secure key import is available", e); 213 } catch (KeyStoreException e) { 214 exception = e; 215 } 216 assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull(); 217 assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class); 218 android.security.KeyStoreException ksException = 219 (android.security.KeyStoreException) exception.getCause(); 220 assertFalse("Importing incorrectly wrapped key should not cause transient failure in" 221 + " Key{Mint/Master}. That means performing same operation will fail always.", 222 ksException.isTransientFailure()); 223 } 224 225 @Test testKeyStore_ImportWrappedKeyWrappingKeyMissing()226 public void testKeyStore_ImportWrappedKeyWrappingKeyMissing() throws Exception { 227 final String EXPECTED_FAILURE = "Failed to import wrapped key. Keystore error code: 7"; 228 KeyStoreException exception = null; 229 230 try { 231 byte [] fakeWrappedKey = new byte[1]; 232 importWrappedKey(fakeWrappedKey, WRAPPING_KEY_ALIAS + "_Missing"); 233 } catch (KeyStoreException e) { 234 exception = e; 235 } 236 237 assertWithMessage("Did not hit a failure but expected one").that(exception).isNotNull(); 238 239 assertThat(exception.getMessage()).isEqualTo(EXPECTED_FAILURE); 240 assertThat(exception.getCause()).isInstanceOf(android.security.KeyStoreException.class); 241 android.security.KeyStoreException ksException = 242 (android.security.KeyStoreException) exception.getCause(); 243 assertThat(ksException.getNumericErrorCode()).isEqualTo( 244 android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST); 245 } 246 247 @Test testKeyStore_ImportWrappedKey_3DES()248 public void testKeyStore_ImportWrappedKey_3DES() throws Exception { 249 testKeyStore_ImportWrappedKey_3DES(false); 250 } 251 252 @Test testKeyStore_ImportWrappedKey_3DES_StrongBox()253 public void testKeyStore_ImportWrappedKey_3DES_StrongBox() throws Exception { 254 testKeyStore_ImportWrappedKey_3DES(true); 255 } 256 testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox)257 public void testKeyStore_ImportWrappedKey_3DES(boolean isStrongBox) throws Exception { 258 if (isStrongBox) { 259 TestUtils.assumeStrongBox(); 260 } 261 262 assumeTrue("Can only test if device supports 3DES", TestUtils.supports3DES()); 263 264 KeyGenerator kg = KeyGenerator.getInstance("DESEDE"); 265 kg.init(168); 266 Key swKey = kg.generateKey(); 267 268 byte[] keyMaterial = swKey.getEncoded(); 269 byte[] mask = new byte[24]; // Zero mask 270 271 try { 272 importWrappedKey(wrapKey( 273 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 274 keyMaterial, 275 mask, 276 KM_KEY_FORMAT_RAW, 277 make3desAuthList(168))); 278 } catch (SecureKeyImportUnavailableException e) { 279 assumeNoException("Can only test if secure key import is available", e); 280 } 281 282 // Use Key 283 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 284 keyStore.load(null, null); 285 286 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 287 288 Key importedKey = keyStore.getKey(ALIAS, null); 289 String plaintext = "hello, world"; 290 291 Cipher c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 292 c.init(Cipher.ENCRYPT_MODE, importedKey); 293 IvParameterSpec paramSpec = new IvParameterSpec(c.getIV()); 294 byte[] encrypted = c.doFinal(plaintext.getBytes()); 295 296 // Decrypt using key imported into keystore. 297 c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 298 c.init(Cipher.DECRYPT_MODE, importedKey, paramSpec); 299 assertEquals(new String(c.doFinal(encrypted)), plaintext); 300 301 // Decrypt using local software copy of the key. 302 c = Cipher.getInstance("DESede/CBC/PKCS7Padding"); 303 c.init(Cipher.DECRYPT_MODE, swKey, paramSpec); 304 assertEquals(new String(c.doFinal(encrypted)), plaintext); 305 } 306 307 @Test testKeyStore_ImportWrappedKey_RSA()308 public void testKeyStore_ImportWrappedKey_RSA() throws Exception { 309 testKeyStore_ImportWrappedKey_RSA(false); 310 } 311 312 @Test testKeyStore_ImportWrappedKey_RSA_StrongBox()313 public void testKeyStore_ImportWrappedKey_RSA_StrongBox() throws Exception { 314 testKeyStore_ImportWrappedKey_RSA(true); 315 } 316 testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox)317 public void testKeyStore_ImportWrappedKey_RSA(boolean isStrongBox) throws Exception { 318 assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.", 319 TestUtils.getVendorApiLevel() >= 35); 320 321 if (isStrongBox) { 322 TestUtils.assumeStrongBox(); 323 } 324 325 KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 326 // Both TEE and Strongbox must support 2048-bit keys. 327 int keySize = 2048; 328 kpg.initialize(keySize); 329 KeyPair kp = kpg.generateKeyPair(); 330 PublicKey publicKey = kp.getPublic(); 331 PrivateKey privateKey = kp.getPrivate(); 332 333 assertEquals(privateKey.getFormat(), "PKCS#8"); 334 335 byte[] keyMaterial = privateKey.getEncoded(); 336 byte[] mask = new byte[32]; // Zero mask 337 338 try { 339 importWrappedKey(wrapKey( 340 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 341 keyMaterial, 342 mask, 343 KM_KEY_FORMAT_PKCS8, 344 makeRsaAuthList(keySize))); 345 } catch (SecureKeyImportUnavailableException e) { 346 assumeNoException("Can only test if secure key import is available", e); 347 } 348 349 // Use Key 350 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 351 keyStore.load(null, null); 352 353 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 354 355 String plaintext = "hello, world"; 356 357 Key importedKey = keyStore.getKey(ALIAS, null); 358 assertTrue(importedKey instanceof PrivateKey); 359 360 // Encrypt with KS private key, then decrypt with local public key. 361 Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 362 c.init(Cipher.ENCRYPT_MODE, importedKey); 363 byte[] encrypted = c.doFinal(plaintext.getBytes()); 364 365 c.init(Cipher.DECRYPT_MODE, publicKey); 366 assertEquals(new String(c.doFinal(encrypted)), plaintext); 367 368 // Encrypt with local public key, then decrypt with KS private key. 369 c.init(Cipher.ENCRYPT_MODE, publicKey); 370 encrypted = c.doFinal(plaintext.getBytes()); 371 372 c.init(Cipher.DECRYPT_MODE, importedKey); 373 assertEquals(new String(c.doFinal(encrypted)), plaintext); 374 375 // Sign with KS private key, then verify with local public key. 376 Signature s = Signature.getInstance("SHA256withRSA"); 377 s.initSign((PrivateKey) importedKey); 378 s.update(plaintext.getBytes()); 379 byte[] signature = s.sign(); 380 381 s.initVerify(publicKey); 382 s.update(plaintext.getBytes()); 383 assertTrue(s.verify(signature)); 384 } 385 386 @Test testKeyStore_ImportWrappedKey_EC()387 public void testKeyStore_ImportWrappedKey_EC() throws Exception { 388 testKeyStore_ImportWrappedKey_EC(false); 389 } 390 391 @Test testKeyStore_ImportWrappedKey_EC_StrongBox()392 public void testKeyStore_ImportWrappedKey_EC_StrongBox() throws Exception { 393 testKeyStore_ImportWrappedKey_EC(true); 394 } 395 testKeyStore_ImportWrappedKey_EC(boolean isStrongBox)396 public void testKeyStore_ImportWrappedKey_EC(boolean isStrongBox) throws Exception { 397 assumeTrue("Only VSR V+ KeyMint implementations are expected to pass.", 398 TestUtils.getVendorApiLevel() >= 35); 399 400 if (isStrongBox) { 401 TestUtils.assumeStrongBox(); 402 } 403 404 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); 405 // Both TEE and Strongbox must support P256 curve. 406 int keySize = 256; 407 kpg.initialize(keySize); 408 KeyPair kp = kpg.generateKeyPair(); 409 PublicKey publicKey = kp.getPublic(); 410 PrivateKey privateKey = kp.getPrivate(); 411 412 assertEquals(privateKey.getFormat(), "PKCS#8"); 413 414 byte[] keyMaterial = privateKey.getEncoded(); 415 byte[] mask = new byte[32]; // Zero mask 416 417 try { 418 importWrappedKey(wrapKey( 419 genKeyPair(WRAPPING_KEY_ALIAS, isStrongBox).getPublic(), 420 keyMaterial, 421 mask, 422 KM_KEY_FORMAT_PKCS8, 423 makeEcAuthList(keySize))); 424 } catch (SecureKeyImportUnavailableException e) { 425 assumeNoException("Can only test if secure key import is available", e); 426 } 427 428 // Use Key 429 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 430 keyStore.load(null, null); 431 432 assertTrue("Failed to load key after wrapped import", keyStore.containsAlias(ALIAS)); 433 434 String plaintext = "hello, world"; 435 436 Key importedKey = keyStore.getKey(ALIAS, null); 437 assertTrue(importedKey instanceof PrivateKey); 438 439 // Sign with KS private key, then verify with local public key. 440 Signature s = Signature.getInstance("SHA256withECDSA"); 441 s.initSign((PrivateKey) importedKey); 442 s.update(plaintext.getBytes()); 443 byte[] signature = s.sign(); 444 445 s.initVerify(publicKey); 446 s.update(plaintext.getBytes()); 447 assertTrue(s.verify(signature)); 448 } 449 importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias)450 public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception { 451 KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 452 keyStore.load(null, null); 453 454 AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias, 455 KeyProperties.PURPOSE_WRAP_KEY) 456 .setDigests(KeyProperties.DIGEST_SHA256) 457 .build(); 458 Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias, 459 "RSA/ECB/OAEPPadding", spec); 460 keyStore.setEntry(ALIAS, wrappedKeyEntry, null); 461 } 462 importWrappedKey(byte[] wrappedKey)463 public void importWrappedKey(byte[] wrappedKey) throws Exception { 464 importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS); 465 } 466 wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList)467 public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, 468 int keyFormat, DERSequence authorizationList) throws Exception { 469 return wrapKey(publicKey, keyMaterial, mask, keyFormat, authorizationList, true); 470 } 471 wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)472 public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask, 473 int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired) 474 throws Exception { 475 // Build description 476 DEREncodableVector descriptionItems = new DEREncodableVector(); 477 descriptionItems.add(new DERInteger(keyFormat)); 478 descriptionItems.add(authorizationList); 479 DERSequence wrappedKeyDescription = new DERSequence(descriptionItems); 480 481 // Generate 12 byte initialization vector 482 byte[] iv = new byte[12]; 483 random.nextBytes(iv); 484 485 // Generate 256 bit AES key. This is the ephemeral key used to encrypt the secure key. 486 byte[] aesKeyBytes = new byte[32]; 487 random.nextBytes(aesKeyBytes); 488 489 // Encrypt ephemeral keys 490 OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); 491 Cipher pkCipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); 492 if (correctWrappingRequired) { 493 pkCipher.init(Cipher.ENCRYPT_MODE, publicKey, spec); 494 } else { 495 // Use incorrect OAEPParameters while initializing cipher. By default, main digest and 496 // MGF1 digest are SHA-1 here. 497 pkCipher.init(Cipher.ENCRYPT_MODE, publicKey); 498 } 499 byte[] encryptedEphemeralKeys = pkCipher.doFinal(aesKeyBytes); 500 501 // Encrypt secure key 502 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); 503 SecretKeySpec secretKeySpec = new SecretKeySpec(aesKeyBytes, "AES"); 504 GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE, iv); 505 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec); 506 byte[] aad = wrappedKeyDescription.getEncoded(); 507 508 cipher.updateAAD(aad); 509 byte[] encryptedSecureKey = cipher.doFinal(keyMaterial); 510 // Get GCM tag. Java puts the tag at the end of the ciphertext data :( 511 int len = encryptedSecureKey.length; 512 int tagSize = (GCM_TAG_SIZE / 8); 513 byte[] tag = Arrays.copyOfRange(encryptedSecureKey, len - tagSize, len); 514 515 // Remove GCM tag from end of output 516 encryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize); 517 518 // Build ASN.1 DER encoded sequence WrappedKeyWrapper 519 DEREncodableVector items = new DEREncodableVector(); 520 items.add(new DERInteger(WRAPPED_FORMAT_VERSION)); 521 items.add(new DEROctetString(encryptedEphemeralKeys)); 522 items.add(new DEROctetString(iv)); 523 items.add(wrappedKeyDescription); 524 items.add(new DEROctetString(encryptedSecureKey)); 525 items.add(new DEROctetString(tag)); 526 return new DERSequence(items).getEncoded(ASN1Encoding.DER); 527 } 528 makeSymKeyAuthList(int size, int algo)529 private DERSequence makeSymKeyAuthList(int size, int algo) { 530 DEREncodableVector allPurposes = new DEREncodableVector(); 531 allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT)); 532 allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT)); 533 DERSet purposeSet = new DERSet(allPurposes); 534 DERTaggedObject purpose = 535 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 536 DERTaggedObject algorithm = 537 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new DERInteger(algo)); 538 DERTaggedObject keySize = 539 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size)); 540 541 DEREncodableVector allBlockModes = new DEREncodableVector(); 542 allBlockModes.add(new DERInteger(KM_MODE_ECB)); 543 allBlockModes.add(new DERInteger(KM_MODE_CBC)); 544 DERSet blockModeSet = new DERSet(allBlockModes); 545 DERTaggedObject blockMode = 546 new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet); 547 548 DEREncodableVector allPaddings = new DEREncodableVector(); 549 allPaddings.add(new DERInteger(KM_PAD_PKCS7)); 550 allPaddings.add(new DERInteger(KM_PAD_NONE)); 551 DERSet paddingSet = new DERSet(allPaddings); 552 DERTaggedObject padding = 553 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet); 554 555 DERTaggedObject noAuthRequired = 556 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 557 558 // Build sequence 559 DEREncodableVector allItems = new DEREncodableVector(); 560 allItems.add(purpose); 561 allItems.add(algorithm); 562 allItems.add(keySize); 563 allItems.add(blockMode); 564 allItems.add(padding); 565 allItems.add(noAuthRequired); 566 567 return new DERSequence(allItems); 568 } 569 make3desAuthList(int size)570 private DERSequence make3desAuthList(int size) { 571 return makeSymKeyAuthList(size, KM_ALGORITHM_3DES); 572 } 573 makeAesAuthList(int size)574 private DERSequence makeAesAuthList(int size) { 575 return makeSymKeyAuthList(size, KM_ALGORITHM_AES); 576 } 577 makeRsaAuthList(int size)578 private DERSequence makeRsaAuthList(int size) { 579 DEREncodableVector allPurposes = new DEREncodableVector(); 580 allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT)); 581 allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT)); 582 allPurposes.add(new DERInteger(KM_PURPOSE_SIGN)); 583 allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY)); 584 DERSet purposeSet = new DERSet(allPurposes); 585 DERTaggedObject purpose = 586 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 587 588 DERTaggedObject algorithm = 589 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), 590 new DERInteger(KM_ALGORITHM_RSA)); 591 DERTaggedObject keySize = 592 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size)); 593 594 DEREncodableVector allDigests = new DEREncodableVector(); 595 allDigests.add(new DERInteger(KM_DIGEST_NONE)); 596 allDigests.add(new DERInteger(KM_DIGEST_MD5)); 597 allDigests.add(new DERInteger(KM_DIGEST_SHA1)); 598 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224)); 599 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256)); 600 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384)); 601 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512)); 602 DERSet digestSet = new DERSet(allDigests); 603 DERTaggedObject digest = 604 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet); 605 606 DEREncodableVector allPaddings = new DEREncodableVector(); 607 allPaddings.add(new DERInteger(KM_PAD_PKCS7)); 608 allPaddings.add(new DERInteger(KM_PAD_NONE)); 609 allPaddings.add(new DERInteger(KM_PAD_RSA_OAEP)); 610 allPaddings.add(new DERInteger(KM_PAD_RSA_PSS)); 611 allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_ENCRYPT)); 612 allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_SIGN)); 613 DERSet paddingSet = new DERSet(allPaddings); 614 DERTaggedObject padding = 615 new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet); 616 617 DERTaggedObject noAuthRequired = 618 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 619 620 // Build sequence 621 DEREncodableVector allItems = new DEREncodableVector(); 622 allItems.add(purpose); 623 allItems.add(algorithm); 624 allItems.add(keySize); 625 allItems.add(digest); 626 allItems.add(padding); 627 allItems.add(noAuthRequired); 628 629 return new DERSequence(allItems); 630 } 631 makeEcAuthList(int size)632 private DERSequence makeEcAuthList(int size) { 633 DEREncodableVector allPurposes = new DEREncodableVector(); 634 allPurposes.add(new DERInteger(KM_PURPOSE_SIGN)); 635 allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY)); 636 DERSet purposeSet = new DERSet(allPurposes); 637 DERTaggedObject purpose = 638 new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet); 639 640 DERTaggedObject algorithm = 641 new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), 642 new DERInteger(KM_ALGORITHM_EC)); 643 DERTaggedObject keySize = 644 new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size)); 645 646 DEREncodableVector allDigests = new DEREncodableVector(); 647 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224)); 648 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256)); 649 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384)); 650 allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512)); 651 DERSet digestSet = new DERSet(allDigests); 652 DERTaggedObject digest = 653 new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet); 654 655 DERTaggedObject noAuthRequired = 656 new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE); 657 658 // Build sequence 659 DEREncodableVector allItems = new DEREncodableVector(); 660 allItems.add(purpose); 661 allItems.add(algorithm); 662 allItems.add(keySize); 663 allItems.add(digest); 664 allItems.add(noAuthRequired); 665 666 return new DERSequence(allItems); 667 } 668 genKeyPair(String alias, boolean isStrongBoxBacked)669 private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception { 670 KeyPairGenerator kpg = 671 KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); 672 kpg.initialize( 673 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY) 674 .setDigests(KeyProperties.DIGEST_SHA256) 675 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) 676 .setBlockModes(KeyProperties.BLOCK_MODE_ECB) 677 .setIsStrongBoxBacked(isStrongBoxBacked) 678 .build()); 679 return kpg.generateKeyPair(); 680 } 681 } 682