From 1a8c29e1ea6b20b0d755a141d3b8bfbb7751724e Mon Sep 17 00:00:00 2001
From: Paul Schaub <vanitasvitae@fsfe.org>
Date: Mon, 17 Feb 2025 15:18:58 +0100
Subject: [PATCH] WIP: Port SecretKeyRingEditor over to OpenPGPKeyEditor

---
 .../main/kotlin/org/pgpainless/PGPainless.kt  |   3 +
 .../secretkeyring/SecretKeyRingEditor.kt      | 332 +++++++-----------
 .../SecretKeyRingEditorInterface.kt           | 291 ++++++---------
 .../key/info/UserIdRevocationTest.java        |   2 +-
 .../RefuseToAddWeakSubkeyTest.java            |   4 +-
 .../key/modification/RevokeSubKeyTest.java    |   6 +-
 6 files changed, 242 insertions(+), 396 deletions(-)

diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt
index f9e009cf..84d860e6 100644
--- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt
+++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt
@@ -54,6 +54,9 @@ class PGPainless(
     fun toCertificate(publicKeyRing: PGPPublicKeyRing): OpenPGPCertificate =
         OpenPGPCertificate(publicKeyRing, implementation)
 
+    fun modifyKey(key: OpenPGPKey, referenceTime: Date = Date()): SecretKeyRingEditor =
+        SecretKeyRingEditor(key, referenceTime)
+
     companion object {
 
         @Volatile private var instance: PGPainless? = null
diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt
index 2af3724a..6676a6d2 100644
--- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt
+++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt
@@ -12,10 +12,13 @@ import openpgp.openPgpKeyId
 import org.bouncycastle.bcpg.KeyIdentifier
 import org.bouncycastle.bcpg.sig.KeyExpirationTime
 import org.bouncycastle.openpgp.*
+import org.bouncycastle.openpgp.api.OpenPGPKey
+import org.bouncycastle.openpgp.api.OpenPGPKeyEditor
+import org.bouncycastle.openpgp.api.SignatureParameters
+import org.bouncycastle.openpgp.api.SignatureParameters.Callback
+import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction
 import org.pgpainless.PGPainless
 import org.pgpainless.PGPainless.Companion.inspectKeyRing
-import org.pgpainless.algorithm.AlgorithmSuite
-import org.pgpainless.algorithm.Feature
 import org.pgpainless.algorithm.KeyFlag
 import org.pgpainless.algorithm.OpenPGPKeyVersion
 import org.pgpainless.algorithm.SignatureType
@@ -23,8 +26,8 @@ import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator
 import org.pgpainless.bouncycastle.extensions.getKeyExpirationDate
 import org.pgpainless.bouncycastle.extensions.publicKeyAlgorithm
 import org.pgpainless.bouncycastle.extensions.requirePublicKey
+import org.pgpainless.bouncycastle.extensions.toOpenPGPKey
 import org.pgpainless.implementation.ImplementationFactory
-import org.pgpainless.key.OpenPgpFingerprint
 import org.pgpainless.key.generation.KeyRingBuilder
 import org.pgpainless.key.generation.KeySpec
 import org.pgpainless.key.protection.*
@@ -38,66 +41,38 @@ import org.pgpainless.signature.subpackets.*
 import org.pgpainless.util.Passphrase
 import org.pgpainless.util.selection.userid.SelectUserId
 
-class SecretKeyRingEditor(
-    var secretKeyRing: PGPSecretKeyRing,
-    override val referenceTime: Date = Date()
+class SecretKeyRingEditor(key: OpenPGPKey,
+                          keyProtector: SecretKeyRingProtector,
+                          override val referenceTime: Date = Date()
 ) : SecretKeyRingEditorInterface {
 
+    private var editor: OpenPGPKeyEditor = OpenPGPKeyEditor(key, keyProtector, PGPainless.getInstance().implementation)
+
+    constructor(
+        secretKey: PGPSecretKeyRing,
+        referenceTime: Date = Date(),
+        keyProtector: SecretKeyRingProtector
+    ) : this(secretKey.toOpenPGPKey(), keyProtector, referenceTime)
+
     override fun addUserId(
         userId: CharSequence,
-        callback: SelfSignatureSubpackets.Callback?,
-        protector: SecretKeyRingProtector
+        callback: SelfSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
         val sanitizedUserId = sanitizeUserId(userId).toString()
-        val primaryKey = secretKeyRing.secretKey
-
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
-        require(!info.isHardRevoked(userId)) {
-            "User-ID $userId is hard revoked and cannot be re-certified."
-        }
-
-        val (
-            hashAlgorithmPreferences,
-            symmetricKeyAlgorithmPreferences,
-            compressionAlgorithmPreferences) =
-            try {
-                Triple(
-                    info.preferredHashAlgorithms,
-                    info.preferredSymmetricKeyAlgorithms,
-                    info.preferredCompressionAlgorithms)
-            } catch (e: IllegalStateException) { // missing user-id sig
-                val algorithmSuite = AlgorithmSuite.defaultAlgorithmSuite
-                Triple(
-                    algorithmSuite.hashAlgorithms,
-                    algorithmSuite.symmetricKeyAlgorithms,
-                    algorithmSuite.compressionAlgorithms)
+        editor.addUserId(sanitizedUserId, object : Callback {
+            override fun apply(parameters: SignatureParameters): SignatureParameters {
+                return parameters.setSignatureCreationTime(referenceTime)
             }
-
-        val builder =
-            SelfSignatureBuilder(primaryKey, protector).apply {
-                hashedSubpackets.setSignatureCreationTime(referenceTime)
-                setSignatureType(SignatureType.POSITIVE_CERTIFICATION)
-            }
-        builder.hashedSubpackets.apply {
-            setKeyFlags(info.getKeyFlagsOf(primaryKey.keyID))
-            setPreferredHashAlgorithms(hashAlgorithmPreferences)
-            setPreferredSymmetricKeyAlgorithms(symmetricKeyAlgorithmPreferences)
-            setPreferredCompressionAlgorithms(compressionAlgorithmPreferences)
-            setFeatures(Feature.MODIFICATION_DETECTION)
-        }
-        builder.applyCallback(callback)
-        secretKeyRing =
-            injectCertification(secretKeyRing, sanitizedUserId, builder.build(sanitizedUserId))
+        })
         return this
     }
 
     override fun addPrimaryUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector
     ): SecretKeyRingEditorInterface {
         val uid = sanitizeUserId(userId)
-        val primaryKey = secretKeyRing.publicKey
-        var info = inspectKeyRing(secretKeyRing, referenceTime)
+        val primaryKey = key.publicKey
+        var info = inspectKeyRing(key, referenceTime)
         val primaryUserId = info.primaryUserId
         val signature =
             if (primaryUserId == null) info.latestDirectKeySelfSignature
@@ -116,18 +91,17 @@ class SecretKeyRingEditor(
                         else setKeyExpirationTime(null)
                     }
                 }
-            },
-            protector)
+            })
 
         // unmark previous primary user-ids to be non-primary
