Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## 1.6.1 -- Unreleased
### Maintenance
* Add support for standard test vectors via `testVectorZip` system property.
* Add support for standard test vectors via `testVectorZip` system property.
* No longer require use of BouncyCastle with RSA `JceMasterKey`s

## 1.6.0 -- 2019-05-31

Expand Down
62 changes: 54 additions & 8 deletions src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,22 @@
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;

import com.amazonaws.encryptionsdk.CryptoAlgorithm;
Expand Down Expand Up @@ -237,17 +242,58 @@ public WrappingData(final Cipher cipher, final byte[] extraInfo) {
}

private static class Rsa extends JceMasterKey {
// MGF1 with SHA-224 isn't really supported, but we include it in the regex because we need it
// for proper handling of the algorithm.
private static final Pattern SUPPORTED_TRANSFORMATIONS =
Pattern.compile("RSA/ECB/(?:PKCS1Padding|OAEPWithSHA-(?:1|256|384|512)AndMGF1Padding)",
Pattern.compile("RSA/ECB/(?:PKCS1Padding|OAEPWith(SHA-(?:1|224|256|384|512))AndMGF1Padding)",
Pattern.CASE_INSENSITIVE);
private final AlgorithmParameterSpec parameterSpec_;
private final String transformation_;

private Rsa(PublicKey wrappingKey, PrivateKey unwrappingKey, String providerName, String keyId,
String transformation) {
super(wrappingKey, unwrappingKey, providerName, keyId);
transformation_ = transformation;
if (!SUPPORTED_TRANSFORMATIONS.matcher(transformation_).matches()) {
LOGGER.warning(transformation_ + " is not officially supported by the JceMasterKey");

final Matcher matcher = SUPPORTED_TRANSFORMATIONS.matcher(transformation);
if (matcher.matches()) {
final String hashUnknownCase = matcher.group(1);
if (hashUnknownCase != null) {
// OAEP mode a.k.a PKCS #1v2
final String hash = hashUnknownCase.toUpperCase();
transformation_ = "RSA/ECB/OAEPPadding";

final MGF1ParameterSpec mgf1Spec;
switch (hash) {
case "SHA-1":
mgf1Spec = MGF1ParameterSpec.SHA1;
break;
case "SHA-224":
LOGGER.warning(transformation + " is not officially supported by the JceMasterKey");
mgf1Spec = MGF1ParameterSpec.SHA224;
break;
case "SHA-256":
mgf1Spec = MGF1ParameterSpec.SHA256;
break;
case "SHA-384":
mgf1Spec = MGF1ParameterSpec.SHA384;
break;
case "SHA-512":
mgf1Spec = MGF1ParameterSpec.SHA512;
break;
default:
throw new IllegalArgumentException("Unsupported algorithm: " + transformation);
}
parameterSpec_ = new OAEPParameterSpec(hash, "MGF1", mgf1Spec, PSource.PSpecified.DEFAULT);
} else {
// PKCS #1 v1.x
transformation_ = transformation;
parameterSpec_ = null;
}
} else {
LOGGER.warning(transformation + " is not officially supported by the JceMasterKey");
// Unsupported transformation, just use exactly what we are given
transformation_ = transformation;
parameterSpec_ = null;
}
}

Expand All @@ -256,8 +302,8 @@ protected WrappingData buildWrappingCipher(Key key, Map<String, String> encrypti
throws GeneralSecurityException {
// We require BouncyCastle to avoid some bugs in the default Java implementation
// of OAEP.
final Cipher cipher = Cipher.getInstance(transformation_, "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
final Cipher cipher = Cipher.getInstance(transformation_);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec_);
return new WrappingData(cipher, EMPTY_ARRAY);
}

Expand All @@ -269,8 +315,8 @@ protected Cipher buildUnwrappingCipher(Key key, byte[] extraInfo, int offset,
}
// We require BouncyCastle to avoid some bugs in the default Java implementation
// of OAEP.
final Cipher cipher = Cipher.getInstance(transformation_, "BC");
cipher.init(Cipher.DECRYPT_MODE, key);
final Cipher cipher = Cipher.getInstance(transformation_);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec_);
return cipher;
}
}
Expand Down