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()