mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-12-08 21:31:08 +01:00
Even more migration and code compiles again
This commit is contained in:
parent
aa3b78b374
commit
6798d2deda
50 changed files with 368 additions and 245 deletions
|
|
@ -61,8 +61,7 @@ public final class GnuPGDummyKeyUtil {
|
|||
return hardwareBackedKeys;
|
||||
}
|
||||
|
||||
public static Builder modify(@Nonnull OpenPGPKey key)
|
||||
{
|
||||
public static Builder modify(@Nonnull OpenPGPKey key) {
|
||||
return modify(key.getPGPSecretKeyRing());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.exception;
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
|
||||
public class WrongPassphraseException extends PGPException {
|
||||
|
|
@ -13,7 +14,11 @@ public class WrongPassphraseException extends PGPException {
|
|||
}
|
||||
|
||||
public WrongPassphraseException(long keyId, PGPException cause) {
|
||||
this("Wrong passphrase provided for key " + Long.toHexString(keyId), cause);
|
||||
this(new KeyIdentifier(keyId), cause);
|
||||
}
|
||||
|
||||
public WrongPassphraseException(KeyIdentifier keyIdentifier, PGPException cause) {
|
||||
this("Wrong passphrase provided for key " + keyIdentifier, cause);
|
||||
}
|
||||
|
||||
public WrongPassphraseException(String message, PGPException cause) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import org.bouncycastle.openpgp.PGPKeyRing
|
|||
import org.bouncycastle.openpgp.PGPOnePassSignature
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSignature
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
|
|
@ -72,3 +74,10 @@ val PGPKeyRing.openPgpFingerprint: OpenPgpFingerprint
|
|||
|
||||
/** Return this OpenPGP key as an ASCII armored String. */
|
||||
fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this)
|
||||
|
||||
@Deprecated("Use toOpenPGPCertificate(implementation) instead.")
|
||||
fun PGPKeyRing.toOpenPGPCertificate(): OpenPGPCertificate =
|
||||
toOpenPGPCertificate(PGPainless.getInstance().implementation)
|
||||
|
||||
fun PGPKeyRing.toOpenPGPCertificate(implementation: OpenPGPImplementation): OpenPGPCertificate =
|
||||
OpenPGPCertificate(this, implementation)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ package org.pgpainless.bouncycastle.extensions
|
|||
|
||||
import openpgp.openPgpKeyId
|
||||
import org.bouncycastle.openpgp.*
|
||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
|
||||
/** OpenPGP certificate containing the public keys of this OpenPGP key. */
|
||||
|
|
@ -74,3 +77,10 @@ fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGP
|
|||
|
||||
fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? =
|
||||
this.getSecretKey(pkesk.keyIdentifier)
|
||||
|
||||
@Deprecated("Use toOpenPGPKey(implementation) instead.")
|
||||
fun PGPSecretKeyRing.toOpenPGPKey(): OpenPGPKey =
|
||||
toOpenPGPKey(PGPainless.getInstance().implementation)
|
||||
|
||||
fun PGPSecretKeyRing.toOpenPGPKey(implementation: OpenPGPImplementation): OpenPGPKey =
|
||||
OpenPGPKey(this, implementation)
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@
|
|||
|
||||
package org.pgpainless.decryption_verification
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.*
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.bouncycastle.extensions.getPublicKeyFor
|
||||
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
|
||||
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
|
|
@ -24,9 +23,7 @@ import org.pgpainless.util.Passphrase
|
|||
import org.pgpainless.util.SessionKey
|
||||
|
||||
/** Options for decryption and signature verification. */
|
||||
class ConsumerOptions(
|
||||
private val implementation: OpenPGPImplementation
|
||||
) {
|
||||
class ConsumerOptions {
|
||||
|
||||
private var ignoreMDCErrors = false
|
||||
var isDisableAsciiArmorCRC = false
|
||||
|
|
@ -183,7 +180,8 @@ class ConsumerOptions(
|
|||
@JvmOverloads
|
||||
fun addDecryptionKey(
|
||||
key: PGPSecretKeyRing,
|
||||
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()
|
||||
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys(),
|
||||
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
|
||||
) = addDecryptionKey(OpenPGPKey(key, implementation), protector)
|
||||
|
||||
/**
|
||||
|
|
@ -402,8 +400,10 @@ class ConsumerOptions(
|
|||
*
|
||||
* @param certificate certificate
|
||||
*/
|
||||
fun addCertificate(certificate: PGPPublicKeyRing,
|
||||
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
|
||||
@JvmOverloads
|
||||
fun addCertificate(
|
||||
certificate: PGPPublicKeyRing,
|
||||
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
|
||||
) {
|
||||
explicitCertificates.add(OpenPGPCertificate(certificate, implementation))
|
||||
}
|
||||
|
|
@ -442,10 +442,6 @@ class ConsumerOptions(
|
|||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun get(
|
||||
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
|
||||
) = ConsumerOptions(implementation)
|
||||
@JvmStatic fun get() = ConsumerOptions()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
package org.pgpainless.decryption_verification
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import java.util.*
|
||||
import javax.annotation.Nonnull
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPLiteralData
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
package org.pgpainless.decryption_verification
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
|
||||
fun interface MissingPublicKeyCallback {
|
||||
|
||||
|
|
@ -14,14 +15,14 @@ fun interface MissingPublicKeyCallback {
|
|||
* here. PGPainless will then continue verification with the next signature.
|
||||
*
|
||||
* Note: The key-id might belong to a subkey, so be aware that when looking up the
|
||||
* [PGPPublicKeyRing], you may not only search for the key-id on the key rings primary key!
|
||||
* [OpenPGPCertificate], you may not only search for the key-id on the key rings primary key!
|
||||
*
|
||||
* It would be super cool to provide the OpenPgp fingerprint here, but unfortunately
|
||||
* one-pass-signatures only contain the key id.
|
||||
*
|
||||
* @param keyId ID of the missing signing (sub)key
|
||||
* @param keyIdentifier ID of the missing signing (sub)key
|
||||
* @return keyring containing the key or null
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-5.4">RFC</a>
|
||||
*/
|
||||
fun onMissingPublicKeyEncountered(keyId: Long): PGPPublicKeyRing?
|
||||
fun onMissingPublicKeyEncountered(keyIdentifier: KeyIdentifier): OpenPGPCertificate?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,8 @@ import org.bouncycastle.openpgp.PGPException
|
|||
import org.bouncycastle.openpgp.PGPKeyPair
|
||||
import org.bouncycastle.openpgp.PGPOnePassSignature
|
||||
import org.bouncycastle.openpgp.PGPPBEEncryptedData
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing
|
||||
import org.bouncycastle.openpgp.PGPSignature
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
|
|
@ -40,11 +36,9 @@ import org.pgpainless.algorithm.CompressionAlgorithm
|
|||
import org.pgpainless.algorithm.OpenPgpPacket
|
||||
import org.pgpainless.algorithm.StreamEncoding
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||
import org.pgpainless.bouncycastle.extensions.getPublicKeyFor
|
||||
import org.pgpainless.bouncycastle.extensions.getSecretKeyFor
|
||||
import org.pgpainless.bouncycastle.extensions.getSigningKeyFor
|
||||
import org.pgpainless.bouncycastle.extensions.issuerKeyId
|
||||
import org.pgpainless.bouncycastle.extensions.unlock
|
||||
import org.pgpainless.decryption_verification.MessageMetadata.CompressedData
|
||||
import org.pgpainless.decryption_verification.MessageMetadata.EncryptedData
|
||||
import org.pgpainless.decryption_verification.MessageMetadata.Layer
|
||||
|
|
@ -61,18 +55,16 @@ import org.pgpainless.exception.MissingDecryptionMethodException
|
|||
import org.pgpainless.exception.MissingPassphraseException
|
||||
import org.pgpainless.exception.SignatureValidationException
|
||||
import org.pgpainless.exception.UnacceptableAlgorithmException
|
||||
import org.pgpainless.exception.WrongPassphraseException
|
||||
import org.pgpainless.implementation.ImplementationFactory
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
import org.pgpainless.key.util.KeyRingUtils
|
||||
import org.pgpainless.policy.Policy
|
||||
import org.pgpainless.signature.consumer.CertificateValidator
|
||||
import org.pgpainless.signature.consumer.OnePassSignatureCheck
|
||||
import org.pgpainless.signature.consumer.SignatureCheck
|
||||
import org.pgpainless.signature.consumer.SignatureValidator
|
||||
import org.pgpainless.util.ArmoredInputStreamFactory
|
||||
import org.pgpainless.util.SessionKey
|
||||
import org.slf4j.LoggerFactory
|
||||
import kotlin.math.sign
|
||||
|
||||
class OpenPgpMessageInputStream(
|
||||
type: Type,
|
||||
|
|
@ -430,10 +422,11 @@ class OpenPgpMessageInputStream(
|
|||
|
||||
val privateKey = secretKey.unlock(protector)
|
||||
if (decryptWithPrivateKey(
|
||||
esks,
|
||||
privateKey,
|
||||
SubkeyIdentifier(secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier),
|
||||
pkesk)) {
|
||||
esks,
|
||||
privateKey,
|
||||
SubkeyIdentifier(
|
||||
secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier),
|
||||
pkesk)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -441,27 +434,24 @@ class OpenPgpMessageInputStream(
|
|||
|
||||
// try anonymous secret keys
|
||||
for (pkesk in esks.anonPkesks) {
|
||||
for (decryptionKeys in findPotentialDecryptionKeys(pkesk)) {
|
||||
if (hasUnsupportedS2KSpecifier(decryptionKeys)) {
|
||||
for (decryptionKey in findPotentialDecryptionKeys(pkesk)) {
|
||||
if (hasUnsupportedS2KSpecifier(decryptionKey)) {
|
||||
continue
|
||||
}
|
||||
|
||||
LOGGER.debug("Attempt decryption of anonymous PKESK with key $decryptionKeys.")
|
||||
val protector = options.getSecretKeyProtector(decryptionKeys.openPGPKey) ?: continue
|
||||
LOGGER.debug("Attempt decryption of anonymous PKESK with key $decryptionKey.")
|
||||
val protector = options.getSecretKeyProtector(decryptionKey.openPGPKey) ?: continue
|
||||
|
||||
if (!protector.hasPassphraseFor(decryptionKeys.keyIdentifier)) {
|
||||
if (!protector.hasPassphraseFor(decryptionKey.keyIdentifier)) {
|
||||
LOGGER.debug(
|
||||
"Missing passphrase for key ${decryptionKeys.keyIdentifier}. Postponing decryption until all other keys were tried.")
|
||||
postponedDueToMissingPassphrase.add(decryptionKeys to pkesk)
|
||||
"Missing passphrase for key ${decryptionKey.keyIdentifier}. Postponing decryption until all other keys were tried.")
|
||||
postponedDueToMissingPassphrase.add(decryptionKey to pkesk)
|
||||
continue
|
||||
}
|
||||
|
||||
val privateKey = decryptionKeys.unlock(protector)
|
||||
val privateKey = decryptionKey.unlock(protector)
|
||||
if (decryptWithPrivateKey(
|
||||
esks,
|
||||
privateKey,
|
||||
SubkeyIdentifier(decryptionKeys.openPGPKey.pgpSecretKeyRing, privateKey.keyIdentifier),
|
||||
pkesk)) {
|
||||
esks, privateKey, SubkeyIdentifier(decryptionKey), pkesk)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -471,7 +461,7 @@ class OpenPgpMessageInputStream(
|
|||
MissingKeyPassphraseStrategy.THROW_EXCEPTION) {
|
||||
// Non-interactive mode: Throw an exception with all locked decryption keys
|
||||
postponedDueToMissingPassphrase
|
||||
.map { SubkeyIdentifier(getDecryptionKey(it.first.keyID)!!, it.first.keyID) }
|
||||
.map { SubkeyIdentifier(it.first) }
|
||||
.also { if (it.isNotEmpty()) throw MissingPassphraseException(it.toSet()) }
|
||||
} else if (options.getMissingKeyPassphraseStrategy() ==
|
||||
MissingKeyPassphraseStrategy.INTERACTIVE) {
|
||||
|
|
@ -486,7 +476,12 @@ class OpenPgpMessageInputStream(
|
|||
LOGGER.debug(
|
||||
"Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.")
|
||||
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
|
||||
val privateKey = secretKey.unlock(protector)
|
||||
val privateKey =
|
||||
try {
|
||||
secretKey.unlock(protector)
|
||||
} catch (e: PGPException) {
|
||||
throw WrongPassphraseException(secretKey.keyIdentifier, e)
|
||||
}
|
||||
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -507,13 +502,12 @@ class OpenPgpMessageInputStream(
|
|||
pkesk: PGPPublicKeyEncryptedData
|
||||
): Boolean {
|
||||
val decryptorFactory =
|
||||
ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey.privateKey)
|
||||
ImplementationFactory.getInstance()
|
||||
.getPublicKeyDataDecryptorFactory(privateKey.privateKey)
|
||||
return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk)
|
||||
}
|
||||
|
||||
private fun hasUnsupportedS2KSpecifier(
|
||||
secretKey: OpenPGPSecretKey
|
||||
): Boolean {
|
||||
private fun hasUnsupportedS2KSpecifier(secretKey: OpenPGPSecretKey): Boolean {
|
||||
val s2k = secretKey.pgpSecretKey.s2K
|
||||
if (s2k != null) {
|
||||
if (s2k.type in 100..110) {
|
||||
|
|
@ -686,7 +680,8 @@ class OpenPgpMessageInputStream(
|
|||
|
||||
private fun getDecryptionKey(keyId: Long): OpenPGPKey? =
|
||||
options.getDecryptionKeys().firstOrNull {
|
||||
it.pgpSecretKeyRing.any { k -> k.keyID == keyId }
|
||||
it.pgpSecretKeyRing
|
||||
.any { k -> k.keyID == keyId }
|
||||
.and(
|
||||
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k ->
|
||||
k.keyIdentifier.keyId == keyId
|
||||
|
|
@ -855,7 +850,9 @@ class OpenPgpMessageInputStream(
|
|||
val verification =
|
||||
SignatureVerification(
|
||||
signature,
|
||||
SubkeyIdentifier(check.verificationKeys.pgpPublicKeyRing, check.onePassSignature.keyIdentifier))
|
||||
SubkeyIdentifier(
|
||||
check.verificationKeys.pgpPublicKeyRing,
|
||||
check.onePassSignature.keyIdentifier))
|
||||
|
||||
try {
|
||||
SignatureValidator.signatureWasCreatedInBounds(
|
||||
|
|
@ -902,13 +899,13 @@ class OpenPgpMessageInputStream(
|
|||
if (options.getMissingCertificateCallback() != null) {
|
||||
return options
|
||||
.getMissingCertificateCallback()!!
|
||||
.onMissingPublicKeyEncountered(signature.keyID)
|
||||
.onMissingPublicKeyEncountered(signature.keyIdentifiers.first())
|
||||
}
|
||||
return null // TODO: Missing cert for sig
|
||||
}
|
||||
|
||||
private fun findCertificate(signature: PGPOnePassSignature): OpenPGPCertificate? {
|
||||
val cert = options.getCertificateSource().getCertificate(signature.keyID)
|
||||
val cert = options.getCertificateSource().getCertificate(signature.keyIdentifier)
|
||||
if (cert != null) {
|
||||
return cert
|
||||
}
|
||||
|
|
@ -916,7 +913,7 @@ class OpenPgpMessageInputStream(
|
|||
if (options.getMissingCertificateCallback() != null) {
|
||||
return options
|
||||
.getMissingCertificateCallback()!!
|
||||
.onMissingPublicKeyEncountered(signature.keyID)
|
||||
.onMissingPublicKeyEncountered(signature.keyIdentifier)
|
||||
}
|
||||
return null // TODO: Missing cert for sig
|
||||
}
|
||||
|
|
@ -968,15 +965,13 @@ class OpenPgpMessageInputStream(
|
|||
fun finish(layer: Layer, policy: Policy) {
|
||||
for (detached in detachedSignatures) {
|
||||
val verification =
|
||||
SignatureVerification(detached.signature, detached.signingKeyIdentifier)
|
||||
SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer))
|
||||
try {
|
||||
SignatureValidator.signatureWasCreatedInBounds(
|
||||
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
||||
.verify(detached.signature)
|
||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
|
||||
detached.signature,
|
||||
KeyRingUtils.publicKeys(detached.signingKeyRing),
|
||||
policy)
|
||||
detached.signature, detached.issuerCertificate.pgpPublicKeyRing, policy)
|
||||
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
|
||||
layer.addVerifiedDetachedSignature(verification)
|
||||
} catch (e: SignatureValidationException) {
|
||||
|
|
@ -988,15 +983,13 @@ class OpenPgpMessageInputStream(
|
|||
|
||||
for (prepended in prependedSignatures) {
|
||||
val verification =
|
||||
SignatureVerification(prepended.signature, prepended.keyIdentifier)
|
||||
SignatureVerification(prepended.signature, SubkeyIdentifier(prepended.issuer))
|
||||
try {
|
||||
SignatureValidator.signatureWasCreatedInBounds(
|
||||
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
||||
.verify(prepended.signature)
|
||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
|
||||
prepended.signature,
|
||||
KeyRingUtils.publicKeys(prepended.signingKeyRing),
|
||||
policy)
|
||||
prepended.signature, prepended.issuerCertificate.pgpPublicKeyRing, policy)
|
||||
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
|
||||
layer.addVerifiedPrependedSignature(verification)
|
||||
} catch (e: SignatureValidationException) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package org.pgpainless.key
|
||||
|
||||
import java.nio.charset.Charset
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
|
|
@ -55,6 +56,8 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable<OpenPgpFingerprint>
|
|||
|
||||
constructor(keys: PGPKeyRing) : this(keys.publicKey)
|
||||
|
||||
abstract val keyIdentifier: KeyIdentifier
|
||||
|
||||
/**
|
||||
* Check, whether the fingerprint consists of 40 valid hexadecimal characters.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import java.net.URI
|
|||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.Charset
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
|
|
@ -39,6 +40,8 @@ class OpenPgpV4Fingerprint : OpenPgpFingerprint {
|
|||
return buf.getLong()
|
||||
}
|
||||
|
||||
override val keyIdentifier: KeyIdentifier = KeyIdentifier(bytes)
|
||||
|
||||
override fun isValid(fingerprint: String): Boolean {
|
||||
return fingerprint.matches("^[0-9A-F]{40}$".toRegex())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.key
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
|
|
@ -24,4 +25,6 @@ class OpenPgpV5Fingerprint : _64DigitFingerprint {
|
|||
override fun getVersion(): Int {
|
||||
return 5
|
||||
}
|
||||
|
||||
override val keyIdentifier: KeyIdentifier = KeyIdentifier(bytes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package org.pgpainless.key
|
|||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||
|
||||
/**
|
||||
* Tuple class used to identify a subkey by fingerprints of the primary key of the subkeys key ring,
|
||||
|
|
@ -25,6 +26,12 @@ class SubkeyIdentifier(
|
|||
|
||||
constructor(keys: PGPKeyRing, keyId: Long) : this(keys, KeyIdentifier(keyId))
|
||||
|
||||
constructor(
|
||||
key: OpenPGPComponentKey
|
||||
) : this(
|
||||
OpenPgpFingerprint.of(key.certificate.pgpPublicKeyRing),
|
||||
OpenPgpFingerprint.of(key.pgpPublicKey))
|
||||
|
||||
constructor(
|
||||
keys: PGPKeyRing,
|
||||
subkeyFingerprint: OpenPgpFingerprint
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package org.pgpainless.key
|
|||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.Charset
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
|
|
@ -51,6 +52,8 @@ open class _64DigitFingerprint : OpenPgpFingerprint {
|
|||
return -1 // might be v5 or v6
|
||||
}
|
||||
|
||||
override val keyIdentifier: KeyIdentifier = KeyIdentifier(bytes)
|
||||
|
||||
override fun isValid(fingerprint: String): Boolean {
|
||||
return fingerprint.matches(("^[0-9A-F]{64}$".toRegex()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import java.util.function.Predicate
|
|||
import javax.annotation.Nonnull
|
||||
import kotlin.NoSuchElementException
|
||||
import openpgp.openPgpKeyId
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.bcpg.sig.KeyExpirationTime
|
||||
import org.bouncycastle.openpgp.*
|
||||
import org.pgpainless.PGPainless
|
||||
|
|
@ -248,7 +249,7 @@ class SecretKeyRingEditor(
|
|||
val version = OpenPGPKeyVersion.from(secretKeyRing.getPublicKey().version)
|
||||
val keyPair = KeyRingBuilder.generateKeyPair(keySpec, OpenPGPKeyVersion.v4, referenceTime)
|
||||
val subkeyProtector =
|
||||
PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyID, subkeyPassphrase)
|
||||
PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyIdentifier, subkeyPassphrase)
|
||||
val keyFlags = KeyFlag.fromBitmask(keySpec.subpackets.keyFlags).toMutableList()
|
||||
return addSubKey(
|
||||
keyPair,
|
||||
|
|
@ -555,15 +556,15 @@ class SecretKeyRingEditor(
|
|||
}
|
||||
|
||||
override fun changeSubKeyPassphraseFromOldPassphrase(
|
||||
keyId: Long,
|
||||
keyIdentifier: KeyIdentifier,
|
||||
oldPassphrase: Passphrase,
|
||||
oldProtectionSettings: KeyRingProtectionSettings
|
||||
): SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings {
|
||||
return WithKeyRingEncryptionSettingsImpl(
|
||||
this,
|
||||
keyId,
|
||||
keyIdentifier,
|
||||
CachingSecretKeyRingProtector(
|
||||
mapOf(keyId to oldPassphrase), oldProtectionSettings, null))
|
||||
mapOf(keyIdentifier to oldPassphrase), oldProtectionSettings, null))
|
||||
}
|
||||
|
||||
override fun done(): PGPSecretKeyRing {
|
||||
|
|
@ -745,7 +746,7 @@ class SecretKeyRingEditor(
|
|||
|
||||
private class WithKeyRingEncryptionSettingsImpl(
|
||||
private val editor: SecretKeyRingEditor,
|
||||
private val keyId: Long?,
|
||||
private val keyId: KeyIdentifier?,
|
||||
private val oldProtector: SecretKeyRingProtector
|
||||
) : SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings {
|
||||
|
||||
|
|
@ -762,7 +763,7 @@ class SecretKeyRingEditor(
|
|||
|
||||
private class WithPassphraseImpl(
|
||||
private val editor: SecretKeyRingEditor,
|
||||
private val keyId: Long?,
|
||||
private val keyId: KeyIdentifier?,
|
||||
private val oldProtector: SecretKeyRingProtector,
|
||||
private val newProtectionSettings: KeyRingProtectionSettings
|
||||
) : SecretKeyRingEditorInterface.WithPassphrase {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import java.io.IOException
|
|||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.*
|
||||
import org.pgpainless.algorithm.KeyFlag
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
|
|
@ -592,19 +593,9 @@ interface SecretKeyRingEditorInterface {
|
|||
KeyRingProtectionSettings.secureDefaultSettings()
|
||||
): WithKeyRingEncryptionSettings
|
||||
|
||||
/**
|
||||
* Change the passphrase of a single subkey in the key ring.
|
||||
*
|
||||
* Note: While it is a valid use-case to have different passphrases per subKey, this is one of
|
||||
* the reasons why OpenPGP sucks in practice.
|
||||
*
|
||||
* @param keyId id of the subkey
|
||||
* @param oldPassphrase old passphrase (empty if the key was unprotected)
|
||||
* @return next builder step
|
||||
*/
|
||||
@Deprecated("Pass KeyIdentifier instead.")
|
||||
fun changeSubKeyPassphraseFromOldPassphrase(keyId: Long, oldPassphrase: Passphrase) =
|
||||
changeSubKeyPassphraseFromOldPassphrase(
|
||||
keyId, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings())
|
||||
changeSubKeyPassphraseFromOldPassphrase(KeyIdentifier(keyId), oldPassphrase)
|
||||
|
||||
/**
|
||||
* Change the passphrase of a single subkey in the key ring.
|
||||
|
|
@ -612,13 +603,30 @@ interface SecretKeyRingEditorInterface {
|
|||
* Note: While it is a valid use-case to have different passphrases per subKey, this is one of
|
||||
* the reasons why OpenPGP sucks in practice.
|
||||
*
|
||||
* @param keyId id of the subkey
|
||||
* @param keyIdentifier id of the subkey
|
||||
* @param oldPassphrase old passphrase (empty if the key was unprotected)
|
||||
* @return next builder step
|
||||
*/
|
||||
fun changeSubKeyPassphraseFromOldPassphrase(
|
||||
keyIdentifier: KeyIdentifier,
|
||||
oldPassphrase: Passphrase
|
||||
) =
|
||||
changeSubKeyPassphraseFromOldPassphrase(
|
||||
keyIdentifier, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings())
|
||||
|
||||
/**
|
||||
* Change the passphrase of a single subkey in the key ring.
|
||||
*
|
||||
* Note: While it is a valid use-case to have different passphrases per subKey, this is one of
|
||||
* the reasons why OpenPGP sucks in practice.
|
||||
*
|
||||
* @param keyIdentifier id of the subkey
|
||||
* @param oldPassphrase old passphrase (empty if the key was unprotected)
|
||||
* @param oldProtectionSettings custom settings for the old passphrase
|
||||
* @return next builder step
|
||||
*/
|
||||
fun changeSubKeyPassphraseFromOldPassphrase(
|
||||
keyId: Long,
|
||||
keyIdentifier: KeyIdentifier,
|
||||
oldPassphrase: Passphrase,
|
||||
oldProtectionSettings: KeyRingProtectionSettings
|
||||
): WithKeyRingEncryptionSettings
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package org.pgpainless.key.protection
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
|
||||
import org.pgpainless.implementation.ImplementationFactory
|
||||
|
|
@ -22,16 +24,21 @@ open class BaseSecretKeyRingProtector(
|
|||
passphraseProvider: SecretKeyPassphraseProvider
|
||||
) : this(passphraseProvider, KeyRingProtectionSettings.secureDefaultSettings())
|
||||
|
||||
override fun hasPassphraseFor(keyId: Long): Boolean = passphraseProvider.hasPassphrase(keyId)
|
||||
override fun hasPassphraseFor(keyIdentifier: KeyIdentifier): Boolean {
|
||||
return passphraseProvider.hasPassphrase(keyIdentifier)
|
||||
}
|
||||
|
||||
override fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? =
|
||||
passphraseProvider.getPassphraseFor(keyId)?.let {
|
||||
getDecryptor(KeyIdentifier(keyId))
|
||||
|
||||
override fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor? =
|
||||
passphraseProvider.getPassphraseFor(keyIdentifier)?.let {
|
||||
if (it.isEmpty) null
|
||||
else ImplementationFactory.getInstance().getPBESecretKeyDecryptor(it)
|
||||
}
|
||||
|
||||
override fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? =
|
||||
passphraseProvider.getPassphraseFor(keyId)?.let {
|
||||
override fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor? {
|
||||
return passphraseProvider.getPassphraseFor(keyIdentifier)?.let {
|
||||
if (it.isEmpty) null
|
||||
else
|
||||
ImplementationFactory.getInstance()
|
||||
|
|
@ -41,4 +48,9 @@ open class BaseSecretKeyRingProtector(
|
|||
protectionSettings.s2kCount,
|
||||
it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKeyPassword(p0: OpenPGPKey.OpenPGPSecretKey): CharArray? {
|
||||
return passphraseProvider.getPassphraseFor(p0.keyIdentifier)?.getChars()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
package org.pgpainless.key.protection
|
||||
|
||||
import openpgp.openPgpKeyId
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
|
@ -21,7 +24,7 @@ import org.pgpainless.util.Passphrase
|
|||
*/
|
||||
class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphraseProvider {
|
||||
|
||||
private val cache: MutableMap<Long?, Passphrase>
|
||||
private val cache: MutableMap<KeyIdentifier?, Passphrase>
|
||||
private val protector: SecretKeyRingProtector
|
||||
private val provider: SecretKeyPassphraseProvider?
|
||||
|
||||
|
|
@ -30,12 +33,12 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
constructor(
|
||||
missingPassphraseCallback: SecretKeyPassphraseProvider?
|
||||
) : this(
|
||||
mapOf<Long, Passphrase>(),
|
||||
mapOf<KeyIdentifier, Passphrase>(),
|
||||
KeyRingProtectionSettings.secureDefaultSettings(),
|
||||
missingPassphraseCallback)
|
||||
|
||||
constructor(
|
||||
passphrases: Map<Long, Passphrase>,
|
||||
passphrases: Map<KeyIdentifier, Passphrase>,
|
||||
protectionSettings: KeyRingProtectionSettings,
|
||||
missingPassphraseCallback: SecretKeyPassphraseProvider?
|
||||
) {
|
||||
|
|
@ -44,6 +47,10 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
this.provider = missingPassphraseCallback
|
||||
}
|
||||
|
||||
@Deprecated("Pass KeyIdentifier instead.")
|
||||
fun addPassphrase(keyId: Long, passphrase: Passphrase) =
|
||||
addPassphrase(KeyIdentifier(keyId), passphrase)
|
||||
|
||||
/**
|
||||
* Add a passphrase to the cache. If the cache already contains a passphrase for the given
|
||||
* key-id, a [IllegalArgumentException] is thrown. The reason for this is to prevent accidental
|
||||
|
|
@ -53,24 +60,30 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
* If you can ensure that there will be no key-id clash, and you want to replace the passphrase,
|
||||
* you can use [replacePassphrase] to replace the passphrase.
|
||||
*
|
||||
* @param keyId id of the key
|
||||
* @param keyIdentifier id of the key
|
||||
* @param passphrase passphrase
|
||||
*/
|
||||
fun addPassphrase(keyId: Long, passphrase: Passphrase) = apply {
|
||||
require(!cache.containsKey(keyId)) {
|
||||
"The cache already holds a passphrase for ID ${keyId.openPgpKeyId()}.\n" +
|
||||
fun addPassphrase(keyIdentifier: KeyIdentifier, passphrase: Passphrase) = apply {
|
||||
require(!cache.containsKey(keyIdentifier)) {
|
||||
"The cache already holds a passphrase for ID ${keyIdentifier}.\n" +
|
||||
"If you want to replace this passphrase, use replacePassphrase(Long, Passphrase) instead."
|
||||
}
|
||||
cache[keyId] = passphrase
|
||||
cache[keyIdentifier] = passphrase
|
||||
}
|
||||
|
||||
@Deprecated("Pass KeyIdentifier instead.")
|
||||
fun replacePassphrase(keyId: Long, passphrase: Passphrase) =
|
||||
replacePassphrase(KeyIdentifier(keyId), passphrase)
|
||||
|
||||
/**
|
||||
* Replace the passphrase for the given key-id in the cache.
|
||||
*
|
||||
* @param keyId keyId
|
||||
* @param passphrase passphrase
|
||||
*/
|
||||
fun replacePassphrase(keyId: Long, passphrase: Passphrase) = apply { cache[keyId] = passphrase }
|
||||
fun replacePassphrase(keyId: KeyIdentifier, passphrase: Passphrase) = apply {
|
||||
cache[keyId] = passphrase
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember the given passphrase for all keys in the given key ring. If for the key-id of any
|
||||
|
|
@ -91,14 +104,14 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
fun addPassphrase(keyRing: PGPKeyRing, passphrase: Passphrase) = apply {
|
||||
// check for existing passphrases before doing anything
|
||||
keyRing.publicKeys.forEach {
|
||||
require(!cache.containsKey(it.keyID)) {
|
||||
"The cache already holds a passphrase for the key with ID ${it.keyID.openPgpKeyId()}.\n" +
|
||||
require(!cache.containsKey(it.keyIdentifier)) {
|
||||
"The cache already holds a passphrase for the key with ID ${it.keyIdentifier}.\n" +
|
||||
"If you want to replace the passphrase, use replacePassphrase(PGPKeyRing, Passphrase) instead."
|
||||
}
|
||||
}
|
||||
|
||||
// only then instert
|
||||
keyRing.publicKeys.forEach { cache[it.keyID] = passphrase }
|
||||
keyRing.publicKeys.forEach { cache[it.keyIdentifier] = passphrase }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -108,7 +121,7 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
* @param passphrase passphrase
|
||||
*/
|
||||
fun replacePassphrase(keyRing: PGPKeyRing, passphrase: Passphrase) = apply {
|
||||
keyRing.publicKeys.forEach { cache[it.keyID] = passphrase }
|
||||
keyRing.publicKeys.forEach { cache[it.keyIdentifier] = passphrase }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -118,7 +131,7 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
* @param passphrase passphrase
|
||||
*/
|
||||
fun addPassphrase(key: PGPPublicKey, passphrase: Passphrase) =
|
||||
addPassphrase(key.keyID, passphrase)
|
||||
addPassphrase(key.keyIdentifier, passphrase)
|
||||
|
||||
/**
|
||||
* Remember the given passphrase for the key with the given fingerprint.
|
||||
|
|
@ -127,14 +140,17 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
* @param passphrase passphrase
|
||||
*/
|
||||
fun addPassphrase(fingerprint: OpenPgpFingerprint, passphrase: Passphrase) =
|
||||
addPassphrase(fingerprint.keyId, passphrase)
|
||||
addPassphrase(fingerprint.keyIdentifier, passphrase)
|
||||
|
||||
@Deprecated("Pass KeyIdentifier instead.")
|
||||
fun forgetPassphrase(keyId: Long) = forgetPassphrase(KeyIdentifier(keyId))
|
||||
|
||||
/**
|
||||
* Remove a passphrase from the cache. The passphrase will be cleared and then removed.
|
||||
*
|
||||
* @param keyId id of the key
|
||||
*/
|
||||
fun forgetPassphrase(keyId: Long) = apply { cache.remove(keyId)?.clear() }
|
||||
fun forgetPassphrase(keyId: KeyIdentifier) = apply { cache.remove(keyId)?.clear() }
|
||||
|
||||
/**
|
||||
* Forget the passphrase to all keys in the provided key ring.
|
||||
|
|
@ -150,18 +166,27 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras
|
|||
*
|
||||
* @param key key
|
||||
*/
|
||||
fun forgetPassphrase(key: PGPPublicKey) = apply { forgetPassphrase(key.keyID) }
|
||||
fun forgetPassphrase(key: PGPPublicKey) = apply { forgetPassphrase(key.keyIdentifier) }
|
||||
|
||||
override fun getPassphraseFor(keyId: Long?): Passphrase? {
|
||||
return if (hasPassphrase(keyId)) cache[keyId]
|
||||
else provider?.getPassphraseFor(keyId)?.also { cache[keyId] = it }
|
||||
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? {
|
||||
return if (hasPassphrase(keyIdentifier)) cache[keyIdentifier]
|
||||
else provider?.getPassphraseFor(keyIdentifier)?.also { cache[keyIdentifier] = it }
|
||||
}
|
||||
|
||||
override fun hasPassphrase(keyId: Long?) = cache[keyId]?.isValid ?: false
|
||||
override fun hasPassphraseFor(keyIdentifier: KeyIdentifier): Boolean {
|
||||
return hasPassphrase(keyIdentifier)
|
||||
}
|
||||
|
||||
override fun hasPassphraseFor(keyId: Long) = hasPassphrase(keyId)
|
||||
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean {
|
||||
return cache[keyIdentifier]?.isValid ?: false
|
||||
}
|
||||
|
||||
override fun getDecryptor(keyId: Long) = protector.getDecryptor(keyId)
|
||||
override fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor? =
|
||||
protector.getDecryptor(keyIdentifier)
|
||||
|
||||
override fun getEncryptor(keyId: Long) = protector.getEncryptor(keyId)
|
||||
override fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor? =
|
||||
protector.getEncryptor(keyIdentifier)
|
||||
|
||||
override fun getKeyPassword(p0: OpenPGPKey.OpenPGPSecretKey): CharArray? =
|
||||
getPassphraseFor(p0.keyIdentifier)?.getChars()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.key.protection
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
|
||||
|
|
@ -38,12 +39,12 @@ class PasswordBasedSecretKeyRingProtector : BaseSecretKeyRingProtector {
|
|||
): PasswordBasedSecretKeyRingProtector {
|
||||
return object : SecretKeyPassphraseProvider {
|
||||
|
||||
override fun getPassphraseFor(keyId: Long?): Passphrase? {
|
||||
return if (hasPassphrase(keyId)) passphrase else null
|
||||
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? {
|
||||
return if (hasPassphrase(keyIdentifier)) passphrase else null
|
||||
}
|
||||
|
||||
override fun hasPassphrase(keyId: Long?): Boolean {
|
||||
return keyId != null && keyRing.getPublicKey(keyId) != null
|
||||
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean {
|
||||
return keyRing.getPublicKey(keyIdentifier) != null
|
||||
}
|
||||
}
|
||||
.let { PasswordBasedSecretKeyRingProtector(it) }
|
||||
|
|
@ -51,20 +52,20 @@ class PasswordBasedSecretKeyRingProtector : BaseSecretKeyRingProtector {
|
|||
|
||||
@JvmStatic
|
||||
fun forKey(key: PGPSecretKey, passphrase: Passphrase): PasswordBasedSecretKeyRingProtector =
|
||||
forKeyId(key.publicKey.keyID, passphrase)
|
||||
forKeyId(key.publicKey.keyIdentifier, passphrase)
|
||||
|
||||
@JvmStatic
|
||||
fun forKeyId(
|
||||
singleKeyId: Long,
|
||||
singleKeyIdentifier: KeyIdentifier,
|
||||
passphrase: Passphrase
|
||||
): PasswordBasedSecretKeyRingProtector {
|
||||
return object : SecretKeyPassphraseProvider {
|
||||
override fun getPassphraseFor(keyId: Long?): Passphrase? {
|
||||
return if (hasPassphrase(keyId)) passphrase else null
|
||||
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? {
|
||||
return if (hasPassphrase(keyIdentifier)) passphrase else null
|
||||
}
|
||||
|
||||
override fun hasPassphrase(keyId: Long?): Boolean {
|
||||
return keyId == singleKeyId
|
||||
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean {
|
||||
return keyIdentifier.matches(singleKeyIdentifier)
|
||||
}
|
||||
}
|
||||
.let { PasswordBasedSecretKeyRingProtector(it) }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.key.protection
|
||||
|
||||
import kotlin.Throws
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPException
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
|
|
@ -14,7 +15,6 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
|
|||
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
|
||||
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider
|
||||
import org.pgpainless.util.Passphrase
|
||||
import kotlin.Throws
|
||||
|
||||
/**
|
||||
* Task of the [SecretKeyRingProtector] is to map encryptor/decryptor objects to key-ids.
|
||||
|
|
@ -43,10 +43,11 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
|
|||
* @param keyId id of the key
|
||||
* @return decryptor for the key
|
||||
*/
|
||||
@Throws(PGPException::class) fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? =
|
||||
getDecryptor(KeyIdentifier(keyId))
|
||||
@Throws(PGPException::class)
|
||||
fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? = getDecryptor(KeyIdentifier(keyId))
|
||||
|
||||
@Throws(PGPException::class) fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor?
|
||||
@Throws(PGPException::class)
|
||||
fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor?
|
||||
|
||||
/**
|
||||
* Return an encryptor for the key of id `keyId`. This method returns null if the key is
|
||||
|
|
@ -55,10 +56,11 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
|
|||
* @param keyId id of the key
|
||||
* @return encryptor for the key
|
||||
*/
|
||||
@Throws(PGPException::class) fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? =
|
||||
getEncryptor(KeyIdentifier(keyId))
|
||||
@Throws(PGPException::class)
|
||||
fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? = getEncryptor(KeyIdentifier(keyId))
|
||||
|
||||
@Throws(PGPException::class) fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor?
|
||||
@Throws(PGPException::class)
|
||||
fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor?
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
@ -97,7 +99,7 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
|
|||
passphrase: Passphrase,
|
||||
keys: PGPSecretKeyRing
|
||||
): SecretKeyRingProtector =
|
||||
fromPassphraseMap(keys.map { it.keyID }.associateWith { passphrase })
|
||||
fromPassphraseMap(keys.map { it.keyIdentifier }.associateWith { passphrase })
|
||||
|
||||
/**
|
||||
* Use the provided passphrase to unlock any key.
|
||||
|
|
@ -132,12 +134,15 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
|
|||
* Otherwise, this protector will always return null.
|
||||
*
|
||||
* @param passphrase passphrase
|
||||
* @param keyId id of the key to lock/unlock
|
||||
* @param keyIdentifier id of the key to lock/unlock
|
||||
* @return protector
|
||||
*/
|
||||
@JvmStatic
|
||||
fun unlockSingleKeyWith(passphrase: Passphrase, keyId: Long): SecretKeyRingProtector =
|
||||
PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase)
|
||||
fun unlockSingleKeyWith(
|
||||
passphrase: Passphrase,
|
||||
keyIdentifier: KeyIdentifier
|
||||
): SecretKeyRingProtector =
|
||||
PasswordBasedSecretKeyRingProtector.forKeyId(keyIdentifier, passphrase)
|
||||
|
||||
/**
|
||||
* Protector for unprotected keys. This protector returns null for all
|
||||
|
|
@ -159,7 +164,9 @@ interface SecretKeyRingProtector : KeyPassphraseProvider {
|
|||
* @return protector
|
||||
*/
|
||||
@JvmStatic
|
||||
fun fromPassphraseMap(passphraseMap: Map<Long, Passphrase>): SecretKeyRingProtector =
|
||||
fun fromPassphraseMap(
|
||||
passphraseMap: Map<KeyIdentifier, Passphrase>
|
||||
): SecretKeyRingProtector =
|
||||
CachingSecretKeyRingProtector(
|
||||
passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.key.protection.passphrase_provider
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
||||
/**
|
||||
|
|
@ -14,9 +15,11 @@ import org.pgpainless.util.Passphrase
|
|||
*
|
||||
* TODO: Make this null-safe and throw an exception instead?
|
||||
*/
|
||||
class MapBasedPassphraseProvider(val map: Map<Long?, Passphrase>) : SecretKeyPassphraseProvider {
|
||||
class MapBasedPassphraseProvider(val map: Map<KeyIdentifier?, Passphrase>) :
|
||||
SecretKeyPassphraseProvider {
|
||||
|
||||
override fun getPassphraseFor(keyId: Long?): Passphrase? = map[keyId]
|
||||
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? = map[keyIdentifier]
|
||||
|
||||
override fun hasPassphrase(keyId: Long?): Boolean = map.containsKey(keyId)
|
||||
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean =
|
||||
map.containsKey(keyIdentifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.pgpainless.key.protection.passphrase_provider
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ interface SecretKeyPassphraseProvider {
|
|||
* @return passphrase or null, if no passphrase record is found.
|
||||
*/
|
||||
fun getPassphraseFor(secretKey: PGPSecretKey): Passphrase? {
|
||||
return getPassphraseFor(secretKey.keyID)
|
||||
return getPassphraseFor(secretKey.keyIdentifier)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -30,7 +31,11 @@ interface SecretKeyPassphraseProvider {
|
|||
* @param keyId if of the secret key
|
||||
* @return passphrase or null, if no passphrase record has been found.
|
||||
*/
|
||||
fun getPassphraseFor(keyId: Long?): Passphrase?
|
||||
fun getPassphraseFor(keyId: Long): Passphrase? = getPassphraseFor(KeyIdentifier(keyId))
|
||||
|
||||
fun hasPassphrase(keyId: Long?): Boolean
|
||||
fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase?
|
||||
|
||||
fun hasPassphrase(keyId: Long): Boolean = hasPassphrase(KeyIdentifier(keyId))
|
||||
|
||||
fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@
|
|||
|
||||
package org.pgpainless.key.protection.passphrase_provider
|
||||
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
||||
/** Implementation of the [SecretKeyPassphraseProvider] that holds a single [Passphrase]. */
|
||||
class SolitaryPassphraseProvider(val passphrase: Passphrase?) : SecretKeyPassphraseProvider {
|
||||
|
||||
override fun getPassphraseFor(keyId: Long?): Passphrase? = passphrase
|
||||
override fun getPassphraseFor(keyIdentifier: KeyIdentifier): Passphrase? = passphrase
|
||||
|
||||
override fun hasPassphrase(keyId: Long?): Boolean = true
|
||||
override fun hasPassphrase(keyIdentifier: KeyIdentifier): Boolean = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package org.pgpainless.key.util
|
|||
import java.io.ByteArrayOutputStream
|
||||
import kotlin.jvm.Throws
|
||||
import openpgp.openPgpKeyId
|
||||
import org.bouncycastle.bcpg.KeyIdentifier
|
||||
import org.bouncycastle.bcpg.S2K
|
||||
import org.bouncycastle.bcpg.SecretKeyPacket
|
||||
import org.bouncycastle.openpgp.*
|
||||
|
|
@ -468,7 +469,7 @@ class KeyRingUtils {
|
|||
@JvmStatic
|
||||
@Throws(MissingPassphraseException::class, PGPException::class)
|
||||
fun changePassphrase(
|
||||
keyId: Long?,
|
||||
keyId: KeyIdentifier?,
|
||||
secretKeys: PGPSecretKeyRing,
|
||||
oldProtector: SecretKeyRingProtector,
|
||||
newProtector: SecretKeyRingProtector
|
||||
|
|
@ -484,7 +485,7 @@ class KeyRingUtils {
|
|||
secretKeys.secretKeys
|
||||
.asSequence()
|
||||
.map {
|
||||
if (it.keyID == keyId) {
|
||||
if (it.keyIdentifier.matches(keyId)) {
|
||||
reencryptPrivateKey(it, oldProtector, newProtector)
|
||||
} else {
|
||||
it
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import org.bouncycastle.openpgp.PGPSignature
|
|||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.algorithm.KeyFlag
|
||||
import org.pgpainless.algorithm.SignatureType
|
||||
import org.pgpainless.bouncycastle.extensions.getPublicKey
|
||||
import org.pgpainless.bouncycastle.extensions.issuerKeyId
|
||||
import org.pgpainless.exception.SignatureValidationException
|
||||
import org.pgpainless.key.util.KeyRingUtils
|
||||
|
|
@ -303,10 +304,12 @@ class CertificateValidator {
|
|||
policy: Policy
|
||||
): Boolean {
|
||||
return validateCertificate(
|
||||
onePassSignature.signature!!, onePassSignature.verificationKeys, policy) &&
|
||||
onePassSignature.signature!!,
|
||||
onePassSignature.verificationKeys.pgpPublicKeyRing,
|
||||
policy) &&
|
||||
SignatureVerifier.verifyOnePassSignature(
|
||||
onePassSignature.signature!!,
|
||||
onePassSignature.verificationKeys.getPublicKey(
|
||||
onePassSignature.verificationKeys.pgpKeyRing.getPublicKey(
|
||||
onePassSignature.signature!!.issuerKeyId),
|
||||
onePassSignature,
|
||||
policy)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import org.bouncycastle.openpgp.PGPOnePassSignature
|
|||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||
import org.bouncycastle.openpgp.PGPSignature
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||
import org.pgpainless.bouncycastle.extensions.getSigningKeyFor
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue