diff --git a/gcm-sender-async/pom.xml b/gcm-sender-async/pom.xml index 2f21d6382..41af87e23 100644 --- a/gcm-sender-async/pom.xml +++ b/gcm-sender-async/pom.xml @@ -19,13 +19,6 @@ resilience4j-retry ${resilience4j.version} - - - org.bouncycastle - bcprov-jdk16 - 1.46 - test - diff --git a/service/pom.xml b/service/pom.xml index 1eb3aea55..82a2f584f 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -45,12 +45,6 @@ - - org.bouncycastle - bcprov-jdk16 - 1.46 - - io.github.resilience4j resilience4j-circuitbreaker diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 75c04a5ca..e96c4fec0 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -61,7 +61,6 @@ import java.util.concurrent.TimeUnit; import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; import javax.servlet.ServletRegistration; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.eclipse.jetty.servlets.CrossOriginFilter; import org.jdbi.v3.core.Jdbi; import org.signal.zkgroup.ServerSecretParams; @@ -194,10 +193,6 @@ import org.whispersystems.websocket.setup.WebSocketEnvironment; public class WhisperServerService extends Application { - static { - Security.addProvider(new BouncyCastleProvider()); - } - @Override public void initialize(Bootstrap bootstrap) { bootstrap.addCommand(new VacuumCommand()); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java index 0c499e3bb..e5f1bb4f4 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java @@ -23,6 +23,7 @@ import javax.ws.rs.core.MediaType; import java.io.IOException; import java.security.InvalidKeyException; import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Base64; @@ -45,7 +46,7 @@ public class AttachmentControllerV3 extends AttachmentControllerBase { private final SecureRandom secureRandom; public AttachmentControllerV3(@Nonnull RateLimiters rateLimiters, @Nonnull String domain, @Nonnull String email, int maxSizeInBytes, @Nonnull String pathPrefix, @Nonnull String rsaSigningKey) - throws IOException, InvalidKeyException { + throws IOException, InvalidKeyException, InvalidKeySpecException { this.rateLimiter = rateLimiters.getAttachmentLimiter(); this.canonicalRequestGenerator = new CanonicalRequestGenerator(domain, email, maxSizeInBytes, pathPrefix); this.canonicalRequestSigner = new CanonicalRequestSigner(rsaSigningKey); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/gcp/CanonicalRequestSigner.java b/service/src/main/java/org/whispersystems/textsecuregcm/gcp/CanonicalRequestSigner.java index 6c73700f2..1445e2402 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/gcp/CanonicalRequestSigner.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/gcp/CanonicalRequestSigner.java @@ -5,26 +5,32 @@ package org.whispersystems.textsecuregcm.gcp; -import org.apache.commons.codec.binary.Hex; -import org.bouncycastle.openssl.PEMReader; - -import javax.annotation.Nonnull; import java.io.IOException; -import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Signature; import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import org.apache.commons.codec.binary.Hex; +import org.whispersystems.websocket.util.Base64; public class CanonicalRequestSigner { @Nonnull private final PrivateKey rsaSigningKey; - public CanonicalRequestSigner(@Nonnull String rsaSigningKey) throws IOException, InvalidKeyException { + private static final Pattern PRIVATE_KEY_PATTERN = + Pattern.compile("(?m)(?s)^-+BEGIN PRIVATE KEY-+$(.+)^-+END PRIVATE KEY-+.*$"); + + public CanonicalRequestSigner(@Nonnull String rsaSigningKey) throws IOException, InvalidKeyException, InvalidKeySpecException { this.rsaSigningKey = initializeRsaSigningKey(rsaSigningKey); } @@ -64,11 +70,23 @@ public class CanonicalRequestSigner { return Hex.encodeHexString(signature); } - private static PrivateKey initializeRsaSigningKey(String rsaSigningKey) throws IOException, InvalidKeyException { - final PEMReader pemReader = new PEMReader(new StringReader(rsaSigningKey)); - final PrivateKey key = (PrivateKey) pemReader.readObject(); - testKeyIsValidForSigning(key); - return key; + private static PrivateKey initializeRsaSigningKey(String rsaSigningKey) throws IOException, InvalidKeyException, InvalidKeySpecException { + final Matcher matcher = PRIVATE_KEY_PATTERN.matcher(rsaSigningKey); + + if (matcher.matches()) { + try { + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(matcher.group(1))); + final PrivateKey key = keyFactory.generatePrivate(keySpec); + + testKeyIsValidForSigning(key); + return key; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + throw new IOException("Invalid RSA key"); } private static void testKeyIsValidForSigning(PrivateKey key) throws InvalidKeyException { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/util/CertificateUtil.java b/service/src/main/java/org/whispersystems/textsecuregcm/util/CertificateUtil.java index 9eed5d4c4..9100e0fbe 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/util/CertificateUtil.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/util/CertificateUtil.java @@ -5,15 +5,13 @@ package org.whispersystems.textsecuregcm.util; -import org.bouncycastle.openssl.PEMReader; - import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStreamReader; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; public class CertificateUtil { @@ -38,8 +36,10 @@ public class CertificateUtil { } public static X509Certificate getCertificate(final String certificatePem) throws CertificateException { - try (PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certificatePem.getBytes())))) { - return (X509Certificate) reader.readObject(); + final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + + try (final ByteArrayInputStream pemInputStream = new ByteArrayInputStream(certificatePem.getBytes())) { + return (X509Certificate) certificateFactory.generateCertificate(pemInputStream); } catch (IOException e) { throw new CertificateException(e); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/securebackup/SecureBackupClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/securebackup/SecureBackupClientTest.java index 5ed972e50..7d9976cd4 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/securebackup/SecureBackupClientTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/securebackup/SecureBackupClientTest.java @@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.securebackup; import com.github.tomakehurst.wiremock.junit.WireMockRule; import org.apache.commons.lang3.RandomStringUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -46,11 +45,6 @@ public class SecureBackupClientTest { @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().dynamicHttpsPort()); - @BeforeClass - public static void setupBeforeClass() { - Security.addProvider(new BouncyCastleProvider()); - } - @Before public void setUp() throws CertificateException { accountUuid = UUID.randomUUID(); @@ -87,11 +81,6 @@ public class SecureBackupClientTest { httpExecutor.awaitTermination(1, TimeUnit.SECONDS); } - @AfterClass - public static void tearDownAfterClass() { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - @Test public void deleteStoredData() { final String username = RandomStringUtils.randomAlphabetic(16); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java index fa462aa37..9b4ea87a7 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/securestorage/SecureStorageClientTest.java @@ -7,7 +7,6 @@ package org.whispersystems.textsecuregcm.securestorage; import com.github.tomakehurst.wiremock.junit.WireMockRule; import org.apache.commons.lang3.RandomStringUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -46,11 +45,6 @@ public class SecureStorageClientTest { @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort().dynamicHttpsPort()); - @BeforeClass - public static void setupBeforeClass() { - Security.addProvider(new BouncyCastleProvider()); - } - @Before public void setUp() throws CertificateException { accountUuid = UUID.randomUUID(); @@ -87,11 +81,6 @@ public class SecureStorageClientTest { httpExecutor.awaitTermination(1, TimeUnit.SECONDS); } - @AfterClass - public static void tearDownAfterClass() { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - @Test public void deleteStoredData() { final String username = RandomStringUtils.randomAlphabetic(16); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java index 8708d6a63..56c17e9e1 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java @@ -11,9 +11,6 @@ import io.dropwizard.testing.junit.ResourceTestRule; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; import org.assertj.core.api.InstanceOfAssertFactories; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMWriter; -import org.bouncycastle.openssl.PKCS8Generator; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -36,7 +33,6 @@ import org.whispersystems.textsecuregcm.util.SystemMapper; import javax.ws.rs.core.Response; import java.io.IOException; -import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; @@ -45,8 +41,7 @@ import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.Security; +import java.security.spec.InvalidKeySpecException; import java.util.HashMap; import java.util.Map; @@ -67,17 +62,14 @@ public class AttachmentControllerTest { static { try { - final Provider provider = new BouncyCastleProvider(); - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", provider); + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); final KeyPair keyPair = keyPairGenerator.generateKeyPair(); - final StringWriter stringWriter = new StringWriter(); - final PEMWriter pemWriter = new PEMWriter(stringWriter); - final PKCS8Generator pkcs8Generator = new PKCS8Generator(keyPair.getPrivate()); - pemWriter.writeObject(pkcs8Generator); - pemWriter.close(); - RSA_PRIVATE_KEY_PEM = stringWriter.toString(); - } catch (NoSuchAlgorithmException | IOException e) { + + RSA_PRIVATE_KEY_PEM = "-----BEGIN PRIVATE KEY-----\n" + + Base64.encodeBytes(keyPair.getPrivate().getEncoded()) + "\n" + + "-----END PRIVATE KEY-----"; + } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } } @@ -87,7 +79,6 @@ public class AttachmentControllerTest { static { try { - Security.insertProviderAt(new BouncyCastleProvider(), 0); resources = ResourceTestRule.builder() .addProvider(AuthHelper.getAuthFilter()) .addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class))) @@ -97,22 +88,11 @@ public class AttachmentControllerTest { .addResource(new AttachmentControllerV2(rateLimiters, "accessKey", "accessSecret", "us-east-1", "attachmentv2-bucket")) .addResource(new AttachmentControllerV3(rateLimiters, "some-cdn.signal.org", "signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM)) .build(); - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } catch (IOException | InvalidKeyException e) { + } catch (IOException | InvalidKeyException | InvalidKeySpecException e) { throw new AssertionError(e); } } - @BeforeClass - public static void setup() { - Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - - @AfterClass - public static void tearDown() { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - } - @Test public void testV3Form() { AttachmentDescriptorV3 descriptor = resources.getJerseyTest()