Expose RSA and ECDSA Signature from Android Keystore Provider.

The RSA Signature supports PKCS#1 and PSS padding.

Bug: 18088752
Bug: 20912868
Change-Id: I03cdc86d1935af36f7c87a0b23d67f813829cfb0
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index aa2b946..e555cc0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -122,6 +122,106 @@
                 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 @@
         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);
+    }
 }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
new file mode 100644
index 0000000..335da07
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -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;
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
new file mode 100644
index 0000000..3ed396d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 649a515..de4213e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -20,6 +20,7 @@
 
 import java.security.Provider;
 import java.security.Security;
+import java.security.Signature;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
@@ -118,12 +119,15 @@
             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");
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
index 36bc997b..08a173e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -30,7 +30,7 @@
 
     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;
     }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
new file mode 100644
index 0000000..898336d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
@@ -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);
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
new file mode 100644
index 0000000..4c4062f
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -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);
+}