mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-19 23:38:07 +01:00
Add client identity to key and certificate to KeyTransparencyServiceClient
This commit is contained in:
@@ -609,6 +609,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
config.getKeyTransparencyServiceConfiguration().host(),
|
||||
config.getKeyTransparencyServiceConfiguration().port(),
|
||||
config.getKeyTransparencyServiceConfiguration().tlsCertificate(),
|
||||
config.getKeyTransparencyServiceConfiguration().clientCertificate(),
|
||||
config.getKeyTransparencyServiceConfiguration().clientPrivateKey().value(),
|
||||
keyTransparencyCallbackExecutor);
|
||||
SecureValueRecovery2Client secureValueRecovery2Client = new SecureValueRecovery2Client(svr2CredentialsGenerator,
|
||||
secureValueRecoveryServiceExecutor, secureValueRecoveryServiceRetryExecutor, config.getSvr2Configuration());
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Positive;
|
||||
|
||||
public record KeyTransparencyServiceConfiguration(@NotBlank String host,
|
||||
@Positive int port,
|
||||
@NotBlank String tlsCertificate) {}
|
||||
@NotBlank String tlsCertificate,
|
||||
@NotBlank String clientCertificate,
|
||||
@NotNull SecretString clientPrivateKey) {}
|
||||
|
||||
@@ -7,27 +7,40 @@ import io.grpc.ChannelCredentials;
|
||||
import io.grpc.Deadline;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.ManagedChannel;
|
||||
|
||||
import io.grpc.TlsChannelCredentials;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.grpc.TlsChannelCredentials;
|
||||
import org.signal.keytransparency.client.ConsistencyParameters;
|
||||
import org.signal.keytransparency.client.KeyTransparencyQueryServiceGrpc;
|
||||
import org.signal.keytransparency.client.MonitorKey;
|
||||
import org.signal.keytransparency.client.MonitorRequest;
|
||||
import org.signal.keytransparency.client.SearchRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.metrics.MetricsUtil;
|
||||
import org.whispersystems.textsecuregcm.util.CompletableFutureUtil;
|
||||
|
||||
public class KeyTransparencyServiceClient implements Managed {
|
||||
|
||||
private static final String DAYS_UNTIL_CLIENT_CERTIFICATE_EXPIRATION_GAUGE_NAME =
|
||||
MetricsUtil.name(KeyTransparencyServiceClient.class, "daysUntilClientCertificateExpiration");
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(KeyTransparencyServiceClient.class);
|
||||
|
||||
private final Executor callbackExecutor;
|
||||
private final String host;
|
||||
private final int port;
|
||||
@@ -39,19 +52,63 @@ public class KeyTransparencyServiceClient implements Managed {
|
||||
final String host,
|
||||
final int port,
|
||||
final String tlsCertificate,
|
||||
final String clientCertificate,
|
||||
final String clientPrivateKey,
|
||||
final Executor callbackExecutor
|
||||
) throws IOException {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
try (final ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(
|
||||
tlsCertificate.getBytes(StandardCharsets.UTF_8))) {
|
||||
tlsCertificate.getBytes(StandardCharsets.UTF_8));
|
||||
final ByteArrayInputStream clientCertificateInputStream = new ByteArrayInputStream(
|
||||
clientCertificate.getBytes(StandardCharsets.UTF_8));
|
||||
final ByteArrayInputStream clientPrivateKeyInputStream = new ByteArrayInputStream(
|
||||
clientPrivateKey.getBytes(StandardCharsets.UTF_8))
|
||||
) {
|
||||
tlsChannelCredentials = TlsChannelCredentials.newBuilder()
|
||||
.trustManager(certificateInputStream)
|
||||
.keyManager(clientCertificateInputStream, clientPrivateKeyInputStream)
|
||||
.build();
|
||||
|
||||
configureClientCertificateMetrics(clientCertificate);
|
||||
|
||||
}
|
||||
this.callbackExecutor = callbackExecutor;
|
||||
}
|
||||
|
||||
private void configureClientCertificateMetrics(String clientCertificate) {
|
||||
try {
|
||||
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
final Collection<? extends Certificate> certificates = cf.generateCertificates(
|
||||
new ByteArrayInputStream(clientCertificate.getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
if (certificates.isEmpty()) {
|
||||
logger.warn("No client certificate found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (certificates.size() > 1) {
|
||||
throw new IllegalArgumentException("Unexpected number of client certificates: " + certificates.size());
|
||||
}
|
||||
|
||||
final Certificate certificate = certificates.iterator().next();
|
||||
|
||||
if (certificate instanceof X509Certificate x509Cert) {
|
||||
final Instant expiration = Instant.ofEpochMilli(x509Cert.getNotAfter().getTime());
|
||||
|
||||
Metrics.gauge(DAYS_UNTIL_CLIENT_CERTIFICATE_EXPIRATION_GAUGE_NAME,
|
||||
this,
|
||||
(ignored) -> Duration.between(Instant.now(), expiration).toDays());
|
||||
|
||||
} else {
|
||||
logger.error("Certificate was of unexpected type: {}", certificate.getClass().getName());
|
||||
}
|
||||
|
||||
} catch (CertificateException e) {
|
||||
throw new AssertionError("JDKs are required to support X.509 algorithms", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public CompletableFuture<byte[]> search(
|
||||
final ByteString searchKey,
|
||||
|
||||
Reference in New Issue
Block a user