Merge "Expose RSA and ECDSA Signature from Android Keystore Provider." into mnc-dev
This commit is contained in:
@ -122,6 +122,106 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
|
||||
put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
|
||||
"RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
|
||||
|
||||
// --------------------- java.security.Signature
|
||||
putSignatureImpl("NONEwithRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding");
|
||||
|
||||
putSignatureImpl("MD5withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA");
|
||||
put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA");
|
||||
|
||||
putSignatureImpl("SHA1withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA");
|
||||
put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA");
|
||||
|
||||
putSignatureImpl("SHA224withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
|
||||
"SHA224WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11",
|
||||
"SHA224WithRSA");
|
||||
|
||||
putSignatureImpl("SHA256withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
|
||||
"SHA256WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
|
||||
"SHA256WithRSA");
|
||||
|
||||
putSignatureImpl("SHA384withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
|
||||
"SHA384WithRSA");
|
||||
|
||||
putSignatureImpl("SHA512withRSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding");
|
||||
put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA");
|
||||
put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
|
||||
"SHA512WithRSA");
|
||||
|
||||
putSignatureImpl("SHA1withRSA/PSS",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding");
|
||||
putSignatureImpl("SHA224withRSA/PSS",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding");
|
||||
putSignatureImpl("SHA256withRSA/PSS",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding");
|
||||
putSignatureImpl("SHA384withRSA/PSS",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding");
|
||||
putSignatureImpl("SHA512withRSA/PSS",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding");
|
||||
|
||||
putSignatureImpl("NONEwithECDSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
|
||||
|
||||
putSignatureImpl("ECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
|
||||
put("Alg.Alias.Signature.SHA1withECDSA", "ECDSA");
|
||||
put("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA");
|
||||
// iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
|
||||
put("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
|
||||
put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "ECDSA");
|
||||
|
||||
// iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
|
||||
putSignatureImpl("SHA224withECDSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224");
|
||||
// ecdsa-with-SHA224(1)
|
||||
put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
|
||||
|
||||
// iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
|
||||
putSignatureImpl("SHA256withECDSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256");
|
||||
// ecdsa-with-SHA256(2)
|
||||
put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
|
||||
|
||||
putSignatureImpl("SHA384withECDSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384");
|
||||
// ecdsa-with-SHA384(3)
|
||||
put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
|
||||
|
||||
putSignatureImpl("SHA512withECDSA",
|
||||
PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512");
|
||||
// ecdsa-with-SHA512(4)
|
||||
put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
|
||||
put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
|
||||
}
|
||||
|
||||
private void putMacImpl(String algorithm, String implClass) {
|
||||
@ -139,4 +239,10 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
|
||||
put("Cipher." + transformation + " SupportedKeyClasses",
|
||||
KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
|
||||
}
|
||||
|
||||
private void putSignatureImpl(String algorithm, String implClass) {
|
||||
put("Signature." + algorithm, implClass);
|
||||
put("Signature." + algorithm + " SupportedKeyClasses",
|
||||
KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.security.KeyStore;
|
||||
import android.security.keymaster.KeyCharacteristics;
|
||||
import android.security.keymaster.KeymasterArguments;
|
||||
import android.security.keymaster.KeymasterDefs;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.SignatureSpi;
|
||||
|
||||
/**
|
||||
* Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
|
||||
|
||||
public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public NONE() {
|
||||
super(KeymasterDefs.KM_DIGEST_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public SHA1() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA1);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public SHA224() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public SHA256() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public SHA384() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
|
||||
public SHA512() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
|
||||
}
|
||||
}
|
||||
|
||||
private final int mKeymasterDigest;
|
||||
|
||||
private int mGroupSizeBytes = -1;
|
||||
|
||||
AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
|
||||
mKeymasterDigest = keymasterDigest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
|
||||
if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
|
||||
throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
|
||||
+ ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
|
||||
}
|
||||
|
||||
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
|
||||
int errorCode = getKeyStore().getKeyCharacteristics(
|
||||
key.getAlias(), null, null, keyCharacteristics);
|
||||
if (errorCode != KeyStore.NO_ERROR) {
|
||||
throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
|
||||
}
|
||||
int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
|
||||
if (keySizeBits == -1) {
|
||||
throw new InvalidKeyException("Size of key not known");
|
||||
}
|
||||
mGroupSizeBytes = (keySizeBits + 7) / 8;
|
||||
|
||||
super.initKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void resetAll() {
|
||||
mGroupSizeBytes = -1;
|
||||
super.resetAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void resetWhilePreservingInitState() {
|
||||
super.resetWhilePreservingInitState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAlgorithmSpecificParametersToBegin(
|
||||
@NonNull KeymasterArguments keymasterArgs) {
|
||||
keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
|
||||
keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAdditionalEntropyAmountForBegin() {
|
||||
return (isSigning()) ? mGroupSizeBytes : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore;
|
||||
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
|
||||
/**
|
||||
* {@link ECPublicKey} backed by keystore.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey {
|
||||
|
||||
private final ECParameterSpec mParams;
|
||||
private final ECPoint mW;
|
||||
|
||||
public AndroidKeyStoreECPublicKey(String alias, byte[] x509EncodedForm, ECParameterSpec params,
|
||||
ECPoint w) {
|
||||
super(alias, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
|
||||
mParams = params;
|
||||
mW = w;
|
||||
}
|
||||
|
||||
public AndroidKeyStoreECPublicKey(String alias, ECPublicKey info) {
|
||||
this(alias, info.getEncoded(), info.getParams(), info.getW());
|
||||
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported key export format: " + info.getFormat());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECParameterSpec getParams() {
|
||||
return mParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECPoint getW() {
|
||||
return mW;
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import android.security.KeyStore;
|
||||
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.Mac;
|
||||
@ -118,12 +119,15 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
Object spi;
|
||||
if (cryptoPrimitive instanceof Mac) {
|
||||
if (cryptoPrimitive instanceof Signature) {
|
||||
spi = ((Signature) cryptoPrimitive).getCurrentSpi();
|
||||
} else if (cryptoPrimitive instanceof Mac) {
|
||||
spi = ((Mac) cryptoPrimitive).getCurrentSpi();
|
||||
} else if (cryptoPrimitive instanceof Cipher) {
|
||||
spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
|
||||
throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
|
||||
+ ". Supported: Signature, Mac, Cipher");
|
||||
}
|
||||
if (spi == null) {
|
||||
throw new IllegalStateException("Crypto primitive not initialized");
|
||||
|
@ -30,7 +30,7 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem
|
||||
|
||||
public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
|
||||
BigInteger publicExponent) {
|
||||
super(alias, "RSA", x509EncodedForm);
|
||||
super(alias, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
|
||||
mModulus = modulus;
|
||||
mPublicExponent = publicExponent;
|
||||
}
|
||||
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.security.keymaster.KeymasterArguments;
|
||||
import android.security.keymaster.KeymasterDefs;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.SignatureSpi;
|
||||
|
||||
/**
|
||||
* Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
|
||||
|
||||
abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
|
||||
PKCS1Padding(int keymasterDigest) {
|
||||
super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int getAdditionalEntropyAmountForBegin() {
|
||||
// No entropy required for this deterministic signature scheme.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NONEWithPKCS1Padding extends PKCS1Padding {
|
||||
public NONEWithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MD5WithPKCS1Padding extends PKCS1Padding {
|
||||
public MD5WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_MD5);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
|
||||
public SHA1WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA1);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
|
||||
public SHA224WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
|
||||
public SHA256WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
|
||||
public SHA384WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
|
||||
public SHA512WithPKCS1Padding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
|
||||
private static final int SALT_LENGTH_BYTES = 20;
|
||||
|
||||
PSSPadding(int keymasterDigest) {
|
||||
super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int getAdditionalEntropyAmountForBegin() {
|
||||
return (isSigning()) ? SALT_LENGTH_BYTES : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA1WithPSSPadding extends PSSPadding {
|
||||
public SHA1WithPSSPadding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA1);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA224WithPSSPadding extends PSSPadding {
|
||||
public SHA224WithPSSPadding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_224);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA256WithPSSPadding extends PSSPadding {
|
||||
public SHA256WithPSSPadding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_256);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA384WithPSSPadding extends PSSPadding {
|
||||
public SHA384WithPSSPadding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_384);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SHA512WithPSSPadding extends PSSPadding {
|
||||
public SHA512WithPSSPadding() {
|
||||
super(KeymasterDefs.KM_DIGEST_SHA_2_512);
|
||||
}
|
||||
}
|
||||
|
||||
private final int mKeymasterDigest;
|
||||
private final int mKeymasterPadding;
|
||||
|
||||
AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
|
||||
mKeymasterDigest = keymasterDigest;
|
||||
mKeymasterPadding = keymasterPadding;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
|
||||
if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
|
||||
throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
|
||||
+ ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
|
||||
}
|
||||
super.initKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void resetAll() {
|
||||
super.resetAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void resetWhilePreservingInitState() {
|
||||
super.resetWhilePreservingInitState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addAlgorithmSpecificParametersToBegin(
|
||||
@NonNull KeymasterArguments keymasterArgs) {
|
||||
keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
|
||||
keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
|
||||
keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
|
||||
}
|
||||
}
|
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.IBinder;
|
||||
import android.security.KeyStore;
|
||||
import android.security.KeyStoreException;
|
||||
import android.security.keymaster.KeymasterArguments;
|
||||
import android.security.keymaster.KeymasterDefs;
|
||||
import android.security.keymaster.OperationResult;
|
||||
|
||||
import com.android.org.conscrypt.util.EmptyArray;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.ProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.security.SignatureSpi;
|
||||
|
||||
/**
|
||||
* Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
|
||||
implements KeyStoreCryptoOperation {
|
||||
private final KeyStore mKeyStore;
|
||||
|
||||
// Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
|
||||
// and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
|
||||
private boolean mSigning;
|
||||
private AndroidKeyStoreKey mKey;
|
||||
|
||||
/**
|
||||
* Token referencing this operation inside keystore service. It is initialized by
|
||||
* {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
|
||||
* {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
|
||||
*/
|
||||
private IBinder mOperationToken;
|
||||
private long mOperationHandle;
|
||||
private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer;
|
||||
|
||||
/**
|
||||
* Encountered exception which could not be immediately thrown because it was encountered inside
|
||||
* a method that does not throw checked exception. This exception will be thrown from
|
||||
* {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
|
||||
* {@code engineUpdate} starts ignoring input data.
|
||||
*/
|
||||
private Exception mCachedException;
|
||||
|
||||
AndroidKeyStoreSignatureSpiBase() {
|
||||
mKeyStore = KeyStore.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
|
||||
engineInitSign(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
resetAll();
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
if (privateKey == null) {
|
||||
throw new InvalidKeyException("Unsupported key: null");
|
||||
}
|
||||
AndroidKeyStoreKey keystoreKey;
|
||||
if (privateKey instanceof AndroidKeyStorePrivateKey) {
|
||||
keystoreKey = (AndroidKeyStoreKey) privateKey;
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported private key type: " + privateKey);
|
||||
}
|
||||
mSigning = true;
|
||||
initKey(keystoreKey);
|
||||
appRandom = random;
|
||||
ensureKeystoreOperationInitialized();
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
resetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
|
||||
resetAll();
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
if (publicKey == null) {
|
||||
throw new InvalidKeyException("Unsupported key: null");
|
||||
}
|
||||
AndroidKeyStoreKey keystoreKey;
|
||||
if (publicKey instanceof AndroidKeyStorePublicKey) {
|
||||
keystoreKey = (AndroidKeyStorePublicKey) publicKey;
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported public key type: " + publicKey);
|
||||
}
|
||||
mSigning = false;
|
||||
initKey(keystoreKey);
|
||||
appRandom = null;
|
||||
ensureKeystoreOperationInitialized();
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
resetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this signature instance to use the provided key.
|
||||
*
|
||||
* @throws InvalidKeyException if the {@code key} is not suitable.
|
||||
*/
|
||||
@CallSuper
|
||||
protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
|
||||
* cipher instance.
|
||||
*
|
||||
* <p>Subclasses storing additional state should override this method, reset the additional
|
||||
* state, and then chain to superclass.
|
||||
*/
|
||||
@CallSuper
|
||||
protected void resetAll() {
|
||||
IBinder operationToken = mOperationToken;
|
||||
if (operationToken != null) {
|
||||
mOperationToken = null;
|
||||
mKeyStore.abort(operationToken);
|
||||
}
|
||||
mSigning = false;
|
||||
mKey = null;
|
||||
appRandom = null;
|
||||
mOperationToken = null;
|
||||
mOperationHandle = 0;
|
||||
mMessageStreamer = null;
|
||||
mCachedException = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this cipher while preserving the initialized state. This must be equivalent to
|
||||
* rolling back the cipher's state to just after the most recent {@code engineInit} completed
|
||||
* successfully.
|
||||
*
|
||||
* <p>Subclasses storing additional post-init state should override this method, reset the
|
||||
* additional state, and then chain to superclass.
|
||||
*/
|
||||
@CallSuper
|
||||
protected void resetWhilePreservingInitState() {
|
||||
IBinder operationToken = mOperationToken;
|
||||
if (operationToken != null) {
|
||||
mOperationToken = null;
|
||||
mKeyStore.abort(operationToken);
|
||||
}
|
||||
mOperationHandle = 0;
|
||||
mMessageStreamer = null;
|
||||
mCachedException = null;
|
||||
}
|
||||
|
||||
private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
|
||||
if (mMessageStreamer != null) {
|
||||
return;
|
||||
}
|
||||
if (mCachedException != null) {
|
||||
return;
|
||||
}
|
||||
if (mKey == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
KeymasterArguments keymasterInputArgs = new KeymasterArguments();
|
||||
addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
|
||||
byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
|
||||
appRandom, getAdditionalEntropyAmountForBegin());
|
||||
|
||||
OperationResult opResult = mKeyStore.begin(
|
||||
mKey.getAlias(),
|
||||
mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
|
||||
true, // permit aborting this operation if keystore runs out of resources
|
||||
keymasterInputArgs,
|
||||
additionalEntropy);
|
||||
if (opResult == null) {
|
||||
throw new KeyStoreConnectException();
|
||||
}
|
||||
|
||||
// Store operation token and handle regardless of the error code returned by KeyStore to
|
||||
// ensure that the operation gets aborted immediately if the code below throws an exception.
|
||||
mOperationToken = opResult.token;
|
||||
mOperationHandle = opResult.operationHandle;
|
||||
|
||||
// If necessary, throw an exception due to KeyStore operation having failed.
|
||||
InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
|
||||
mKeyStore, mKey, opResult.resultCode);
|
||||
if (e != null) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (mOperationToken == null) {
|
||||
throw new ProviderException("Keystore returned null operation token");
|
||||
}
|
||||
if (mOperationHandle == 0) {
|
||||
throw new ProviderException("Keystore returned invalid operation handle");
|
||||
}
|
||||
|
||||
mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
|
||||
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
|
||||
mKeyStore, opResult.token));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long getOperationHandle() {
|
||||
return mOperationHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
|
||||
if (mCachedException != null) {
|
||||
throw new SignatureException(mCachedException);
|
||||
}
|
||||
|
||||
try {
|
||||
ensureKeystoreOperationInitialized();
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new SignatureException(e);
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] output;
|
||||
try {
|
||||
output = mMessageStreamer.update(b, off, len);
|
||||
} catch (KeyStoreException e) {
|
||||
throw new SignatureException(e);
|
||||
}
|
||||
|
||||
if (output.length != 0) {
|
||||
throw new ProviderException(
|
||||
"Update operation unexpectedly produced output: " + output.length + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineUpdate(byte b) throws SignatureException {
|
||||
engineUpdate(new byte[] {b}, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void engineUpdate(ByteBuffer input) {
|
||||
byte[] b;
|
||||
int off;
|
||||
int len = input.remaining();
|
||||
if (input.hasArray()) {
|
||||
b = input.array();
|
||||
off = input.arrayOffset() + input.position();
|
||||
input.position(input.limit());
|
||||
} else {
|
||||
b = new byte[len];
|
||||
off = 0;
|
||||
input.get(b);
|
||||
}
|
||||
|
||||
try {
|
||||
engineUpdate(b, off, len);
|
||||
} catch (SignatureException e) {
|
||||
mCachedException = e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int engineSign(byte[] out, int outOffset, int outLen)
|
||||
throws SignatureException {
|
||||
return super.engineSign(out, outOffset, outLen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final byte[] engineSign() throws SignatureException {
|
||||
if (mCachedException != null) {
|
||||
throw new SignatureException(mCachedException);
|
||||
}
|
||||
|
||||
byte[] signature;
|
||||
try {
|
||||
ensureKeystoreOperationInitialized();
|
||||
signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0);
|
||||
} catch (InvalidKeyException | KeyStoreException e) {
|
||||
throw new SignatureException(e);
|
||||
}
|
||||
|
||||
resetWhilePreservingInitState();
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean engineVerify(byte[] signature) throws SignatureException {
|
||||
if (mCachedException != null) {
|
||||
throw new SignatureException(mCachedException);
|
||||
}
|
||||
|
||||
boolean result;
|
||||
try {
|
||||
ensureKeystoreOperationInitialized();
|
||||
mMessageStreamer.flush();
|
||||
OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
|
||||
if (opResult == null) {
|
||||
throw new KeyStoreConnectException();
|
||||
}
|
||||
switch (opResult.resultCode) {
|
||||
case KeyStore.NO_ERROR:
|
||||
result = true;
|
||||
break;
|
||||
case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
|
||||
result = false;
|
||||
break;
|
||||
default:
|
||||
throw new SignatureException(
|
||||
KeyStore.getKeyStoreException(opResult.resultCode));
|
||||
}
|
||||
} catch (InvalidKeyException | KeyStoreException e) {
|
||||
throw new SignatureException(e);
|
||||
}
|
||||
|
||||
resetWhilePreservingInitState();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
|
||||
throws SignatureException {
|
||||
return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
protected final Object engineGetParameter(String param) throws InvalidParameterException {
|
||||
throw new InvalidParameterException();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
protected final void engineSetParameter(String param, Object value)
|
||||
throws InvalidParameterException {
|
||||
throw new InvalidParameterException();
|
||||
}
|
||||
|
||||
protected final KeyStore getKeyStore() {
|
||||
return mKeyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this signature is initialized for signing, {@code false} if this
|
||||
* signature is initialized for verification.
|
||||
*/
|
||||
protected final boolean isSigning() {
|
||||
return mSigning;
|
||||
}
|
||||
|
||||
// The methods below need to be implemented by subclasses.
|
||||
|
||||
/**
|
||||
* Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
|
||||
* {@code begin} operation.
|
||||
*
|
||||
* <p>For signature verification, this should be {@code 0} because verification should not be
|
||||
* consuming any entropy. For signature generation, this value should match (or exceed) the
|
||||
* amount of Shannon entropy of the produced signature assuming the key and the message are
|
||||
* known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the
|
||||
* RSA signature with PKCS#1 padding this should be {@code 0}.
|
||||
*/
|
||||
protected abstract int getAdditionalEntropyAmountForBegin();
|
||||
|
||||
/**
|
||||
* Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
|
||||
*
|
||||
* @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
|
||||
* parameters.
|
||||
*/
|
||||
protected abstract void addAlgorithmSpecificParametersToBegin(
|
||||
@NonNull KeymasterArguments keymasterArgs);
|
||||
}
|
Reference in New Issue
Block a user