1/* 2 * Copyright (C) 2015 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 17package android.security.keystore; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.security.KeyStore; 22import android.security.keymaster.KeyCharacteristics; 23import android.security.keymaster.KeymasterArguments; 24import android.security.keymaster.KeymasterDefs; 25 26import java.security.AlgorithmParameters; 27import java.security.InvalidAlgorithmParameterException; 28import java.security.InvalidKeyException; 29import java.security.Key; 30import java.security.NoSuchAlgorithmException; 31import java.security.PrivateKey; 32import java.security.ProviderException; 33import java.security.spec.AlgorithmParameterSpec; 34import java.security.spec.InvalidParameterSpecException; 35import java.security.spec.MGF1ParameterSpec; 36 37import javax.crypto.Cipher; 38import javax.crypto.CipherSpi; 39import javax.crypto.spec.OAEPParameterSpec; 40import javax.crypto.spec.PSource; 41 42/** 43 * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption. 44 * 45 * @hide 46 */ 47abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase { 48 49 /** 50 * Raw RSA cipher without any padding. 51 */ 52 public static final class NoPadding extends AndroidKeyStoreRSACipherSpi { 53 public NoPadding() { 54 super(KeymasterDefs.KM_PAD_NONE); 55 } 56 57 @Override 58 protected boolean adjustConfigForEncryptingWithPrivateKey() { 59 // RSA encryption with no padding using private key is a way to implement raw RSA 60 // signatures which JCA does not expose via Signature. We thus have to support this. 61 setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); 62 return true; 63 } 64 65 @Override 66 protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} 67 68 @Override 69 protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params) 70 throws InvalidAlgorithmParameterException { 71 if (params != null) { 72 throw new InvalidAlgorithmParameterException( 73 "Unexpected parameters: " + params + ". No parameters supported"); 74 } 75 } 76 77 @Override 78 protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) 79 throws InvalidAlgorithmParameterException { 80 81 if (params != null) { 82 throw new InvalidAlgorithmParameterException( 83 "Unexpected parameters: " + params + ". No parameters supported"); 84 } 85 } 86 87 @Override 88 protected AlgorithmParameters engineGetParameters() { 89 return null; 90 } 91 92 @Override 93 protected final int getAdditionalEntropyAmountForBegin() { 94 return 0; 95 } 96 97 @Override 98 protected final int getAdditionalEntropyAmountForFinish() { 99 return 0; 100 } 101 } 102 103 /** 104 * RSA cipher with PKCS#1 v1.5 encryption padding. 105 */ 106 public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi { 107 public PKCS1Padding() { 108 super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT); 109 } 110 111 @Override 112 protected boolean adjustConfigForEncryptingWithPrivateKey() { 113 // RSA encryption with PCKS#1 padding using private key is a way to implement RSA 114 // signatures with PKCS#1 padding. We have to support this for legacy reasons. 115 setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); 116 setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); 117 return true; 118 } 119 120 @Override 121 protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} 122 123 @Override 124 protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params) 125 throws InvalidAlgorithmParameterException { 126 if (params != null) { 127 throw new InvalidAlgorithmParameterException( 128 "Unexpected parameters: " + params + ". No parameters supported"); 129 } 130 } 131 132 @Override 133 protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) 134 throws InvalidAlgorithmParameterException { 135 136 if (params != null) { 137 throw new InvalidAlgorithmParameterException( 138 "Unexpected parameters: " + params + ". No parameters supported"); 139 } 140 } 141 142 @Override 143 protected AlgorithmParameters engineGetParameters() { 144 return null; 145 } 146 147 @Override 148 protected final int getAdditionalEntropyAmountForBegin() { 149 return 0; 150 } 151 152 @Override 153 protected final int getAdditionalEntropyAmountForFinish() { 154 return (isEncrypting()) ? getModulusSizeBytes() : 0; 155 } 156 } 157 158 /** 159 * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF. 160 */ 161 abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi { 162 163 private static final String MGF_ALGORITGM_MGF1 = "MGF1"; 164 165 private int mKeymasterDigest = -1; 166 private int mDigestOutputSizeBytes; 167 168 OAEPWithMGF1Padding(int keymasterDigest) { 169 super(KeymasterDefs.KM_PAD_RSA_OAEP); 170 mKeymasterDigest = keymasterDigest; 171 mDigestOutputSizeBytes = 172 (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8; 173 } 174 175 @Override 176 protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {} 177 178 @Override 179 protected final void initAlgorithmSpecificParameters( 180 @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { 181 if (params == null) { 182 return; 183 } 184 185 if (!(params instanceof OAEPParameterSpec)) { 186 throw new InvalidAlgorithmParameterException( 187 "Unsupported parameter spec: " + params 188 + ". Only OAEPParameterSpec supported"); 189 } 190 OAEPParameterSpec spec = (OAEPParameterSpec) params; 191 if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) { 192 throw new InvalidAlgorithmParameterException( 193 "Unsupported MGF: " + spec.getMGFAlgorithm() 194 + ". Only " + MGF_ALGORITGM_MGF1 + " supported"); 195 } 196 String jcaDigest = spec.getDigestAlgorithm(); 197 int keymasterDigest; 198 try { 199 keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest); 200 } catch (IllegalArgumentException e) { 201 throw new InvalidAlgorithmParameterException( 202 "Unsupported digest: " + jcaDigest, e); 203 } 204 switch (keymasterDigest) { 205 case KeymasterDefs.KM_DIGEST_SHA1: 206 case KeymasterDefs.KM_DIGEST_SHA_2_224: 207 case KeymasterDefs.KM_DIGEST_SHA_2_256: 208 case KeymasterDefs.KM_DIGEST_SHA_2_384: 209 case KeymasterDefs.KM_DIGEST_SHA_2_512: 210 // Permitted. 211 break; 212 default: 213 throw new InvalidAlgorithmParameterException( 214 "Unsupported digest: " + jcaDigest); 215 } 216 AlgorithmParameterSpec mgfParams = spec.getMGFParameters(); 217 if (mgfParams == null) { 218 throw new InvalidAlgorithmParameterException("MGF parameters must be provided"); 219 } 220 // Check whether MGF parameters match the OAEPParameterSpec 221 if (!(mgfParams instanceof MGF1ParameterSpec)) { 222 throw new InvalidAlgorithmParameterException("Unsupported MGF parameters" 223 + ": " + mgfParams + ". Only MGF1ParameterSpec supported"); 224 } 225 MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams; 226 String mgf1JcaDigest = mgfSpec.getDigestAlgorithm(); 227 if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) { 228 throw new InvalidAlgorithmParameterException( 229 "Unsupported MGF1 digest: " + mgf1JcaDigest 230 + ". Only " + KeyProperties.DIGEST_SHA1 + " supported"); 231 } 232 PSource pSource = spec.getPSource(); 233 if (!(pSource instanceof PSource.PSpecified)) { 234 throw new InvalidAlgorithmParameterException( 235 "Unsupported source of encoding input P: " + pSource 236 + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported"); 237 } 238 PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource; 239 byte[] pSourceValue = pSourceSpecified.getValue(); 240 if ((pSourceValue != null) && (pSourceValue.length > 0)) { 241 throw new InvalidAlgorithmParameterException( 242 "Unsupported source of encoding input P: " + pSource 243 + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported"); 244 } 245 mKeymasterDigest = keymasterDigest; 246 mDigestOutputSizeBytes = 247 (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8; 248 } 249 250 @Override 251 protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params) 252 throws InvalidAlgorithmParameterException { 253 if (params == null) { 254 return; 255 } 256 257 OAEPParameterSpec spec; 258 try { 259 spec = params.getParameterSpec(OAEPParameterSpec.class); 260 } catch (InvalidParameterSpecException e) { 261 throw new InvalidAlgorithmParameterException("OAEP parameters required" 262 + ", but not found in parameters: " + params, e); 263 } 264 if (spec == null) { 265 throw new InvalidAlgorithmParameterException("OAEP parameters required" 266 + ", but not provided in parameters: " + params); 267 } 268 initAlgorithmSpecificParameters(spec); 269 } 270 271 @Override 272 protected final AlgorithmParameters engineGetParameters() { 273 OAEPParameterSpec spec = 274 new OAEPParameterSpec( 275 KeyProperties.Digest.fromKeymaster(mKeymasterDigest), 276 MGF_ALGORITGM_MGF1, 277 MGF1ParameterSpec.SHA1, 278 PSource.PSpecified.DEFAULT); 279 try { 280 AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP"); 281 params.init(spec); 282 return params; 283 } catch (NoSuchAlgorithmException e) { 284 throw new ProviderException( 285 "Failed to obtain OAEP AlgorithmParameters", e); 286 } catch (InvalidParameterSpecException e) { 287 throw new ProviderException( 288 "Failed to initialize OAEP AlgorithmParameters with an IV", 289 e); 290 } 291 } 292 293 @Override 294 protected final void addAlgorithmSpecificParametersToBegin( 295 KeymasterArguments keymasterArgs) { 296 super.addAlgorithmSpecificParametersToBegin(keymasterArgs); 297 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); 298 } 299 300 @Override 301 protected final void loadAlgorithmSpecificParametersFromBeginResult( 302 @NonNull KeymasterArguments keymasterArgs) { 303 super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs); 304 } 305 306 @Override 307 protected final int getAdditionalEntropyAmountForBegin() { 308 return 0; 309 } 310 311 @Override 312 protected final int getAdditionalEntropyAmountForFinish() { 313 return (isEncrypting()) ? mDigestOutputSizeBytes : 0; 314 } 315 } 316 317 public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding { 318 public OAEPWithSHA1AndMGF1Padding() { 319 super(KeymasterDefs.KM_DIGEST_SHA1); 320 } 321 } 322 323 public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding { 324 public OAEPWithSHA224AndMGF1Padding() { 325 super(KeymasterDefs.KM_DIGEST_SHA_2_224); 326 } 327 } 328 329 public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding { 330 public OAEPWithSHA256AndMGF1Padding() { 331 super(KeymasterDefs.KM_DIGEST_SHA_2_256); 332 } 333 } 334 335 public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding { 336 public OAEPWithSHA384AndMGF1Padding() { 337 super(KeymasterDefs.KM_DIGEST_SHA_2_384); 338 } 339 } 340 341 public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding { 342 public OAEPWithSHA512AndMGF1Padding() { 343 super(KeymasterDefs.KM_DIGEST_SHA_2_512); 344 } 345 } 346 347 private final int mKeymasterPadding; 348 private int mKeymasterPaddingOverride; 349 350 private int mModulusSizeBytes = -1; 351 352 AndroidKeyStoreRSACipherSpi(int keymasterPadding) { 353 mKeymasterPadding = keymasterPadding; 354 } 355 356 @Override 357 protected final void initKey(int opmode, Key key) throws InvalidKeyException { 358 if (key == null) { 359 throw new InvalidKeyException("Unsupported key: null"); 360 } 361 if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) { 362 throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() 363 + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported"); 364 } 365 AndroidKeyStoreKey keystoreKey; 366 if (key instanceof AndroidKeyStorePrivateKey) { 367 keystoreKey = (AndroidKeyStoreKey) key; 368 } else if (key instanceof AndroidKeyStorePublicKey) { 369 keystoreKey = (AndroidKeyStoreKey) key; 370 } else { 371 throw new InvalidKeyException("Unsupported key type: " + key); 372 } 373 374 if (keystoreKey instanceof PrivateKey) { 375 // Private key 376 switch (opmode) { 377 case Cipher.DECRYPT_MODE: 378 case Cipher.UNWRAP_MODE: 379 // Permitted 380 break; 381 case Cipher.ENCRYPT_MODE: 382 case Cipher.WRAP_MODE: 383 if (!adjustConfigForEncryptingWithPrivateKey()) { 384 throw new InvalidKeyException( 385 "RSA private keys cannot be used with " + opmodeToString(opmode) 386 + " and padding " 387 + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) 388 + ". Only RSA public keys supported for this mode"); 389 } 390 break; 391 default: 392 throw new InvalidKeyException( 393 "RSA private keys cannot be used with opmode: " + opmode); 394 } 395 } else { 396 // Public key 397 switch (opmode) { 398 case Cipher.ENCRYPT_MODE: 399 case Cipher.WRAP_MODE: 400 // Permitted 401 break; 402 case Cipher.DECRYPT_MODE: 403 case Cipher.UNWRAP_MODE: 404 throw new InvalidKeyException( 405 "RSA public keys cannot be used with " + opmodeToString(opmode) 406 + " and padding " 407 + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) 408 + ". Only RSA private keys supported for this opmode."); 409 // break; 410 default: 411 throw new InvalidKeyException( 412 "RSA public keys cannot be used with " + opmodeToString(opmode)); 413 } 414 } 415 416 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 417 int errorCode = getKeyStore().getKeyCharacteristics( 418 keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics); 419 if (errorCode != KeyStore.NO_ERROR) { 420 throw getKeyStore().getInvalidKeyException( 421 keystoreKey.getAlias(), keystoreKey.getUid(), errorCode); 422 } 423 long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); 424 if (keySizeBits == -1) { 425 throw new InvalidKeyException("Size of key not known"); 426 } else if (keySizeBits > Integer.MAX_VALUE) { 427 throw new InvalidKeyException("Key too large: " + keySizeBits + " bits"); 428 } 429 mModulusSizeBytes = (int) ((keySizeBits + 7) / 8); 430 431 setKey(keystoreKey); 432 } 433 434 /** 435 * Adjusts the configuration of this cipher for encrypting using the private key. 436 * 437 * <p>The default implementation does nothing and refuses to adjust the configuration. 438 * 439 * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting 440 * using private key is not permitted for this cipher. 441 */ 442 protected boolean adjustConfigForEncryptingWithPrivateKey() { 443 return false; 444 } 445 446 @Override 447 protected final void resetAll() { 448 mModulusSizeBytes = -1; 449 mKeymasterPaddingOverride = -1; 450 super.resetAll(); 451 } 452 453 @Override 454 protected final void resetWhilePreservingInitState() { 455 super.resetWhilePreservingInitState(); 456 } 457 458 @Override 459 protected void addAlgorithmSpecificParametersToBegin( 460 @NonNull KeymasterArguments keymasterArgs) { 461 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); 462 int keymasterPadding = getKeymasterPaddingOverride(); 463 if (keymasterPadding == -1) { 464 keymasterPadding = mKeymasterPadding; 465 } 466 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); 467 int purposeOverride = getKeymasterPurposeOverride(); 468 if ((purposeOverride != -1) 469 && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN) 470 || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) { 471 // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE. 472 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE); 473 } 474 } 475 476 @Override 477 protected void loadAlgorithmSpecificParametersFromBeginResult( 478 @NonNull KeymasterArguments keymasterArgs) { 479 } 480 481 @Override 482 protected final int engineGetBlockSize() { 483 // Not a block cipher, according to the RI 484 return 0; 485 } 486 487 @Override 488 protected final byte[] engineGetIV() { 489 // IV never used 490 return null; 491 } 492 493 @Override 494 protected final int engineGetOutputSize(int inputLen) { 495 return getModulusSizeBytes(); 496 } 497 498 protected final int getModulusSizeBytes() { 499 if (mModulusSizeBytes == -1) { 500 throw new IllegalStateException("Not initialized"); 501 } 502 return mModulusSizeBytes; 503 } 504 505 /** 506 * Overrides the default padding of the crypto operation. 507 */ 508 protected final void setKeymasterPaddingOverride(int keymasterPadding) { 509 mKeymasterPaddingOverride = keymasterPadding; 510 } 511 512 protected final int getKeymasterPaddingOverride() { 513 return mKeymasterPaddingOverride; 514 } 515} 516