-        info = inspectKeyRing(secretKeyRing, referenceTime)
+        info = inspectKeyRing(key, referenceTime)
         info.validAndExpiredUserIds
             .filterNot { it == uid }
             .forEach { otherUserId ->
                 if (info
-                    .getLatestUserIdCertification(otherUserId)!!
-                    .hashedSubPackets
-                    .isPrimaryUserID) {
+                        .getLatestUserIdCertification(otherUserId)!!
+                        .hashedSubPackets
+                        .isPrimaryUserID) {
                     // We need to unmark this user-id as primary
                     addUserId(
                         otherUserId,
@@ -140,34 +114,16 @@ class SecretKeyRingEditor(
                                     setKeyExpirationTime(null) // non-primary
                                 }
                             }
-                        },
-                        protector)
+                        })
                 }
             }
         return this
     }
 
-    @Deprecated(
-        "Use of SelectUserId class is deprecated.",
-        replaceWith = ReplaceWith("removeUserId(protector, predicate)"))
     override fun removeUserId(
-        selector: SelectUserId,
-        protector: SecretKeyRingProtector
-    ): SecretKeyRingEditorInterface {
-        return revokeUserIds(
-            selector,
-            protector,
-            RevocationAttributes.createCertificateRevocation()
-                .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID)
-                .withoutDescription())
-    }
-
-    override fun removeUserId(
-        protector: SecretKeyRingProtector,
         predicate: (String) -> Boolean
     ): SecretKeyRingEditorInterface {
         return revokeUserIds(
-            protector,
             RevocationAttributes.createCertificateRevocation()
                 .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID)
                 .withoutDescription(),
@@ -175,23 +131,21 @@ class SecretKeyRingEditor(
     }
 
     override fun removeUserId(
-        userId: CharSequence,
-        protector: SecretKeyRingProtector
+        userId: CharSequence
     ): SecretKeyRingEditorInterface {
-        return removeUserId(protector) { uid -> userId == uid }
+        return removeUserId { uid -> userId == uid }
     }
 
     override fun replaceUserId(
         oldUserId: CharSequence,
-        newUserId: CharSequence,
-        protector: SecretKeyRingProtector
+        newUserId: CharSequence
     ): SecretKeyRingEditorInterface {
         val oldUID = sanitizeUserId(oldUserId)
         val newUID = sanitizeUserId(newUserId)
         require(oldUID.isNotBlank()) { "Old user-ID cannot be empty." }
         require(newUID.isNotBlank()) { "New user-ID cannot be empty." }
 
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
+        val info = inspectKeyRing(key, referenceTime)
         if (!info.isUserIdValid(oldUID)) {
             throw NoSuchElementException(
                 "Key does not carry user-ID '$oldUID', or it is not valid.")
@@ -218,16 +172,14 @@ class SecretKeyRingEditor(
                         oldCertification.unhashedSubPackets,
                         unhashedSubpackets as SignatureSubpackets)
                 }
-            },
-            protector)
+            })
 
-        return revokeUserId(oldUID, protector)
+        return revokeUserId(oldUID)
     }
 
