libcore: throw InvalidKeyException instead of ProviderException
In java.security javax.crypto and java.security classes with
late binding, when guessing the provider and none of the
available ones supports the specified key
Bug: 18987633
(cherry pick from e38b83dd96281d178b01476b67d354655bf2de62)
Change-Id: I5931046e9044984baf724157138bf3a7c7ef5e90
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index 795ccad..b11abaa 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -172,7 +172,12 @@
throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm);
}
- SpiAndProvider spiAndProvider = tryAlgorithm(null, provider, algorithm);
+ SpiAndProvider spiAndProvider;
+ try {
+ spiAndProvider = tryAlgorithm(null, provider, algorithm);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
if (spiAndProvider == null) {
if (provider == null) {
throw new NoSuchAlgorithmException("No provider found for " + algorithm);
@@ -187,7 +192,12 @@
return new SignatureImpl(algorithm, provider);
}
- private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ /**
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize any provider.
+ */
+ private static Engine.SpiAndProvider tryAlgorithm(
+ Key key, Provider provider, String algorithm) throws InvalidKeyException {
if (provider != null) {
Provider.Service service = provider.getService(SERVICE, algorithm);
if (service == null) {
@@ -196,15 +206,22 @@
return tryAlgorithmWithProvider(null, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
- if (services == null) {
+ if (services == null || services.isEmpty()) {
return null;
}
+ boolean keySupported = false;
for (Provider.Service service : services) {
- Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
- if (sap != null) {
- return sap;
+ if (key == null || service.supportsParameter(key)) {
+ keySupported = true;
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
+ if (sap != null) {
+ return sap;
+ }
}
}
+ if (!keySupported) {
+ throw new InvalidKeyException("No provider supports the provided key");
+ }
return null;
}
@@ -661,7 +678,7 @@
@Override
void ensureProviderChosen() {
- getSpi(null);
+ getSpi();
}
@Override
@@ -719,8 +736,11 @@
/**
* Makes sure a CipherSpi that matches this type is selected.
+ *
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this signature.
*/
- private SignatureSpi getSpi(Key key) {
+ private SignatureSpi getSpi(Key key) throws InvalidKeyException {
synchronized (initLock) {
if (spiImpl != null && key == null) {
return spiImpl;
@@ -742,7 +762,11 @@
* Convenience call when the Key is not available.
*/
private SignatureSpi getSpi() {
- return getSpi(null);
+ try {
+ return getSpi(null);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
}
@Override
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 5a66c20..9d6bd22 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -297,7 +297,15 @@
}
String[] transformParts = checkTransformation(transformation);
- if (tryCombinations(null, provider, transformParts) == null) {
+
+ boolean providerSupportsAlgorithm;
+ try {
+ providerSupportsAlgorithm =
+ tryCombinations(null /* key */, provider, transformParts) != null;
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
+ if (!providerSupportsAlgorithm) {
if (provider == null) {
throw new NoSuchAlgorithmException("No provider found for " + transformation);
} else {
@@ -340,8 +348,11 @@
/**
* Makes sure a CipherSpi that matches this type is selected.
+ *
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this cipher.
*/
- private CipherSpi getSpi(Key key) {
+ private CipherSpi getSpi(Key key) throws InvalidKeyException {
if (specifiedSpi != null) {
return specifiedSpi;
}
@@ -351,8 +362,8 @@
return spiImpl;
}
- final Engine.SpiAndProvider sap = tryCombinations(key, specifiedProvider,
- transformParts);
+ final Engine.SpiAndProvider sap = tryCombinations(
+ key, specifiedProvider, transformParts);
if (sap == null) {
throw new ProviderException("No provider for " + transformation);
}
@@ -368,7 +379,11 @@
* Convenience call when the Key is not available.
*/
private CipherSpi getSpi() {
- return getSpi(null);
+ try {
+ return getSpi(null);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
}
/**
@@ -396,9 +411,12 @@
* [cipher]//[padding]
* [cipher]
* </pre>
+ *
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize any provider.
*/
private static Engine.SpiAndProvider tryCombinations(Key key, Provider provider,
- String[] transformParts) {
+ String[] transformParts) throws InvalidKeyException {
Engine.SpiAndProvider sap = null;
if (transformParts[1] != null && transformParts[2] != null) {
@@ -428,35 +446,42 @@
return tryTransform(key, provider, transformParts[0], transformParts, NeedToSet.BOTH);
}
+ /**
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this cipher.
+ */
private static Engine.SpiAndProvider tryTransform(Key key, Provider provider, String transform,
- String[] transformParts, NeedToSet type) {
+ String[] transformParts, NeedToSet type) throws InvalidKeyException {
if (provider != null) {
Provider.Service service = provider.getService(SERVICE, transform);
if (service == null) {
return null;
}
- return tryTransformWithProvider(null, transformParts, type, service);
+ return tryTransformWithProvider(transformParts, type, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(transform);
- if (services == null) {
+ if (services == null || services.isEmpty()) {
return null;
}
+ boolean keySupported = false;
for (Provider.Service service : services) {
- Engine.SpiAndProvider sap = tryTransformWithProvider(key, transformParts, type, service);
- if (sap != null) {
- return sap;
+ if (key == null || service.supportsParameter(key)) {
+ keySupported = true;
+ Engine.SpiAndProvider sap = tryTransformWithProvider(transformParts, type, service);
+ if (sap != null) {
+ return sap;
+ }
}
}
+ if (!keySupported) {
+ throw new InvalidKeyException("No provider supports the provided key");
+ }
return null;
}
- private static Engine.SpiAndProvider tryTransformWithProvider(Key key, String[] transformParts,
+ private static Engine.SpiAndProvider tryTransformWithProvider(String[] transformParts,
NeedToSet type, Provider.Service service) {
try {
- if (key != null && !service.supportsParameter(key)) {
- return null;
- }
-
/*
* Check to see if the Cipher even supports the attributes before
* trying to instantiate it.
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index 1ba660d..22c2f3f 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -178,7 +178,13 @@
throw new NullPointerException("algorithm == null");
}
- if (tryAlgorithm(null, provider, algorithm) == null) {
+ boolean providerSupportsAlgorithm;
+ try {
+ providerSupportsAlgorithm = tryAlgorithm(null /* key */, provider, algorithm) != null;
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
+ if (!providerSupportsAlgorithm) {
if (provider == null) {
throw new NoSuchAlgorithmException("No provider found for " + algorithm);
} else {
@@ -189,33 +195,41 @@
return new KeyAgreement(null, provider, algorithm);
}
- private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ /**
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize any provider.
+ */
+ private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm)
+ throws InvalidKeyException {
if (provider != null) {
Provider.Service service = provider.getService(SERVICE, algorithm);
if (service == null) {
return null;
}
- return tryAlgorithmWithProvider(null, service);
+ return tryAlgorithmWithProvider(service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
- if (services == null) {
+ if (services == null || services.isEmpty()) {
return null;
}
+ boolean keySupported = false;
for (Provider.Service service : services) {
- Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
- if (sap != null) {
- return sap;
+ if (key == null || service.supportsParameter(key)) {
+ keySupported = true;
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(service);
+ if (sap != null) {
+ return sap;
+ }
}
}
+ if (!keySupported) {
+ throw new InvalidKeyException("No provider supports the provided key");
+ }
return null;
}
- private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+ private static Engine.SpiAndProvider tryAlgorithmWithProvider(Provider.Service service) {
try {
- if (key != null && !service.supportsParameter(key)) {
- return null;
- }
-
Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
if (sap.spi == null || sap.provider == null) {
return null;
@@ -231,8 +245,11 @@
/**
* Makes sure a KeyAgreementSpi that matches this type is selected.
+ *
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this key agreement.
*/
- private KeyAgreementSpi getSpi(Key key) {
+ private KeyAgreementSpi getSpi(Key key) throws InvalidKeyException {
synchronized (initLock) {
if (spiImpl != null && key == null) {
return spiImpl;
@@ -254,7 +271,11 @@
* Convenience call when the Key is not available.
*/
private KeyAgreementSpi getSpi() {
- return getSpi(null);
+ try {
+ return getSpi(null /* key */);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
}
/**
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index 7da84e6..b8fb947 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -182,7 +182,13 @@
throw new NullPointerException("algorithm == null");
}
- if (tryAlgorithm(null, provider, algorithm) == null) {
+ boolean providerSupportsAlgorithm;
+ try {
+ providerSupportsAlgorithm = tryAlgorithm(null /* key */, provider, algorithm) != null;
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
+ if (!providerSupportsAlgorithm) {
if (provider == null) {
throw new NoSuchAlgorithmException("No provider found for " + algorithm);
} else {
@@ -192,34 +198,41 @@
}
return new Mac(null, provider, algorithm);
}
-
- private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
+ /**
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this mac.
+ */
+ private static Engine.SpiAndProvider tryAlgorithm(
+ Key key, Provider provider, String algorithm) throws InvalidKeyException {
if (provider != null) {
Provider.Service service = provider.getService(SERVICE, algorithm);
if (service == null) {
return null;
}
- return tryAlgorithmWithProvider(null, service);
+ return tryAlgorithmWithProvider(service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
- if (services == null) {
+ if (services == null || services.isEmpty()) {
return null;
}
+ boolean keySupported = false;
for (Provider.Service service : services) {
- Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
- if (sap != null) {
- return sap;
+ if (key == null || service.supportsParameter(key)) {
+ keySupported = true;
+ Engine.SpiAndProvider sap = tryAlgorithmWithProvider(service);
+ if (sap != null) {
+ return sap;
+ }
}
}
+ if (!keySupported) {
+ throw new InvalidKeyException("No provider supports the provided key");
+ }
return null;
}
- private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
+ private static Engine.SpiAndProvider tryAlgorithmWithProvider(Provider.Service service) {
try {
- if (key != null && !service.supportsParameter(key)) {
- return null;
- }
-
Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
if (sap.spi == null || sap.provider == null) {
return null;
@@ -235,8 +248,11 @@
/**
* Makes sure a MacSpi that matches this type is selected.
+ *
+ * @throws InvalidKeyException if the specified key cannot be used to
+ * initialize this mac.
*/
- private MacSpi getSpi(Key key) {
+ private MacSpi getSpi(Key key) throws InvalidKeyException {
synchronized (initLock) {
if (spiImpl != null && provider != null && key == null) {
return spiImpl;
@@ -268,7 +284,11 @@
* Convenience call when the Key is not available.
*/
private MacSpi getSpi() {
- return getSpi(null);
+ try {
+ return getSpi(null);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("InvalidKeyException thrown when key == null", e);
+ }
}
/**
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index d34acd8..9aa75bd 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -94,6 +94,31 @@
}
}
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testSignature_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ put("Signature.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Signature s = Signature.getInstance("FOO");
+ s.initSign(new MockPrivateKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index 611d5f4..c8dd932 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -3263,4 +3263,29 @@
}
}
}
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testCipher_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.DECRYPT_MODE, new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
index ba03f75..9281b43 100644
--- a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
@@ -16,6 +16,7 @@
package libcore.javax.crypto;
+import java.security.InvalidKeyException;
import java.security.Provider;
import java.security.Security;
@@ -54,7 +55,6 @@
public void setup() {
put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
put("KeyAgreement.FOO SupportedKeyClasses", "none");
-
}
};
@@ -67,4 +67,29 @@
Security.removeProvider(mockProvider.getName());
}
}
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testKeyAgreement_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ KeyAgreement c = KeyAgreement.getInstance("FOO");
+ c.init(new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java
new file mode 100644
index 0000000..314a564
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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 libcore.javax.crypto;
+
+import junit.framework.TestCase;
+
+import java.security.InvalidKeyException;
+import java.security.Provider;
+import java.security.Security;
+
+import javax.crypto.Mac;
+
+public class MacTest extends TestCase {
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testMac_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ put("Mac.FOO SupportedKeyClasses", "none");
+
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Mac c = Mac.getInstance("FOO");
+ c.init(new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java
new file mode 100644
index 0000000..0edeba7
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java
@@ -0,0 +1,92 @@
+/*
+ * 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 libcore.javax.crypto;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.MacSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Mock CipherSpi used by {@link libcore.javax.crypto.CipherTest}.
+ */
+public class MockMacSpi extends MacSpi {
+ public static class SpecificKeyTypes extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ System.err.println("Checking key of type " + key.getClass().getName());
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockMacSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected int engineGetMacLength() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected byte[] engineDoFinal() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineReset() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}