diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py
index 8a3a0956660984a5eb1b5d05cf8fb04759f6870d..6d15951d6411f9861d4358b6043e66342c5c38e7 100644
--- a/src/_zkapauthorizer/lease_maintenance.py
+++ b/src/_zkapauthorizer/lease_maintenance.py
@@ -55,6 +55,8 @@ from allmydata.interfaces import (
 from allmydata.util.hashutil import (
     file_renewal_secret_hash,
     bucket_renewal_secret_hash,
+    file_cancel_secret_hash,
+    bucket_cancel_secret_hash,
 )
 
 from .controller import (
@@ -165,6 +167,7 @@ def renew_leases(
     storage_indexes = yield iter_storage_indexes(visit_assets)
 
     renewal_secret = secret_holder.get_renewal_secret()
+    cancel_secret = secret_holder.get_cancel_secret()
     servers = list(
         server.get_storage_server()
         for server
@@ -176,6 +179,7 @@ def renew_leases(
         yield renew_leases_on_server(
             min_lease_remaining,
             renewal_secret,
+            cancel_secret,
             storage_indexes,
             server,
             activity,
@@ -189,6 +193,7 @@ def renew_leases(
 def renew_leases_on_server(
         min_lease_remaining,
         renewal_secret,
+        cancel_secret,
         storage_indexes,
         server,
         activity,
@@ -202,8 +207,9 @@ def renew_leases_on_server(
     :param timedelta min_lease_remaining: The minimum amount of time remaining
         to allow on a lease without renewing it.
 
-    :param renewal_secret: A seed for the renewal secret hash calculation for
-        any leases which need to be renewed.
+    :param renewal_secret: See ``renew_lease``.
+
+    :param cancel_secret: See ``renew_lease``.
 
     :param list[bytes] storage_indexes: The storage indexes to check.
 
@@ -230,16 +236,19 @@ def renew_leases_on_server(
         # All shares have the same lease information.
         stat = stat_dict.popitem()[1]
         if needs_lease_renew(min_lease_remaining, stat, now):
-            yield renew_lease(renewal_secret, storage_index, server)
+            yield renew_lease(renewal_secret, cancel_secret, storage_index, server)
 
 
-def renew_lease(renewal_secret, storage_index, server):
+def renew_lease(renewal_secret, cancel_secret, storage_index, server):
     """
     Renew the lease on the shares in one storage index on one server.
 
     :param renewal_secret: A seed for the renewal secret hash calculation for
         any leases which need to be renewed.
 
+    :param cancel_secret: A seed for the cancel secret hash calculation for
+        any leases which need to be renewed.
+
     :param bytes storage_index: The storage index to operate on.
 
     :param StorageServer server: The storage server to operate on.
@@ -254,9 +263,19 @@ def renew_lease(renewal_secret, storage_index, server):
         ),
         server.get_lease_seed(),
     )
-    return server.renew_lease(
+    cancel_secret = bucket_cancel_secret_hash(
+        file_cancel_secret_hash(
+            cancel_secret,
+            storage_index,
+        ),
+        server.get_lease_seed(),
+    )
+    # Use add_lease to add a new lease *or* renew an existing one with a
+    # matching renew secret.
+    return server.add_lease(
         storage_index,
         renew_secret,
+        cancel_secret,
     )
 
 
diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py
index 48d690359ebf43e38587f13af728751bb5a4eb5c..654f7eb679205bb97593bdd19fd933e8104c556d 100644
--- a/src/_zkapauthorizer/tests/test_lease_maintenance.py
+++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py
@@ -160,7 +160,7 @@ class DummyStorageServer(object):
     def get_lease_seed(self):
         return self.lease_seed
 
-    def renew_lease(self, storage_index, renew_secret):
+    def add_lease(self, storage_index, renew_secret, cancel_secret):
         self.buckets[storage_index].lease_expiration = (
             self.clock.seconds() + timedelta(days=31).total_seconds()
         )