diff --git a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java index 5fbd4a3c6..71cafa20e 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java +++ b/src/main/java/com/amazonaws/encryptionsdk/CryptoAlgorithm.java @@ -24,7 +24,6 @@ import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import com.amazonaws.encryptionsdk.internal.BouncyCastleConfiguration; import com.amazonaws.encryptionsdk.internal.HmacKeyDerivationFunction; import com.amazonaws.encryptionsdk.internal.Constants; @@ -99,27 +98,19 @@ public enum CryptoAlgorithm { private final int dataKeyLen_; private final boolean safeToCache_; - /** - * This block is used to ensure static blocks of BouncyCastleConfiguration are evaluated as a dependency of - * the CryptoAlgorithm class - */ - static { - BouncyCastleConfiguration.init(); - } - /* * Create a mapping between the CiphertextType object and its byte value representation. Make * this is a static method so the map is created when the object is created. This enables fast * lookups of the CryptoAlgorithm given its short value representation. */ - private static final Map ID_MAPPING = new HashMap(); + private static final Map ID_MAPPING = new HashMap<>(); static { for (final CryptoAlgorithm s : EnumSet.allOf(CryptoAlgorithm.class)) { ID_MAPPING.put(s.value_, s); } } - private CryptoAlgorithm( + CryptoAlgorithm( final int blockSizeBits, final int nonceLenBytes, final int tagLenBytes, final long maxContentLen, final String keyAlgo, final int keyLenBytes, final int value, final String dataKeyAlgo, final int dataKeyLen, boolean safeToCache @@ -130,7 +121,7 @@ private CryptoAlgorithm( } - private CryptoAlgorithm( + CryptoAlgorithm( final int blockSizeBits, final int nonceLenBytes, final int tagLenBytes, final long maxContentLen, final String keyAlgo, final int keyLenBytes, final int value, final String dataKeyAlgo, final int dataKeyLen, @@ -164,8 +155,7 @@ private CryptoAlgorithm( * @return the CryptoAlgorithm object that matches the given value, null if no match is found. */ public static CryptoAlgorithm deserialize(final short value) { - final CryptoAlgorithm result = ID_MAPPING.get(value); - return result; + return ID_MAPPING.get(value); } /** diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/BouncyCastleConfiguration.java b/src/main/java/com/amazonaws/encryptionsdk/internal/BouncyCastleConfiguration.java deleted file mode 100644 index deecac895..000000000 --- a/src/main/java/com/amazonaws/encryptionsdk/internal/BouncyCastleConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.amazonaws.encryptionsdk.internal; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.security.Security; - -/** - * This API is internal and subject to change. It is used to add BouncyCastleProvider to the - * java.security.Provider list, and to provide a static reference to BouncyCastleProvider for internal - * classes. - */ -public class BouncyCastleConfiguration { - static final BouncyCastleProvider INTERNAL_BOUNCY_CASTLE_PROVIDER; - static { - BouncyCastleProvider bouncyCastleProvider; - try { - bouncyCastleProvider = new BouncyCastleProvider(); - Security.addProvider(bouncyCastleProvider); - } catch (final Throwable ex) { - bouncyCastleProvider = null; - // Swallow this error. We'll either succeed or fail later with reasonable - // stacktraces. - } - INTERNAL_BOUNCY_CASTLE_PROVIDER = bouncyCastleProvider; - } - - /** - * Prevent instantiation - */ - private BouncyCastleConfiguration() { - } - - /** - * No-op used to force class loading on first call, which will cause the static blocks to be executed - */ - public static void init() {} -} diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/TrailingSignatureAlgorithm.java b/src/main/java/com/amazonaws/encryptionsdk/internal/TrailingSignatureAlgorithm.java index 11a418380..db1c4a8c7 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/internal/TrailingSignatureAlgorithm.java +++ b/src/main/java/com/amazonaws/encryptionsdk/internal/TrailingSignatureAlgorithm.java @@ -59,14 +59,14 @@ private static final class ECDSASignatureAlgorithm extends TrailingSignatureAlgo private static final BigInteger THREE = BigInteger.valueOf(3); private static final BigInteger FOUR = BigInteger.valueOf(4); - private ECDSASignatureAlgorithm(ECGenParameterSpec ecSpec, String messageDigestAlgorithm) { + private ECDSASignatureAlgorithm(ECGenParameterSpec ecSpec, String messageDigestAlgorithm, String hashAndSignAlgorithm) { if (!ecSpec.getName().startsWith(SEC_PRIME_FIELD_PREFIX)) { throw new IllegalStateException("Non-prime curves are not supported at this time"); } this.ecSpec = ecSpec; this.messageDigestAlgorithm = messageDigestAlgorithm; - this.hashAndSignAlgorithm = messageDigestAlgorithm + "withECDSA"; + this.hashAndSignAlgorithm = hashAndSignAlgorithm; try { final AlgorithmParameters parameters = AlgorithmParameters.getInstance(ELLIPTIC_CURVE_ALGORITHM); @@ -190,9 +190,9 @@ public KeyPair generateKey() throws GeneralSecurityException { } private static final ECDSASignatureAlgorithm SHA256_ECDSA_P256 - = new ECDSASignatureAlgorithm(new ECGenParameterSpec(SEC_PRIME_FIELD_PREFIX + "256r1"), "SHA256"); + = new ECDSASignatureAlgorithm(new ECGenParameterSpec(SEC_PRIME_FIELD_PREFIX + "256r1"), "SHA-256", "SHA256withECDSA"); private static final ECDSASignatureAlgorithm SHA384_ECDSA_P384 - = new ECDSASignatureAlgorithm(new ECGenParameterSpec(SEC_PRIME_FIELD_PREFIX + "384r1"), "SHA384"); + = new ECDSASignatureAlgorithm(new ECGenParameterSpec(SEC_PRIME_FIELD_PREFIX + "384r1"), "SHA-384", "SHA384withECDSA"); public static TrailingSignatureAlgorithm forCryptoAlgorithm(CryptoAlgorithm algorithm) { switch (algorithm) { diff --git a/src/main/java/com/amazonaws/encryptionsdk/internal/Utils.java b/src/main/java/com/amazonaws/encryptionsdk/internal/Utils.java index b64c53143..adedea54a 100644 --- a/src/main/java/com/amazonaws/encryptionsdk/internal/Utils.java +++ b/src/main/java/com/amazonaws/encryptionsdk/internal/Utils.java @@ -24,6 +24,7 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; +import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.util.encoders.Base64; /** @@ -268,7 +269,7 @@ public static ByteBuffer limit(final ByteBuffer buff, final int newLimit) { * @return decoded data as a byte array */ public static byte[] decodeBase64String(final String encoded) { - return Base64.decode(encoded); + return encoded.isEmpty() ? ArrayUtils.EMPTY_BYTE_ARRAY : Base64.decode(encoded); } /** diff --git a/src/test/java/com/amazonaws/encryptionsdk/internal/CipherHandlerTest.java b/src/test/java/com/amazonaws/encryptionsdk/internal/CipherHandlerTest.java index bf7ca0b33..d85a0220b 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/internal/CipherHandlerTest.java +++ b/src/test/java/com/amazonaws/encryptionsdk/internal/CipherHandlerTest.java @@ -47,7 +47,7 @@ public void tamperCiphertext() { final byte[] keyBytes = RandomBytesGenerator.generate(cryptoAlgorithm.getKeyLength()); final byte[] nonce = RandomBytesGenerator.generate(cryptoAlgorithm.getNonceLen()); - final SecretKey key = new SecretKeySpec(keyBytes, cryptoAlgorithm.name()); + final SecretKey key = new SecretKeySpec(keyBytes, cryptoAlgorithm.getKeyAlgo()); CipherHandler cipherHandler = createCipherHandler(key, cryptoAlgorithm, Cipher.ENCRYPT_MODE); final byte[] encryptedBytes = cipherHandler.cipherData(nonce, contentAad_, content, 0, content.length); @@ -72,7 +72,7 @@ private byte[] encryptDecrypt(final byte[] content, final CryptoAlgorithm crypto final byte[] keyBytes = RandomBytesGenerator.generate(cryptoAlgorithm.getKeyLength()); final byte[] nonce = RandomBytesGenerator.generate(cryptoAlgorithm.getNonceLen()); - final SecretKey key = new SecretKeySpec(keyBytes, cryptoAlgorithm.name()); + final SecretKey key = new SecretKeySpec(keyBytes, cryptoAlgorithm.getKeyAlgo()); CipherHandler cipherHandler = createCipherHandler(key, cryptoAlgorithm, Cipher.ENCRYPT_MODE); final byte[] encryptedBytes = cipherHandler.cipherData( nonce, contentAad_, content, 0, content.length); diff --git a/src/test/java/com/amazonaws/encryptionsdk/jce/KeyStoreProviderTest.java b/src/test/java/com/amazonaws/encryptionsdk/jce/KeyStoreProviderTest.java index ce059c8e3..985001ca3 100644 --- a/src/test/java/com/amazonaws/encryptionsdk/jce/KeyStoreProviderTest.java +++ b/src/test/java/com/amazonaws/encryptionsdk/jce/KeyStoreProviderTest.java @@ -27,17 +27,14 @@ import java.security.KeyStore.PasswordProtection; import java.security.KeyStoreException; import java.security.SecureRandom; -import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; import javax.crypto.spec.SecretKeySpec; -import javax.security.auth.x500.X500Principal; -import org.bouncycastle.asn1.x509.X509Name; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.x509.X509V3CertificateGenerator; import org.junit.Before; import org.junit.Test; @@ -47,7 +44,18 @@ import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException; import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory; -@SuppressWarnings("deprecation") +/* These internal sun classes are included solely for test purposes as + this test cannot use BouncyCastle cert generation, as there are incompatibilities + between how standard BC and FIPS BC perform cert generation. */ +import sun.security.x509.AlgorithmId; +import sun.security.x509.CertificateAlgorithmId; +import sun.security.x509.CertificateSerialNumber; +import sun.security.x509.CertificateValidity; +import sun.security.x509.CertificateX509Key; +import sun.security.x509.X500Name; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CertInfo; + public class KeyStoreProviderTest { private static final SecureRandom RND = new SecureRandom(); private static final KeyPairGenerator KG; @@ -72,7 +80,7 @@ public void setup() throws Exception { } @Test - public void singleKeyPkcs1() throws GeneralSecurityException { + public void singleKeyPkcs1() throws Exception { addEntry("key1"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/PKCS1Padding", "key1"); final JceMasterKey mk1 = mkp.getMasterKey("key1"); @@ -87,7 +95,7 @@ public void singleKeyPkcs1() throws GeneralSecurityException { } @Test - public void singleKeyOaepSha1() throws GeneralSecurityException { + public void singleKeyOaepSha1() throws Exception { addEntry("key1"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-1AndMGF1Padding", "key1"); @@ -103,7 +111,7 @@ public void singleKeyOaepSha1() throws GeneralSecurityException { } @Test - public void singleKeyOaepSha256() throws GeneralSecurityException { + public void singleKeyOaepSha256() throws Exception { addEntry("key1"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "key1"); @@ -119,7 +127,7 @@ public void singleKeyOaepSha256() throws GeneralSecurityException { } @Test - public void multipleKeys() throws GeneralSecurityException { + public void multipleKeys() throws Exception { addEntry("key1"); addEntry("key2"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", @@ -146,7 +154,7 @@ public void multipleKeys() throws GeneralSecurityException { } @Test(expected = CannotUnwrapDataKeyException.class) - public void encryptOnly() throws GeneralSecurityException { + public void encryptOnly() throws Exception { addPublicEntry("key1"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "key1"); @@ -157,7 +165,7 @@ public void encryptOnly() throws GeneralSecurityException { } @Test - public void escrowAndSymmetric() throws GeneralSecurityException { + public void escrowAndSymmetric() throws Exception { addPublicEntry("key1"); addEntry("key2"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", @@ -185,7 +193,7 @@ public void escrowAndSymmetric() throws GeneralSecurityException { } @Test - public void escrowAndSymmetricSecondProvider() throws GeneralSecurityException { + public void escrowAndSymmetricSecondProvider() throws GeneralSecurityException, IOException { addPublicEntry("key1"); addEntry("key2"); final KeyStoreProvider mkp = new KeyStoreProvider(ks, PP, "KeyStore", "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", @@ -263,40 +271,34 @@ public void keystoreAndRawProvider() throws GeneralSecurityException, IOExceptio assertArrayEquals(PLAINTEXT, crypto.decryptData(ksp, ct.getResult()).getResult()); } - private void addEntry(final String alias) throws GeneralSecurityException { + private void addEntry(final String alias) throws GeneralSecurityException, IOException { final KeyPair pair = KG.generateKeyPair(); - // build a certificate generator - final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - final X500Principal dnName = new X500Principal("cn=" + alias); - - certGen.setSerialNumber(new BigInteger(256, RND)); - certGen.setSubjectDN(new X509Name("dc=" + alias)); - certGen.setIssuerDN(dnName); // use the same - certGen.setNotBefore(new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000)); - certGen.setNotAfter(new Date(System.currentTimeMillis() + 2 * 365 * 24 * 60 * 60 * 1000)); - certGen.setPublicKey(pair.getPublic()); - certGen.setSignatureAlgorithm("SHA256WithRSA"); - final X509Certificate cert = certGen.generate(pair.getPrivate(), "BC"); - - ks.setEntry(alias, new KeyStore.PrivateKeyEntry(pair.getPrivate(), new X509Certificate[] { cert }), PP); + ks.setEntry(alias, new KeyStore.PrivateKeyEntry(pair.getPrivate(), + new X509Certificate[] { generateCertificate(pair, alias) }), PP); } - private void addPublicEntry(final String alias) throws GeneralSecurityException { + private void addPublicEntry(final String alias) throws GeneralSecurityException, IOException { final KeyPair pair = KG.generateKeyPair(); - // build a certificate generator - final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - final X500Principal dnName = new X500Principal("cn=" + alias); - - certGen.setSerialNumber(new BigInteger(256, RND)); - certGen.setSubjectDN(new X509Name("dc=" + alias)); - certGen.setIssuerDN(dnName); // use the same - certGen.setNotBefore(new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000)); - certGen.setNotAfter(new Date(System.currentTimeMillis() + 2 * 365 * 24 * 60 * 60 * 1000)); - certGen.setPublicKey(pair.getPublic()); - certGen.setSignatureAlgorithm("SHA256WithRSA"); - final X509Certificate cert = certGen.generate(pair.getPrivate(), "BC"); - - ks.setEntry(alias, new KeyStore.TrustedCertificateEntry(cert), null); + ks.setEntry(alias, new KeyStore.TrustedCertificateEntry(generateCertificate(pair, alias)), null); + } + + private X509Certificate generateCertificate(final KeyPair pair, final String alias) throws GeneralSecurityException, IOException { + final X509CertInfo info = new X509CertInfo(); + final X500Name name = new X500Name("dc=" + alias); + info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new BigInteger(256, RND))); + info.set(X509CertInfo.SUBJECT, name); + info.set(X509CertInfo.ISSUER, name); + info.set(X509CertInfo.VALIDITY, + new CertificateValidity(Date.from(Instant.now().minus(1, ChronoUnit.DAYS)), + Date.from(Instant.now().plus(730, ChronoUnit.DAYS)))); + info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); + info.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha256WithRSAEncryption_oid))); + + final X509CertImpl cert = new X509CertImpl(info); + cert.sign(pair.getPrivate(), AlgorithmId.sha256WithRSAEncryption_oid.toString()); + + return cert; } private void copyPublicPart(final KeyStore src, final KeyStore dst, final String alias) throws KeyStoreException {