public void PublicKeyPinningHashShouldBeEqual() { var(c1, c2) = CertificateUtils.CreateTwoTestCertificatesWithSameKey(Environment.MachineName, "sameKey"); var c1Cert = new X509Certificate2(c1); var c2Cert = new X509Certificate2(c2); var h1 = c1Cert.GetPublicKeyPinningHash(); var h2 = c2Cert.GetPublicKeyPinningHash(); Assert.Equal(h1, h2); }
public async Task CanTrustNewClientCertBasedOnPublicKeyPinningHash() { // Setting up two clusters with external replication cluster1 --> cluster2 var clusterSize = 1; var databaseName = GetDatabaseName(); // We generate two certificates for cluster 1, an original and a renewed certificate with same private key. var(cluster1CertBytes, cluster1ReplacementCertBytes) = CertificateUtils.CreateTwoTestCertificatesWithSameKey(Environment.MachineName, "RavenTestsTwoCerts"); var cluster1CertFileName = GetTempFileName(); var cluster1ReplacementCertFileName = GetTempFileName(); File.WriteAllBytes(cluster1CertFileName, cluster1CertBytes); File.WriteAllBytes(cluster1ReplacementCertFileName, cluster1ReplacementCertBytes); var cert = new X509Certificate2(cluster1CertBytes); var replacementCert = new X509Certificate2(cluster1ReplacementCertBytes); Assert.Equal(cert.GetPublicKeyPinningHash(), replacementCert.GetPublicKeyPinningHash()); // Create cluster 1 with the original certificate. Later we will replace that with the renewed certificate. var leader1 = await CreateRaftClusterAndGetLeader(clusterSize, false, useSsl : true, serverCertPath : cluster1CertFileName); var cluster1Cert = new X509Certificate2(cluster1CertFileName); var adminCertificate1 = AskServerForClientCertificate(cluster1CertFileName, new Dictionary <string, DatabaseAccess>(), SecurityClearance.ClusterAdmin, server: leader1); await CreateDatabaseInCluster(databaseName, clusterSize, leader1.WebUrl, adminCertificate1); // Cluster 2 gets a normal test certificate var leader2 = await CreateRaftClusterAndGetLeader(clusterSize, false, useSsl : true); var cluster2Cert = new X509Certificate2(_selfSignedCertFileName); var adminCertificate2 = AskServerForClientCertificate(_selfSignedCertFileName, new Dictionary <string, DatabaseAccess>(), SecurityClearance.ClusterAdmin, server: leader2); await CreateDatabaseInCluster(databaseName, clusterSize, leader2.WebUrl, adminCertificate2); // This will register cluster 1's cert as a user cert in cluster 2 AskCluster2ToTrustCluster1(cluster1Cert, cluster2Cert, new Dictionary <string, DatabaseAccess> { [databaseName] = DatabaseAccess.ReadWrite }, SecurityClearance.ValidUser, leader2); // First we'll make sure external replication works between the two clusters using (var store1 = new DocumentStore { Urls = new[] { leader1.WebUrl }, Database = databaseName, Certificate = adminCertificate1 }.Initialize()) using (var store2 = new DocumentStore { Urls = new[] { leader2.WebUrl }, Database = databaseName, Certificate = adminCertificate2 }.Initialize()) { var externalList = await SetupReplicationAsync((DocumentStore)store1, (DocumentStore)store2); using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Karmelush" }, "users/1"); await session.SaveChangesAsync(); } var replicated1 = WaitForDocumentToReplicate <User>(store2, "users/1", 10000); Assert.NotNull(replicated1); Assert.Equal("Karmelush", replicated1.Name); // Let's replace the certificate in cluster 1 (new cert has same private key) and make sure cluster 2 still trusts cluster 1. var cluster1ReplacementCert = new X509Certificate2(cluster1ReplacementCertBytes); var mre = new ManualResetEventSlim(); leader1.ServerCertificateChanged += (sender, args) => mre.Set(); var requestExecutor = store1.GetRequestExecutor(); using (requestExecutor.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { var command = new ReplaceClusterCertificateOperation(cluster1ReplacementCertBytes, false) .GetCommand(store1.Conventions, context); requestExecutor.Execute(command, context); } Assert.True(mre.Wait(Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromMinutes(2)), "Waited too long"); Assert.NotNull(leader1.Certificate.Certificate.Thumbprint); Assert.True(leader1.Certificate.Certificate.Thumbprint.Equals(cluster1ReplacementCert.Thumbprint), "New cert is identical"); // Disable external replication var external = new ExternalReplication(store1.Database, $"ConnectionString-{store2.Identifier}") { TaskId = externalList.First().TaskId, Disabled = true }; var responsibleNode = externalList[0].ResponsibleNode; var clusterNodes1 = Servers.Where(s => s.ServerStore.GetClusterTopology().TryGetNodeTagByUrl(leader1.WebUrl).HasUrl); var node = clusterNodes1.Single(n => n.ServerStore.NodeTag == responsibleNode); var db1 = await node.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(store1.Database); Assert.NotNull(db1); var replicationConnection = db1.ReplicationLoader.OutgoingHandlers.Single(x => x.Destination is ExternalReplication); var res = await store1.Maintenance.SendAsync(new UpdateExternalReplicationOperation(external)); Assert.Equal(externalList.First().TaskId, res.TaskId); // Make sure the command is processed await db1.ServerStore.Cluster.WaitForIndexNotification(res.RaftCommandIndex); var connectionDropped = await WaitForValueAsync(() => replicationConnection.IsConnectionDisposed, true); Assert.True(connectionDropped); // Enable external replication external.Disabled = false; res = await store1.Maintenance.SendAsync(new UpdateExternalReplicationOperation(external)); Assert.Equal(externalList.First().TaskId, res.TaskId); using (var session = store1.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Avivush" }, "users/2"); await session.SaveChangesAsync(); } var replicated2 = WaitForDocumentToReplicate <User>(store2, "users/2", 10000); Assert.NotNull(replicated2); Assert.Equal("Avivush", replicated2.Name); } }