-    override fun addSubKey(
+    override fun addSubkey(
         keySpec: KeySpec,
-        subkeyPassphrase: Passphrase,
-        protector: SecretKeyRingProtector
+        subkeyPassphrase: Passphrase
     ): SecretKeyRingEditorInterface {
         val callback =
             object : SelfSignatureSubpackets.Callback {
@@ -237,34 +189,31 @@ class SecretKeyRingEditor(
                     hashedSubpackets.setSignatureCreationTime(referenceTime)
                 }
             }
-        return addSubKey(keySpec, subkeyPassphrase, callback, protector)
+        return addSubkey(keySpec, subkeyPassphrase, callback)
     }
 
-    override fun addSubKey(
+    override fun addSubkey(
         keySpec: KeySpec,
         subkeyPassphrase: Passphrase,
-        callback: SelfSignatureSubpackets.Callback?,
-        protector: SecretKeyRingProtector
+        callback: SelfSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
-        val version = OpenPGPKeyVersion.from(secretKeyRing.getPublicKey().version)
+        val version = OpenPGPKeyVersion.from(key.getPublicKey().version)
         val keyPair = KeyRingBuilder.generateKeyPair(keySpec, OpenPGPKeyVersion.v4, referenceTime)
         val subkeyProtector =
             PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyIdentifier, subkeyPassphrase)
         val keyFlags = KeyFlag.fromBitmask(keySpec.subpackets.keyFlags).toMutableList()
-        return addSubKey(
+        return addSubkey(
             keyPair,
             callback,
             subkeyProtector,
-            protector,
             keyFlags.removeFirst(),
             *keyFlags.toTypedArray())
     }
 
-    override fun addSubKey(
+    override fun addSubkey(
         subkey: PGPKeyPair,
         callback: SelfSignatureSubpackets.Callback?,
         subkeyProtector: SecretKeyRingProtector,
-        primaryKeyProtector: SecretKeyRingProtector,
         keyFlag: KeyFlag,
         vararg keyFlags: KeyFlag
     ): SecretKeyRingEditorInterface {
@@ -277,11 +226,11 @@ class SecretKeyRingEditor(
             PGPainless.getPolicy()
                 .publicKeyAlgorithmPolicy
                 .isAcceptable(subkeyAlgorithm, bitStrength)) {
-                "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable."
-            }
+            "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable."
+        }
 
-        val primaryKey = secretKeyRing.secretKey
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
+        val primaryKey = key.secretKey
+        val info = inspectKeyRing(key, referenceTime)
         val hashAlgorithm =
             HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy())
                 .negotiateHashAlgorithm(info.preferredHashAlgorithms)
@@ -309,61 +258,60 @@ class SecretKeyRingEditor(
         secretSubkey =
             KeyRingUtils.secretKeyPlusSignature(
                 secretSubkey, skBindingBuilder.build(secretSubkey.publicKey))
-        secretKeyRing = KeyRingUtils.keysPlusSecretKey(secretKeyRing, secretSubkey)
+        key = KeyRingUtils.keysPlusSecretKey(key, secretSubkey)
         return this
     }
 
     override fun revoke(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
     ): SecretKeyRingEditorInterface {
-        return revoke(protector, callbackFromRevocationAttributes(revocationAttributes))
+        return revoke(callbackFromRevocationAttributes(revocationAttributes))
     }
 
     override fun revoke(
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
-        return revokeSubKey(secretKeyRing.secretKey.keyID, protector, callback)
+        editor.revokeKey(object : SignatureParameters.Callback {
+            override fun apply(parameters: SignatureParameters?): SignatureParameters {
+                if (callback != null) {
+                    callback.modifyHashedSubpackets()
+                }
+            }
+        })
     }
 
-    override fun revokeSubKey(
-        subkeyId: Long,
-        protector: SecretKeyRingProtector,
+    override fun revokeSubkey(
+        subkeyIdentifier: KeyIdentifier,
         revocationAttributes: RevocationAttributes?
     ): SecretKeyRingEditorInterface {
-        return revokeSubKey(
-            subkeyId, protector, callbackFromRevocationAttributes(revocationAttributes))
+        return revokeSubkey(subkeyIdentifier, callbackFromRevocationAttributes(revocationAttributes))
     }
 
-    override fun revokeSubKey(
+    override fun revokeSubkey(
         subkeyId: Long,
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
-        val revokeeSubKey = secretKeyRing.requirePublicKey(subkeyId)
-        val subkeyRevocation = generateRevocation(protector, revokeeSubKey, callback)
-        secretKeyRing = injectCertification(secretKeyRing, revokeeSubKey, subkeyRevocation)
+        val revokeeSubKey = key.requirePublicKey(subkeyId)
+        val subkeyRevocation = generateRevocation(revokeeSubKey, callback)
+        key = injectCertification(key, revokeeSubKey, subkeyRevocation)
         return this
     }
 
     override fun revokeUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
     ): SecretKeyRingEditorInterface {
         if (revocationAttributes != null) {
             require(
                 revocationAttributes.reason == RevocationAttributes.Reason.NO_REASON ||
                     revocationAttributes.reason ==
-                        RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) {
-                    "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID"
-                }
+                    RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) {
+                "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID"
+            }
         }
 
         return revokeUserId(
             userId,
-            protector,
             object : RevocationSignatureSubpackets.Callback {
                 override fun modifyHashedSubpackets(
                     hashedSubpackets: RevocationSignatureSubpackets
@@ -377,19 +325,16 @@ class SecretKeyRingEditor(
 
     override fun revokeUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
-        return revokeUserIds(protector, callback, SelectUserId.exactMatch(sanitizeUserId(userId)))
+        return revokeUserIds(callback, SelectUserId.exactMatch(sanitizeUserId(userId)))
     }
 
     override fun revokeUserIds(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?,
         predicate: (String) -> Boolean
     ): SecretKeyRingEditorInterface {
         return revokeUserIds(
-            protector,
             object : RevocationSignatureSubpackets.Callback {
                 override fun modifyHashedSubpackets(
                     hashedSubpackets: RevocationSignatureSubpackets
@@ -402,7 +347,6 @@ class SecretKeyRingEditor(
     }
 
     override fun revokeUserIds(
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?,
         predicate: (String) -> Boolean
     ): SecretKeyRingEditorInterface {
@@ -411,38 +355,36 @@ class SecretKeyRingEditor(
                 if (it.isEmpty())
                     throw NoSuchElementException("No matching user-ids found on the key.")
             }
-            .forEach { userId -> doRevokeUserId(userId, protector, callback) }
+            .forEach { userId -> doRevokeUserId(userId, callback) }
         return this
     }
 
     override fun setExpirationDate(
-        expiration: Date?,
-        protector: SecretKeyRingProtector
+        expiration: Date?
     ): SecretKeyRingEditorInterface {
-        require(secretKeyRing.secretKey.isMasterKey) {
+        require(key.secretKey.isMasterKey) {
             "OpenPGP key does not appear to contain a primary secret key."
         }
 
         val prevDirectKeySig = getPreviousDirectKeySignature()
         // reissue direct key sig
         if (prevDirectKeySig != null) {
-            secretKeyRing =
+            key =
                 injectCertification(
-                    secretKeyRing,
-                    secretKeyRing.publicKey,
-                    reissueDirectKeySignature(expiration, protector, prevDirectKeySig))
+                    key,
+                    key.publicKey,
+                    reissueDirectKeySignature(expiration, prevDirectKeySig))
         }
 
-        val primaryUserId =
-            inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId()
+        val primaryUserId = inspectKeyRing(key, referenceTime).getPossiblyExpiredPrimaryUserId()
         if (primaryUserId != null) {
             val prevUserIdSig = getPreviousUserIdSignatures(primaryUserId)
             val userIdSig =
-                reissuePrimaryUserIdSig(expiration, protector, primaryUserId, prevUserIdSig!!)
-            secretKeyRing = injectCertification(secretKeyRing, primaryUserId, userIdSig)
+                reissuePrimaryUserIdSig(expiration, primaryUserId, prevUserIdSig!!)
+            key = injectCertification(key, primaryUserId, userIdSig)
         }
 
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
+        val info = inspectKeyRing(key, referenceTime)
         for (userId in info.validUserIds) {
             if (userId == primaryUserId) {
                 continue
@@ -453,11 +395,11 @@ class SecretKeyRingEditor(
                     ?: throw AssertionError(
                         "A valid user-id shall never have no user-id signature.")
             if (prevUserIdSig.hashedSubPackets.isPrimaryUserID) {
-                secretKeyRing =
+                key =
                     injectCertification(
-                        secretKeyRing,
+                        key,
                         primaryUserId!!,
-                        reissueNonPrimaryUserId(protector, userId, prevUserIdSig))
+                        reissueNonPrimaryUserId(userId, prevUserIdSig))
             }
         }
 
@@ -466,28 +408,26 @@ class SecretKeyRingEditor(
 
     override fun setExpirationDateOfSubkey(
         expiration: Date?,
-        keyId: Long,
-        protector: SecretKeyRingProtector
+        keyIdentifier: KeyIdentifier
     ): SecretKeyRingEditorInterface = apply {
         // is primary key
-        if (keyId == secretKeyRing.publicKey.keyID) {
-            return setExpirationDate(expiration, protector)
+        if (keyId == key.publicKey.keyID) {
+            return setExpirationDate(expiration)
         }
 
         // is subkey
         val subkey =
-            secretKeyRing.getPublicKey(keyId)
+            key.getPublicKey(keyId)
                 ?: throw NoSuchElementException("No subkey with ID ${keyId.openPgpKeyId()} found.")
         val prevBinding =
-            inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
+            inspectKeyRing(key).getCurrentSubkeyBindingSignature(keyId)
                 ?: throw NoSuchElementException(
                     "Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.")
-        val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
-        secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
+        val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, prevBinding)
+        key = injectCertification(key, subkey, bindingSig)
     }
 
     override fun createMinimalRevocationCertificate(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
     ): PGPPublicKeyRing {
         // Check reason
@@ -497,51 +437,34 @@ class SecretKeyRingEditor(
             }
         }
 
-        val revocation = createRevocation(protector, revocationAttributes)
-        var primaryKey = secretKeyRing.secretKey.publicKey
+        val revocation = createRevocation(revocationAttributes)
+        var primaryKey = key.secretKey.publicKey
         primaryKey = KeyRingUtils.getStrippedDownPublicKey(primaryKey)
         primaryKey = PGPPublicKey.addCertification(primaryKey, revocation)
         return PGPPublicKeyRing(listOf(primaryKey))
     }
 
     override fun createRevocation(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
     ): PGPSignature {
         return generateRevocation(
-            protector,
-            secretKeyRing.publicKey,
+            key.publicKey, callbackFromRevocationAttributes(revocationAttributes))
+    }
+
+    override fun createRevocation(
+        subkeyIdentifier: KeyIdentifier,
+        revocationAttributes: RevocationAttributes?
+    ): PGPSignature {
+        return generateRevocation(
+            key.requirePublicKey(subkeyId),
             callbackFromRevocationAttributes(revocationAttributes))
     }
 
     override fun createRevocation(
-        subkeyId: Long,
-        protector: SecretKeyRingProtector,
-        revocationAttributes: RevocationAttributes?
-    ): PGPSignature {
-        return generateRevocation(
-            protector,
-            secretKeyRing.requirePublicKey(subkeyId),
-            callbackFromRevocationAttributes(revocationAttributes))
-    }
-
-    override fun createRevocation(
-        subkeyId: Long,
-        protector: SecretKeyRingProtector,
+        subkeyIdentifier: KeyIdentifier,
         callback: RevocationSignatureSubpackets.Callback?
     ): PGPSignature {
-        return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyId), callback)
-    }
-
-    override fun createRevocation(
-        subkeyFingerprint: OpenPgpFingerprint,
-        protector: SecretKeyRingProtector,
-        revocationAttributes: RevocationAttributes?
-    ): PGPSignature {
-        return generateRevocation(
-            protector,
-            secretKeyRing.requirePublicKey(subkeyFingerprint),
-            callbackFromRevocationAttributes(revocationAttributes))
+        return generateRevocation(key.requirePublicKey(subkeyId), callback)
     }
 
     override fun changePassphraseFromOldPassphrase(
@@ -567,12 +490,12 @@ class SecretKeyRingEditor(
                 mapOf(keyIdentifier to oldPassphrase), oldProtectionSettings, null))
     }
 
-    override fun done(): PGPSecretKeyRing {
-        return secretKeyRing
+    override fun done(): OpenPGPKey {
+        return editor.done()
     }
 
     private fun sanitizeUserId(userId: CharSequence): CharSequence =
-        // TODO: Further research how to sanitize user IDs.
+    // TODO: Further research how to sanitize user IDs.
         //  e.g. what about newlines?
         userId.toString().trim()
 
@@ -586,45 +509,39 @@ class SecretKeyRingEditor(
         }
 
     private fun generateRevocation(
-        protector: SecretKeyRingProtector,
         revokeeSubkey: PGPPublicKey,
         callback: RevocationSignatureSubpackets.Callback?
     ): PGPSignature {
-        val primaryKey = secretKeyRing.secretKey
+        val primaryKey = key.secretKey
         val signatureType =
             if (revokeeSubkey.isMasterKey) SignatureType.KEY_REVOCATION
             else SignatureType.SUBKEY_REVOCATION
 
-        return RevocationSignatureBuilder(signatureType, primaryKey, protector)
+        return RevocationSignatureBuilder(signatureType, primaryKey)
             .apply { applyCallback(callback) }
             .build(revokeeSubkey)
     }
 
     private fun doRevokeUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface {
-        RevocationSignatureBuilder(
-                SignatureType.CERTIFICATION_REVOCATION, secretKeyRing.secretKey, protector)
+        RevocationSignatureBuilder(SignatureType.CERTIFICATION_REVOCATION, key.secretKey)
             .apply {
                 hashedSubpackets.setSignatureCreationTime(referenceTime)
                 applyCallback(callback)
             }
-            .let {
-                secretKeyRing =
-                    injectCertification(secretKeyRing, userId, it.build(userId.toString()))
-            }
+            .let { key = injectCertification(key, userId, it.build(userId.toString())) }
         return this
     }
 
     private fun getPreviousDirectKeySignature(): PGPSignature? {
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
+        val info = inspectKeyRing(key, referenceTime)
         return info.latestDirectKeySelfSignature
     }
 
     private fun getPreviousUserIdSignatures(userId: String): PGPSignature? {
-        val info = inspectKeyRing(secretKeyRing, referenceTime)
+        val info = inspectKeyRing(key, referenceTime)
         return info.getLatestUserIdCertification(userId)
     }
 
@@ -634,8 +551,7 @@ class SecretKeyRingEditor(
         userId: String,
         prevUserIdSig: PGPSignature
     ): PGPSignature {
-        val builder =
-            SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig)
+        val builder = SelfSignatureBuilder(key.secretKey, secretKeyRingProtector, prevUserIdSig)
         builder.hashedSubpackets.setSignatureCreationTime(referenceTime)
         builder.applyCallback(
             object : SelfSignatureSubpackets.Callback {
@@ -654,7 +570,7 @@ class SecretKeyRingEditor(
         @Nonnull primaryUserId: String,
         @Nonnull prevUserIdSig: PGPSignature
     ): PGPSignature {
-        return SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig)
+        return SelfSignatureBuilder(key.secretKey, secretKeyRingProtector, prevUserIdSig)
             .apply {
                 hashedSubpackets.setSignatureCreationTime(referenceTime)
                 applyCallback(
@@ -664,7 +580,7 @@ class SecretKeyRingEditor(
                         ) {
                             if (expiration != null) {
                                 hashedSubpackets.setKeyExpirationTime(
-                                    true, secretKeyRing.publicKey.creationTime, expiration)
+                                    true, key.publicKey.creationTime, expiration)
                             } else {
                                 hashedSubpackets.setKeyExpirationTime(KeyExpirationTime(true, 0))
                             }
@@ -682,7 +598,7 @@ class SecretKeyRingEditor(
         prevDirectKeySig: PGPSignature
     ): PGPSignature {
         return DirectKeySelfSignatureBuilder(
-                secretKeyRing.secretKey, secretKeyRingProtector, prevDirectKeySig)
+            key.secretKey, secretKeyRingProtector, prevDirectKeySig)
             .apply {
                 hashedSubpackets.setSignatureCreationTime(referenceTime)
                 applyCallback(
@@ -692,7 +608,7 @@ class SecretKeyRingEditor(
                         ) {
                             if (expiration != null) {
                                 hashedSubpackets.setKeyExpirationTime(
-                                    secretKeyRing.publicKey.creationTime, expiration)
+                                    key.publicKey.creationTime, expiration)
                             } else {
                                 hashedSubpackets.setKeyExpirationTime(null)
                             }
@@ -708,9 +624,9 @@ class SecretKeyRingEditor(
         protector: SecretKeyRingProtector,
         prevSubkeyBindingSignature: PGPSignature
     ): PGPSignature {
-        val primaryKey = secretKeyRing.publicKey
-        val secretPrimaryKey = secretKeyRing.secretKey
-        val secretSubkey: PGPSecretKey? = secretKeyRing.getSecretKey(subkey.keyID)
+        val primaryKey = key.publicKey
+        val secretPrimaryKey = key.secretKey
+        val secretSubkey: PGPSecretKey? = key.getSecretKey(subkey.keyID)
 
         val builder =
             SubkeyBindingSignatureBuilder(secretPrimaryKey, protector, prevSubkeyBindingSignature)
@@ -742,7 +658,7 @@ class SecretKeyRingEditor(
     }
 
     private fun selectUserIds(predicate: Predicate<String>): List<String> =
-        inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) }
+        inspectKeyRing(key).validUserIds.filter { predicate.test(it) }
 
     private class WithKeyRingEncryptionSettingsImpl(
         private val editor: SecretKeyRingEditor,
@@ -772,15 +688,15 @@ class SecretKeyRingEditor(
             val protector =
                 PasswordBasedSecretKeyRingProtector(
                     newProtectionSettings, SolitaryPassphraseProvider(passphrase))
-            val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector)
-            editor.secretKeyRing = secretKeys
+            val secretKeys = changePassphrase(keyId, editor.key, oldProtector, protector)
+            editor.key = secretKeys
             return editor
         }
 
         override fun toNoPassphrase(): SecretKeyRingEditorInterface {
             val protector = UnprotectedKeysProtector()
-            val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector)
-            editor.secretKeyRing = secretKeys
+            val secretKeys = changePassphrase(keyId, editor.key, oldProtector, protector)
+            editor.key = secretKeys
             return editor
         }
     }
diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt
index ad8e36ff..74e60a36 100644
--- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt
+++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt
@@ -10,6 +10,7 @@ import java.security.NoSuchAlgorithmException
 import java.util.*
 import org.bouncycastle.bcpg.KeyIdentifier
 import org.bouncycastle.openpgp.*
+import org.bouncycastle.openpgp.api.OpenPGPKey
 import org.pgpainless.algorithm.KeyFlag
 import org.pgpainless.key.OpenPgpFingerprint
 import org.pgpainless.key.generation.KeySpec
@@ -19,7 +20,6 @@ import org.pgpainless.key.util.RevocationAttributes
 import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets
 import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
 import org.pgpainless.util.Passphrase
-import org.pgpainless.util.selection.userid.SelectUserId
 
 interface SecretKeyRingEditorInterface {
 
@@ -33,28 +33,23 @@ interface SecretKeyRingEditorInterface {
      * Add a user-id to the key ring.
      *
      * @param userId user-id
-     * @param protector protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a signature for the user-id
      */
-    @Throws(PGPException::class)
-    fun addUserId(userId: CharSequence, protector: SecretKeyRingProtector) =
-        addUserId(userId, null, protector)
+    @Throws(PGPException::class) fun addUserId(userId: CharSequence) = addUserId(userId, null)
 
     /**
      * Add a user-id to the key ring.
      *
      * @param userId user-id
      * @param callback callback to modify the self-signature subpackets
-     * @param protector protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a signature for the user-id
      */
     @Throws(PGPException::class)
     fun addUserId(
         userId: CharSequence,
-        callback: SelfSignatureSubpackets.Callback? = null,
-        protector: SecretKeyRingProtector
+        callback: SelfSignatureSubpackets.Callback? = null
     ): SecretKeyRingEditorInterface
 
     /**
@@ -62,48 +57,23 @@ interface SecretKeyRingEditorInterface {
      * new certification signature will be created.
      *
      * @param userId user id
-     * @param protector protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a signature for the user-id
      */
     @Throws(PGPException::class)
-    fun addPrimaryUserId(
-        userId: CharSequence,
-        protector: SecretKeyRingProtector
-    ): SecretKeyRingEditorInterface
+    fun addPrimaryUserId(userId: CharSequence): SecretKeyRingEditorInterface
 
     /**
      * Convenience method to revoke selected user-ids using soft revocation signatures. The
      * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the
      * user-id can be re-certified at a later point.
      *
-     * @param selector selector to select user-ids
-     * @param protector protector to unlock the primary key
-     * @return the builder
-     * @throws PGPException in case we cannot generate a revocation signature for the user-id
-     */
-    @Deprecated(
-        "Use of SelectUserId class is deprecated.",
-        ReplaceWith("removeUserId(protector, predicate)"))
-    @Throws(PGPException::class)
-    fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector) =
-        removeUserId(protector, selector)
-
-    /**
-     * Convenience method to revoke selected user-ids using soft revocation signatures. The
-     * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the
-     * user-id can be re-certified at a later point.
-     *
-     * @param protector protector to unlock the primary key
      * @param predicate predicate to select user-ids for revocation
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the user-id
      */
     @Throws(PGPException::class)
-    fun removeUserId(
-        protector: SecretKeyRingProtector,
-        predicate: (String) -> Boolean
-    ): SecretKeyRingEditorInterface
+    fun removeUserId(predicate: (String) -> Boolean): SecretKeyRingEditorInterface
 
     /**
      * Convenience method to revoke a single user-id using a soft revocation signature. The
@@ -111,15 +81,11 @@ interface SecretKeyRingEditorInterface {
      * can be re-certified at a later point.
      *
      * @param userId user-id to revoke
-     * @param protector protector to unlock the primary key
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the user-id
      */
     @Throws(PGPException::class)
-    fun removeUserId(
-        userId: CharSequence,
-        protector: SecretKeyRingProtector
-    ): SecretKeyRingEditorInterface
+    fun removeUserId(userId: CharSequence): SecretKeyRingEditorInterface
 
     /**
      * Replace a user-id on the key with a new one. The old user-id gets soft revoked and the new
@@ -130,7 +96,6 @@ interface SecretKeyRingEditorInterface {
      *
      * @param oldUserId old user-id
      * @param newUserId new user-id
-     * @param protector protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation and certification signature
      * @throws java.util.NoSuchElementException if the old user-id was not found on the key; or if
@@ -139,8 +104,7 @@ interface SecretKeyRingEditorInterface {
     @Throws(PGPException::class)
     fun replaceUserId(
         oldUserId: CharSequence,
-        newUserId: CharSequence,
-        protector: SecretKeyRingProtector
+        newUserId: CharSequence
     ): SecretKeyRingEditorInterface
 
     /**
@@ -149,7 +113,6 @@ interface SecretKeyRingEditorInterface {
      * @param keySpec key specification
      * @param subkeyPassphrase passphrase to encrypt the sub key
      * @param callback callback to modify the subpackets of the subkey binding signature
-     * @param protector protector to unlock the secret key of the key ring
      * @return the builder
      * @throws InvalidAlgorithmParameterException in case the user wants to use invalid parameters
      *   for the key
@@ -162,11 +125,10 @@ interface SecretKeyRingEditorInterface {
         IOException::class,
         InvalidAlgorithmParameterException::class,
         NoSuchAlgorithmException::class)
-    fun addSubKey(
+    fun addSubkey(
         keySpec: KeySpec,
         subkeyPassphrase: Passphrase,
-        callback: SelfSignatureSubpackets.Callback? = null,
-        protector: SecretKeyRingProtector
+        callback: SelfSignatureSubpackets.Callback? = null
     ): SecretKeyRingEditorInterface
 
     /**
@@ -175,7 +137,6 @@ interface SecretKeyRingEditorInterface {
      * @param subkey subkey key pair
      * @param callback callback to modify the subpackets of the subkey binding signature
      * @param subkeyProtector protector to unlock and encrypt the subkey
-     * @param primaryKeyProtector protector to unlock the primary key
      * @param keyFlag first mandatory key flag for the subkey
      * @param keyFlags optional additional key flags
      * @return builder
@@ -183,11 +144,10 @@ interface SecretKeyRingEditorInterface {
      * @throws IOException in case of an IO error
      */
     @Throws(PGPException::class, IOException::class)
-    fun addSubKey(
+    fun addSubkey(
         subkey: PGPKeyPair,
         callback: SelfSignatureSubpackets.Callback?,
         subkeyProtector: SecretKeyRingProtector,
-        primaryKeyProtector: SecretKeyRingProtector,
         keyFlag: KeyFlag,
         vararg keyFlags: KeyFlag
     ): SecretKeyRingEditorInterface
@@ -195,42 +155,60 @@ interface SecretKeyRingEditorInterface {
     /**
      * Revoke the key ring using a hard revocation.
      *
-     * @param protector protector of the primary key
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature
      */
-    @Throws(PGPException::class)
-    fun revoke(protector: SecretKeyRingProtector) = revoke(protector, null as RevocationAttributes?)
+    @Throws(PGPException::class) fun revoke() = revoke(null as RevocationAttributes?)
 
     /**
      * Revoke the key ring using the provided revocation attributes. The attributes define, whether
      * the revocation was a hard revocation or not.
      *
-     * @param protector protector of the primary key
      * @param revocationAttributes reason for the revocation
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature
      */
     @Throws(PGPException::class)
-    fun revoke(
-        protector: SecretKeyRingProtector,
-        revocationAttributes: RevocationAttributes? = null
-    ): SecretKeyRingEditorInterface
+    fun revoke(revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface
 
     /**
      * Revoke the key ring. You can use the [RevocationSignatureSubpackets.Callback] to modify the
      * revocation signatures subpackets, e.g. in order to define whether this is a hard or soft
      * revocation.
      *
-     * @param protector protector to unlock the primary secret key
      * @param callback callback to modify the revocations subpackets
      * @return builder
      * @throws PGPException in case we cannot generate a revocation signature
      */
     @Throws(PGPException::class)
-    fun revoke(
-        protector: SecretKeyRingProtector,
-        callback: RevocationSignatureSubpackets.Callback?
+    fun revoke(callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface
+
+    fun revokeSubkey(subkeyIdentifier: KeyIdentifier) = revokeSubkey(subkeyIdentifier, null)
+
+    /**
+     * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint
+     * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
+     *
+     * @param fingerprint fingerprint of the subkey to be revoked
+     * @return the builder
+     * @throws PGPException in case we cannot generate a revocation signature for the subkey
+     */
+    @Throws(PGPException::class)
+    @Deprecated("Pass in the subkeys KeyIdentifier instead.")
+    fun revokeSubkey(fingerprint: OpenPgpFingerprint) = revokeSubkey(fingerprint.keyIdentifier)
+
+    /**
+     * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint
+     * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
+     *
+     * @param subkeyIdentifier identifier of the subkey to be revoked
+     * @return the builder
+     * @throws PGPException in case we cannot generate a revocation signature for the subkey
+     */
+    @Throws(PGPException::class)
+    fun revokeSubkey(
+        subkeyIdentifier: KeyIdentifier,
+        revocationAttributes: RevocationAttributes? = null
     ): SecretKeyRingEditorInterface
 
     /**
@@ -238,61 +216,44 @@ interface SecretKeyRingEditorInterface {
      * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
      *
      * @param fingerprint fingerprint of the subkey to be revoked
-     * @param protector protector to unlock the primary key
-     * @return the builder
-     * @throws PGPException in case we cannot generate a revocation signature for the subkey
-     */
-    @Throws(PGPException::class)
-    fun revokeSubKey(fingerprint: OpenPgpFingerprint, protector: SecretKeyRingProtector) =
-        revokeSubKey(fingerprint, protector, null)
-
-    /**
-     * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint
-     * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
-     *
-     * @param fingerprint fingerprint of the subkey to be revoked
-     * @param protector protector to unlock the primary key
      * @param revocationAttributes reason for the revocation
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the subkey
      */
     @Throws(PGPException::class)
-    fun revokeSubKey(
+    @Deprecated("Pass in the subkeys KeyIdentifier instead.")
+    fun revokeSubkey(
         fingerprint: OpenPgpFingerprint,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes? = null
-    ): SecretKeyRingEditorInterface =
-        revokeSubKey(fingerprint.keyId, protector, revocationAttributes)
+    ): SecretKeyRingEditorInterface = revokeSubkey(fingerprint.keyIdentifier, revocationAttributes)
 
     /**
      * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
      * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
      *
      * @param subkeyId id of the subkey
-     * @param protector protector to unlock the primary key
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the subkey
      */
     @Throws(PGPException::class)
-    fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector) =
-        revokeSubKey(subkeyId, protector, null as RevocationAttributes?)
+    @Deprecated("Pass in the keys KeyIdentifier instead.")
+    fun revokeSubkey(subkeyId: Long) =
+        revokeSubkey(KeyIdentifier(subkeyId), null as RevocationAttributes?)
 
     /**
      * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
      * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown.
      *
      * @param subkeyId id of the subkey
-     * @param protector protector to unlock the primary key
      * @param revocationAttributes reason for the revocation
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the subkey
      */
     @Throws(PGPException::class)
-    fun revokeSubKey(
+    fun revokeSubkey(
         subkeyId: Long,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes? = null
-    ): SecretKeyRingEditorInterface
+    ): SecretKeyRingEditorInterface = revokeSubkey(KeyIdentifier(subkeyId), revocationAttributes)
 
     /**
      * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be
@@ -301,16 +262,14 @@ interface SecretKeyRingEditorInterface {
      * The provided subpackets callback is used to modify the revocation signatures subpackets.
      *
      * @param subkeyId id of the subkey
-     * @param protector protector to unlock the secret key ring
      * @param callback callback which can be used to modify the subpackets of the revocation
      *   signature
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the subkey
      */
     @Throws(PGPException::class)
-    fun revokeSubKey(
+    fun revokeSubkey(
         subkeyId: Long,
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface
 
@@ -318,19 +277,16 @@ interface SecretKeyRingEditorInterface {
      * Hard-revoke the given userID.
      *
      * @param userId userId to revoke
-     * @param protector protector to unlock the primary key
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the user-id
      */
     @Throws(PGPException::class)
-    fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector) =
-        revokeUserId(userId, protector, null as RevocationAttributes?)
+    fun revokeUserId(userId: CharSequence) = revokeUserId(userId, null as RevocationAttributes?)
 
     /**
      * Revoke the given userID using the provided revocation attributes.
      *
      * @param userId userId to revoke
-     * @param protector protector to unlock the primary key
      * @param revocationAttributes reason for the revocation
      * @return the builder
      * @throws PGPException in case we cannot generate a revocation signature for the user-id
@@ -338,7 +294,6 @@ interface SecretKeyRingEditorInterface {
     @Throws(PGPException::class)
     fun revokeUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes? = null
     ): SecretKeyRingEditorInterface
 
@@ -350,7 +305,6 @@ interface SecretKeyRingEditorInterface {
      * revocation reason in the signatures hashed area using the subpacket callback.
      *
      * @param userId userid to be revoked
-     * @param protector protector to unlock the primary secret key
      * @param callback callback to modify the revocations subpackets
      * @return builder
      * @throws PGPException in case we cannot generate a revocation signature for the user-id
@@ -358,41 +312,16 @@ interface SecretKeyRingEditorInterface {
     @Throws(PGPException::class)
     fun revokeUserId(
         userId: CharSequence,
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?
     ): SecretKeyRingEditorInterface
 
     /**
-     * Revoke all user-ids that match the provided [SelectUserId] filter. The provided
-     * [RevocationAttributes] will be set as reason for revocation in each revocation signature.
+     * Revoke all user-ids that match the provided [predicate]. The provided [RevocationAttributes]
+     * will be set as reason for revocation in each revocation signature.
      *
      * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft
      * revocation reason. See [RevocationAttributes.Reason] for more information.
      *
-     * @param selector user-id selector
-     * @param protector protector to unlock the primary secret key
-     * @param revocationAttributes revocation attributes
-     * @return builder
-     * @throws PGPException in case we cannot generate a revocation signature for the user-id
-     */
-    @Throws(PGPException::class)
-    @Deprecated(
-        "Use of SelectUserId class is deprecated.",
-        ReplaceWith("revokeUserIds(protector, revocationAttributes, predicate)"))
-    fun revokeUserIds(
-        selector: SelectUserId,
-        protector: SecretKeyRingProtector,
-        revocationAttributes: RevocationAttributes?
-    ) = revokeUserIds(protector, revocationAttributes, selector)
-
-    /**
-     * Revoke all user-ids that match the provided [SelectUserId] filter. The provided
-     * [RevocationAttributes] will be set as reason for revocation in each revocation signature.
-     *
-     * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft
-     * revocation reason. See [RevocationAttributes.Reason] for more information.
-     *
-     * @param protector protector to unlock the primary secret key
      * @param revocationAttributes revocation attributes
      * @param predicate to select user-ids for revocation
      * @return builder
@@ -400,13 +329,12 @@ interface SecretKeyRingEditorInterface {
      */
     @Throws(PGPException::class)
     fun revokeUserIds(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?,
         predicate: (String) -> Boolean
     ): SecretKeyRingEditorInterface
 
     /**
-     * Revoke all user-ids that match the provided [SelectUserId] filter. The provided
+     * Revoke all user-ids that match the provided [predicate]. The provided
      * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures
      * subpackets.
      *
@@ -415,33 +343,6 @@ interface SecretKeyRingEditorInterface {
      *
      * See [RevocationAttributes.Reason] for more information.
      *
-     * @param selector user-id selector
-     * @param protector protector to unlock the primary secret key
-     * @param callback callback to modify the revocations subpackets
-     * @return builder
-     * @throws PGPException in case we cannot generate a revocation signature for the user-id
-     */
-    @Throws(PGPException::class)
-    @Deprecated(
-        "Use of SelectUserId class is deprecated.",
-        ReplaceWith("revokeUserIds(protector, callback, predicate)"))
-    fun revokeUserIds(
-        selector: SelectUserId,
-        protector: SecretKeyRingProtector,
-        callback: RevocationSignatureSubpackets.Callback?
-    ) = revokeUserIds(protector, callback, selector)
-
-    /**
-     * Revoke all user-ids that match the provided [SelectUserId] filter. The provided
-     * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures
-     * subpackets.
-     *
-     * Note: If you intend to re-certify these user-ids at a later point, make sure to set a soft
-     * revocation reason in the revocation signatures hashed subpacket area using the callback.
-     *
-     * See [RevocationAttributes.Reason] for more information.
-     *
-     * @param protector protector to unlock the primary secret key
      * @param callback callback to modify the revocations subpackets
      * @param predicate to select user-ids for revocation
      * @return builder
@@ -449,7 +350,6 @@ interface SecretKeyRingEditorInterface {
      */
     @Throws(PGPException::class)
     fun revokeUserIds(
-        protector: SecretKeyRingProtector,
         callback: RevocationSignatureSubpackets.Callback?,
         predicate: (String) -> Boolean
     ): SecretKeyRingEditorInterface
@@ -459,15 +359,28 @@ interface SecretKeyRingEditorInterface {
      * expire, then an expiration date of null is expected.
      *
      * @param expiration new expiration date or null
-     * @param protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a new self-signature with the changed
      *   expiration date
      */
     @Throws(PGPException::class)
-    fun setExpirationDate(
+    fun setExpirationDate(expiration: Date?): SecretKeyRingEditorInterface
+
+    /**
+     * Set the expiration date for the subkey identified by the given [KeyIdentifier] to the given
+     * expiration date. If the key is supposed to never expire, then an expiration date of null is
+     * expected.
+     *
+     * @param expiration new expiration date of null
+     * @param keyIdentifier identifier of the subkey
+     * @return the builder
+     * @throws PGPException in case we cannot generate a new subkey-binding or self-signature with
+     *   the changed expiration date
+     */
+    @Throws(PGPException::class)
+    fun setExpirationDateOfSubkey(
         expiration: Date?,
-        protector: SecretKeyRingProtector
+        keyIdentifier: KeyIdentifier
     ): SecretKeyRingEditorInterface
 
     /**
@@ -476,31 +389,26 @@ interface SecretKeyRingEditorInterface {
      *
      * @param expiration new expiration date of null
      * @param keyId id of the subkey
-     * @param protector to unlock the secret key
      * @return the builder
      * @throws PGPException in case we cannot generate a new subkey-binding or self-signature with
      *   the changed expiration date
      */
     @Throws(PGPException::class)
-    fun setExpirationDateOfSubkey(
-        expiration: Date?,
-        keyId: Long,
-        protector: SecretKeyRingProtector
-    ): SecretKeyRingEditorInterface
+    @Deprecated("Pass in the subkeys KeyIdentifier instead.")
+    fun setExpirationDateOfSubkey(expiration: Date?, keyId: Long): SecretKeyRingEditorInterface =
+        setExpirationDateOfSubkey(expiration, KeyIdentifier(keyId))
 
     /**
      * Create a minimal, self-authorizing revocation certificate, containing only the primary key
      * and a revocation signature. This type of revocation certificates was introduced in OpenPGP
      * v6. This method has no side effects on the original key and will leave it intact.
      *
-     * @param protector protector to unlock the primary key.
      * @param revocationAttributes reason for the revocation (key revocation)
      * @return minimal revocation certificate
      * @throws PGPException in case we cannot generate a revocation signature
      */
     @Throws(PGPException::class)
     fun createMinimalRevocationCertificate(
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
     ): PGPPublicKeyRing
 
@@ -508,14 +416,25 @@ interface SecretKeyRingEditorInterface {
      * Create a detached revocation certificate, which can be used to revoke the whole key. The
      * original key will not be modified by this method.
      *
-     * @param protector protector to unlock the primary key.
+     * @param revocationAttributes reason for the revocation
+     * @return revocation certificate
+     * @throws PGPException in case we cannot generate a revocation certificate
+     */
+    @Throws(PGPException::class)
+    fun createRevocation(revocationAttributes: RevocationAttributes?): PGPSignature
+
+    /**
+     * Create a detached revocation certificate, which can be used to revoke the specified subkey.
+     * The original key will not be modified by this method.
+     *
+     * @param subkeyIdentifier identifier of the subkey to be revoked
      * @param revocationAttributes reason for the revocation
      * @return revocation certificate
      * @throws PGPException in case we cannot generate a revocation certificate
      */
     @Throws(PGPException::class)
     fun createRevocation(
-        protector: SecretKeyRingProtector,
+        subkeyIdentifier: KeyIdentifier,
         revocationAttributes: RevocationAttributes?
     ): PGPSignature
 
@@ -524,51 +443,63 @@ interface SecretKeyRingEditorInterface {
      * The original key will not be modified by this method.
      *
      * @param subkeyId id of the subkey to be revoked
-     * @param protector protector to unlock the primary key.
      * @param revocationAttributes reason for the revocation
      * @return revocation certificate
      * @throws PGPException in case we cannot generate a revocation certificate
      */
     @Throws(PGPException::class)
+    @Deprecated("Pass in the subkeys KeyIdentifier instead.")
     fun createRevocation(
         subkeyId: Long,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
-    ): PGPSignature
+    ): PGPSignature = createRevocation(KeyIdentifier(subkeyId), revocationAttributes)
 
     /**
      * Create a detached revocation certificate, which can be used to revoke the specified subkey.
      * The original key will not be modified by this method.
      *
-     * @param subkeyId id of the subkey to be revoked
-     * @param protector protector to unlock the primary key.
+     * @param subkeyIdentifier identifier of the subkey to be revoked
      * @param callback callback to modify the subpackets of the revocation certificate.
      * @return revocation certificate
      * @throws PGPException in case we cannot generate a revocation certificate
      */
     @Throws(PGPException::class)
     fun createRevocation(
-        subkeyId: Long,
-        protector: SecretKeyRingProtector,
+        subkeyIdentifier: KeyIdentifier,
         callback: RevocationSignatureSubpackets.Callback?
     ): PGPSignature
 
+    /**
+     * Create a detached revocation certificate, which can be used to revoke the specified subkey.
+     * The original key will not be modified by this method.
+     *
+     * @param subkeyId id of the subkey to be revoked
+     * @param callback callback to modify the subpackets of the revocation certificate.
+     * @return revocation certificate
+     * @throws PGPException in case we cannot generate a revocation certificate
+     */
+    @Throws(PGPException::class)
+    @Deprecated("Pass in the subkeys KeyIdentifier instead.")
+    fun createRevocation(
+        subkeyId: Long,
+        callback: RevocationSignatureSubpackets.Callback?
+    ): PGPSignature = createRevocation(KeyIdentifier(subkeyId), callback)
+
     /**
      * Create a detached revocation certificate, which can be used to revoke the specified subkey.
      * The original key will not be modified by this method.
      *
      * @param subkeyFingerprint fingerprint of the subkey to be revoked
-     * @param protector protector to unlock the primary key.
      * @param revocationAttributes reason for the revocation
      * @return revocation certificate
      * @throws PGPException in case we cannot generate a revocation certificate
      */
     @Throws(PGPException::class)
+    @Deprecated("Pass in the subkey KeyIdentifier instead.")
     fun createRevocation(
         subkeyFingerprint: OpenPgpFingerprint,
-        protector: SecretKeyRingProtector,
         revocationAttributes: RevocationAttributes?
-    ): PGPSignature
+    ): PGPSignature = createRevocation(subkeyFingerprint.keyIdentifier, revocationAttributes)
 
     /**
      * Change the passphrase of the whole key ring.
@@ -676,11 +607,7 @@ interface SecretKeyRingEditorInterface {
      *
      * @return the key
      */
-    fun done(): PGPSecretKeyRing
+    fun done(): OpenPGPKey
 
-    fun addSubKey(
-        keySpec: KeySpec,
-        subkeyPassphrase: Passphrase,
-        protector: SecretKeyRingProtector
-    ): SecretKeyRingEditorInterface
+    fun addSubkey(keySpec: KeySpec, subkeyPassphrase: Passphrase): SecretKeyRingEditorInterface
 }
diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java
index cf34f9fe..f8dfc12a 100644
--- a/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java
+++ b/pgpainless-core/src/test/java/org/pgpainless/key/info/UserIdRevocationTest.java
@@ -109,7 +109,7 @@ public class UserIdRevocationTest {
                 .forKey(secretKeys.getSecretKey(), TestKeys.CRYPTIE_PASSPHRASE);
 
         assertThrows(NoSuchElementException.class, () -> PGPainless.modifyKeyRing(secretKeys)
-                .revokeSubKey(1L, protector));
+                .revokeSubkey(1L, protector));
     }
 
     @Test
diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java
index e640302e..dc063cb4 100644
--- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java
+++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RefuseToAddWeakSubkeyTest.java
@@ -38,7 +38,7 @@ public class RefuseToAddWeakSubkeyTest {
         KeySpec spec = KeySpec.getBuilder(KeyType.RSA(RsaLength._1024), KeyFlag.ENCRYPT_COMMS).build();
 
         assertThrows(IllegalArgumentException.class, () ->
-                editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()));
+                editor.addSubkey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys()));
     }
 
     @Test
@@ -75,7 +75,7 @@ public class RefuseToAddWeakSubkeyTest {
                 .setKeyCreationDate(editor.getReferenceTime()) // The key gets created after we instantiate the editor.
                 .build();
 
-        secretKeys = editor.addSubKey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())
+        secretKeys = editor.addSubkey(spec, Passphrase.emptyPassphrase(), SecretKeyRingProtector.unprotectedKeys())
                 .done();
 
         assertEquals(2, PGPainless.inspectKeyRing(secretKeys).getEncryptionSubkeys(EncryptionPurpose.ANY).size());
diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java
index 729c2151..8bdce950 100644
--- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java
+++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java
@@ -56,7 +56,7 @@ public class RevokeSubKeyTest {
                 .forKey(secretKeys, Passphrase.fromPassword("password123"));
 
         secretKeys = PGPainless.modifyKeyRing(secretKeys)
-                .revokeSubKey(new OpenPgpV4Fingerprint(subKey), protector)
+                .revokeSubkey(new OpenPgpV4Fingerprint(subKey), protector)
                 .done();
         keysIterator = secretKeys.iterator();
         primaryKey = keysIterator.next();
@@ -133,7 +133,7 @@ public class RevokeSubKeyTest {
                 .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey();
 
         secretKeys = PGPainless.modifyKeyRing(secretKeys)
-                .revokeSubKey(encryptionSubkey.getKeyID(), protector)
+                .revokeSubkey(encryptionSubkey.getKeyID(), protector)
                 .done();
 
         encryptionSubkey = secretKeys.getPublicKey(encryptionSubkey.getKeyID());
@@ -158,7 +158,7 @@ public class RevokeSubKeyTest {
                 .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getPGPPublicKey();
 
         secretKeys = PGPainless.modifyKeyRing(secretKeys)
-                .revokeSubKey(encryptionSubkey.getKeyID(), protector, new RevocationSignatureSubpackets.Callback() {
+                .revokeSubkey(encryptionSubkey.getKeyID(), protector, new RevocationSignatureSubpackets.Callback() {
                     @Override
                     public void modifyHashedSubpackets(RevocationSignatureSubpackets hashedSubpackets) {
                         hashedSubpackets.setRevocationReason(