1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 18:29:39 +02:00

Port ConsumerOptions, SigningOptions to new OpenPGPCertificate, OpenPGPKey classes

This commit is contained in:
Paul Schaub 2025-02-12 19:32:22 +01:00
parent bbecdd693f
commit 4a90b8721f
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 260 additions and 150 deletions

View file

@ -12,6 +12,7 @@ import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
@ -73,12 +74,20 @@ class ConsumerOptions {
this.certificates.addCertificate(verificationCert) this.certificates.addCertificate(verificationCert)
} }
fun addVerificationCerts(verificationCerts: Collection<OpenPGPCertificate>): ConsumerOptions =
apply {
for (cert in verificationCerts) {
addVerificationCert(cert)
}
}
/** /**
* Add a certificate (public key ring) for signature verification. * Add a certificate (public key ring) for signature verification.
* *
* @param verificationCert certificate for signature verification * @param verificationCert certificate for signature verification
* @return options * @return options
*/ */
@Deprecated("Pass OpenPGPCertificate instead.")
fun addVerificationCert(verificationCert: PGPPublicKeyRing): ConsumerOptions = apply { fun addVerificationCert(verificationCert: PGPPublicKeyRing): ConsumerOptions = apply {
this.certificates.addCertificate(verificationCert) this.certificates.addCertificate(verificationCert)
} }
@ -89,6 +98,7 @@ class ConsumerOptions {
* @param verificationCerts certificates for signature verification * @param verificationCerts certificates for signature verification
* @return options * @return options
*/ */
@Deprecated("Use of methods taking PGPPublicKeyRingCollections is discouraged.")
fun addVerificationCerts(verificationCerts: PGPPublicKeyRingCollection): ConsumerOptions = fun addVerificationCerts(verificationCerts: PGPPublicKeyRingCollection): ConsumerOptions =
apply { apply {
for (cert in verificationCerts) { for (cert in verificationCerts) {
@ -125,6 +135,14 @@ class ConsumerOptions {
} }
} }
fun addVerificationOfDetachedSignature(signature: OpenPGPDocumentSignature): ConsumerOptions =
apply {
if (signature.issuerCertificate != null) {
addVerificationCert(signature.issuerCertificate)
}
addVerificationOfDetachedSignature(signature.signature)
}
/** /**
* Add a detached signature for the signature verification process. * Add a detached signature for the signature verification process.
* *
@ -178,6 +196,7 @@ class ConsumerOptions {
* @return options * @return options
*/ */
@JvmOverloads @JvmOverloads
@Deprecated("Pass OpenPGPKey instead.")
fun addDecryptionKey( fun addDecryptionKey(
key: PGPSecretKeyRing, key: PGPSecretKeyRing,
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys(), protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys(),
@ -192,6 +211,7 @@ class ConsumerOptions {
* @return options * @return options
*/ */
@JvmOverloads @JvmOverloads
@Deprecated("Pass OpenPGPKey instances instead.")
fun addDecryptionKeys( fun addDecryptionKeys(
keys: PGPSecretKeyRingCollection, keys: PGPSecretKeyRingCollection,
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()
@ -201,21 +221,6 @@ class ConsumerOptions {
} }
} }
/**
* Add a passphrase for message decryption. This passphrase will be used to try to decrypt
* messages which were symmetrically encrypted for a passphrase.
*
* See
* [Symmetrically Encrypted Data Packet](https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
*
* @param passphrase passphrase
* @return options
*/
@Deprecated(
"Deprecated in favor of addMessagePassphrase",
ReplaceWith("addMessagePassphrase(passphrase)"))
fun addDecryptionPassphrase(passphrase: Passphrase) = addMessagePassphrase(passphrase)
/** /**
* Add a passphrase for message decryption. This passphrase will be used to try to decrypt * Add a passphrase for message decryption. This passphrase will be used to try to decrypt
* messages which were symmetrically encrypted for a passphrase. * messages which were symmetrically encrypted for a passphrase.
@ -255,21 +260,21 @@ class ConsumerOptions {
* *
* @return decryption keys * @return decryption keys
*/ */
fun getDecryptionKeys() = decryptionKeys.keys.toSet() fun getDecryptionKeys(): Set<OpenPGPKey> = decryptionKeys.keys.toSet()
/** /**
* Return the set of available message decryption passphrases. * Return the set of available message decryption passphrases.
* *
* @return decryption passphrases * @return decryption passphrases
*/ */
fun getDecryptionPassphrases() = decryptionPassphrases.toSet() fun getDecryptionPassphrases(): Set<Passphrase> = decryptionPassphrases.toSet()
/** /**
* Return an object holding available certificates for signature verification. * Return an object holding available certificates for signature verification.
* *
* @return certificate source * @return certificate source
*/ */
fun getCertificateSource() = certificates fun getCertificateSource(): CertificateSource = certificates
/** /**
* Return the callback that gets called when a certificate for signature verification is * Return the callback that gets called when a certificate for signature verification is
@ -277,7 +282,7 @@ class ConsumerOptions {
* *
* @return missing public key callback * @return missing public key callback
*/ */
fun getMissingCertificateCallback() = missingCertificateCallback fun getMissingCertificateCallback(): MissingPublicKeyCallback? = missingCertificateCallback
/** /**
* Return the [SecretKeyRingProtector] for the given [PGPSecretKeyRing]. * Return the [SecretKeyRingProtector] for the given [PGPSecretKeyRing].
@ -321,7 +326,7 @@ class ConsumerOptions {
this.ignoreMDCErrors = ignoreMDCErrors this.ignoreMDCErrors = ignoreMDCErrors
} }
fun isIgnoreMDCErrors() = ignoreMDCErrors fun isIgnoreMDCErrors(): Boolean = ignoreMDCErrors
/** /**
* Force PGPainless to handle the data provided by the [InputStream] as non-OpenPGP data. This * Force PGPainless to handle the data provided by the [InputStream] as non-OpenPGP data. This
@ -337,7 +342,7 @@ class ConsumerOptions {
* *
* @return true if non-OpenPGP data is forced * @return true if non-OpenPGP data is forced
*/ */
fun isForceNonOpenPgpData() = forceNonOpenPgpData fun isForceNonOpenPgpData(): Boolean = forceNonOpenPgpData
/** /**
* Specify the [MissingKeyPassphraseStrategy]. This strategy defines, how missing passphrases * Specify the [MissingKeyPassphraseStrategy]. This strategy defines, how missing passphrases

View file

@ -26,6 +26,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
@ -58,6 +59,7 @@ import org.pgpainless.exception.UnacceptableAlgorithmException
import org.pgpainless.exception.WrongPassphraseException import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
import org.pgpainless.policy.Policy import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.CertificateValidator import org.pgpainless.signature.consumer.CertificateValidator
import org.pgpainless.signature.consumer.OnePassSignatureCheck import org.pgpainless.signature.consumer.OnePassSignatureCheck
@ -420,10 +422,15 @@ class OpenPgpMessageInputStream(
continue continue
} }
val privateKey = secretKey.unlock(protector) val privateKey =
try {
unlockSecretKey(secretKey, protector)
} catch (e: PGPException) {
throw WrongPassphraseException(secretKey.keyIdentifier, e)
}
if (decryptWithPrivateKey( if (decryptWithPrivateKey(
esks, esks,
privateKey, privateKey.unlockedKey,
SubkeyIdentifier( SubkeyIdentifier(
secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier), secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier),
pkesk)) { pkesk)) {
@ -451,7 +458,7 @@ class OpenPgpMessageInputStream(
val privateKey = decryptionKey.unlock(protector) val privateKey = decryptionKey.unlock(protector)
if (decryptWithPrivateKey( if (decryptWithPrivateKey(
esks, privateKey, SubkeyIdentifier(decryptionKey), pkesk)) { esks, privateKey.unlockedKey, SubkeyIdentifier(decryptionKey), pkesk)) {
return true return true
} }
} }
@ -476,13 +483,13 @@ class OpenPgpMessageInputStream(
LOGGER.debug( LOGGER.debug(
"Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.") "Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
val privateKey = val privateKey: OpenPGPPrivateKey =
try { try {
secretKey.unlock(protector) unlockSecretKey(secretKey, protector)
} catch (e: PGPException) { } catch (e: PGPException) {
throw WrongPassphraseException(secretKey.keyIdentifier, e) throw WrongPassphraseException(secretKey.keyIdentifier, e)
} }
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { if (decryptWithPrivateKey(esks, privateKey.unlockedKey, decryptionKeyId, pkesk)) {
return true return true
} }
} }

View file

@ -17,6 +17,7 @@ import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.util.ArmoredOutputStreamFactory import org.pgpainless.util.ArmoredOutputStreamFactory
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -250,7 +251,7 @@ class EncryptionStream(
options.signingOptions.signingMethods.entries.reversed().forEach { (key, method) -> options.signingOptions.signingMethods.entries.reversed().forEach { (key, method) ->
method.signatureGenerator.generate().let { sig -> method.signatureGenerator.generate().let { sig ->
if (method.isDetached) { if (method.isDetached) {
resultBuilder.addDetachedSignature(key, sig) resultBuilder.addDetachedSignature(SubkeyIdentifier(key), sig)
} }
if (!method.isDetached || options.isCleartextSigned) { if (!method.isDetached || options.isCleartextSigned) {
sig.encode(signatureLayerStream) sig.encode(signatureLayerStream)

View file

@ -7,19 +7,22 @@ package org.pgpainless.encryption_signing
import java.util.* import java.util.*
import org.bouncycastle.bcpg.KeyIdentifier import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.pgpainless.PGPainless.Companion.getPolicy import org.pgpainless.PGPainless.Companion.getPolicy
import org.pgpainless.PGPainless.Companion.inspectKeyRing import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.DocumentSignatureType import org.pgpainless.algorithm.DocumentSignatureType
import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.PublicKeyAlgorithm.Companion.requireFromId import org.pgpainless.algorithm.PublicKeyAlgorithm.Companion.requireFromId
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator.Companion.negotiateSignatureHashAlgorithm import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator.Companion.negotiateSignatureHashAlgorithm
import org.pgpainless.bouncycastle.extensions.unlock import org.pgpainless.bouncycastle.extensions.toOpenPGPKey
import org.pgpainless.exception.KeyException import org.pgpainless.exception.KeyException
import org.pgpainless.exception.KeyException.* import org.pgpainless.exception.KeyException.*
import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.OpenPgpFingerprint.Companion.of import org.pgpainless.key.OpenPgpFingerprint.Companion.of
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.key.protection.SecretKeyRingProtector
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
import org.pgpainless.policy.Policy import org.pgpainless.policy.Policy
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets.Callback import org.pgpainless.signature.subpackets.BaseSignatureSubpackets.Callback
import org.pgpainless.signature.subpackets.SignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpackets
@ -27,7 +30,7 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper
class SigningOptions { class SigningOptions {
val signingMethods: Map<SubkeyIdentifier, SigningMethod> = mutableMapOf() val signingMethods: Map<OpenPGPPrivateKey, SigningMethod> = mutableMapOf()
private var _hashAlgorithmOverride: HashAlgorithm? = null private var _hashAlgorithmOverride: HashAlgorithm? = null
private var _evaluationDate: Date = Date() private var _evaluationDate: Date = Date()
@ -62,17 +65,33 @@ class SigningOptions {
* Sign the message using an inline signature made by the provided signing key. * Sign the message using an inline signature made by the provided signing key.
* *
* @param signingKeyProtector protector to unlock the signing key * @param signingKeyProtector protector to unlock the signing key
* @param signingKey key ring containing the signing key * @param signingKey OpenPGPKey containing the signing (sub-)key.
* @return this * @return this
* @throws KeyException if something is wrong with the key * @throws KeyException if something is wrong with the key
* @throws PGPException if the key cannot be unlocked or a signing method cannot be created * @throws PGPException if the key cannot be unlocked or a signing method cannot be created
*/ */
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
fun addSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey
): SigningOptions = apply {
addInlineSignature(
signingKeyProtector, signingKey, null, DocumentSignatureType.BINARY_DOCUMENT)
}
/**
* Sign the message using an inline signature made by the provided signing key.
*
* @param signingKeyProtector protector to unlock the signing key
* @param signingKey key ring containing the signing key
* @return this
* @throws KeyException if something is wrong with the key
* @throws PGPException if the key cannot be unlocked or a signing method cannot be created
*/
@Deprecated("Pass an OpenPGPKey instead.")
@Throws(KeyException::class, PGPException::class)
fun addSignature(signingKeyProtector: SecretKeyRingProtector, signingKey: PGPSecretKeyRing) = fun addSignature(signingKeyProtector: SecretKeyRingProtector, signingKey: PGPSecretKeyRing) =
apply { addSignature(signingKeyProtector, signingKey.toOpenPGPKey())
addInlineSignature(
signingKeyProtector, signingKey, null, DocumentSignatureType.BINARY_DOCUMENT)
}
/** /**
* Add inline signatures with all secret key rings in the provided secret key ring collection. * Add inline signatures with all secret key rings in the provided secret key ring collection.
@ -86,6 +105,7 @@ class SigningOptions {
* created * created
*/ */
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
@Deprecated("Repeatedly call addInlineSignature(), passing an OpenPGPKey instead.")
fun addInlineSignatures( fun addInlineSignatures(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
signingKeys: Iterable<PGPSecretKeyRing>, signingKeys: Iterable<PGPSecretKeyRing>,
@ -94,6 +114,12 @@ class SigningOptions {
signingKeys.forEach { addInlineSignature(signingKeyProtector, it, null, signatureType) } signingKeys.forEach { addInlineSignature(signingKeyProtector, it, null, signatureType) }
} }
fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT
): SigningOptions = addInlineSignature(signingKeyProtector, signingKey, null, signatureType)
/** /**
* Add an inline-signature. Inline signatures are being embedded into the message itself and can * Add an inline-signature. Inline signatures are being embedded into the message itself and can
* be processed in one pass, thanks to the use of one-pass-signature packets. * be processed in one pass, thanks to the use of one-pass-signature packets.
@ -106,11 +132,49 @@ class SigningOptions {
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created * @throws PGPException if the key cannot be unlocked or the signing method cannot be created
*/ */
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
@Deprecated("Pass an OpenPGPKey instead.")
fun addInlineSignature( fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
signingKey: PGPSecretKeyRing, signingKey: PGPSecretKeyRing,
signatureType: DocumentSignatureType signatureType: DocumentSignatureType
) = apply { addInlineSignature(signingKeyProtector, signingKey, null, signatureType) } ) = addInlineSignature(signingKeyProtector, signingKey.toOpenPGPKey(), signatureType)
fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey,
userId: CharSequence? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null
) = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
throw UnboundUserIdException(
of(signingKey.pgpSecretKeyRing),
userId.toString(),
keyRingInfo.getLatestUserIdCertification(userId),
keyRingInfo.getUserIdRevocation(userId))
}
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey.pgpSecretKeyRing))
}
for (signingPubKey in signingPubKeys) {
val signingSecKey: OpenPGPSecretKey =
signingKey.getSecretKey(signingPubKey)
?: throw MissingSecretKeyException(
of(signingKey.pgpSecretKeyRing), signingPubKey.keyIdentifier.keyId)
val signingPrivKey: OpenPGPPrivateKey =
unlockSecretKey(signingSecKey, signingKeyProtector)
val hashAlgorithms =
if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId)
else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingPrivKey, hashAlgorithm, signatureType, false, subpacketsCallback)
}
}
/** /**
* Add an inline-signature. Inline signatures are being embedded into the message itself and can * Add an inline-signature. Inline signatures are being embedded into the message itself and can
@ -129,6 +193,7 @@ class SigningOptions {
* @throws KeyException if the key is invalid * @throws KeyException if the key is invalid
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created * @throws PGPException if the key cannot be unlocked or the signing method cannot be created
*/ */
@Deprecated("Pass an OpenPGPKey instead.")
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
@JvmOverloads @JvmOverloads
fun addInlineSignature( fun addInlineSignature(
@ -137,34 +202,36 @@ class SigningOptions {
userId: CharSequence? = null, userId: CharSequence? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null subpacketsCallback: Callback? = null
) = apply { ) =
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) addInlineSignature(
if (userId != null && !keyRingInfo.isUserIdValid(userId)) { signingKeyProtector,
throw UnboundUserIdException( signingKey.toOpenPGPKey(),
of(signingKey), userId,
userId.toString(), signatureType,
keyRingInfo.getLatestUserIdCertification(userId), subpacketsCallback)
keyRingInfo.getUserIdRevocation(userId))
}
fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPSecretKey,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null
): SigningOptions = apply {
val openPGPKey = signingKey.openPGPKey
val keyRingInfo = inspectKeyRing(openPGPKey, evaluationDate)
val signingPubKeys = keyRingInfo.signingSubkeys val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) { if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey)) throw UnacceptableSigningKeyException(of(openPGPKey.pgpSecretKeyRing))
} }
for (signingPubKey in signingPubKeys) { if (!signingPubKeys.any { it.keyIdentifier.matches(signingKey.keyIdentifier) }) {
val signingSecKey: PGPSecretKey = throw MissingSecretKeyException(
signingKey.getSecretKey(signingPubKey.keyIdentifier) of(openPGPKey.pgpSecretKeyRing), signingKey.keyIdentifier.keyId)
?: throw MissingSecretKeyException(
of(signingKey), signingPubKey.keyIdentifier.keyId)
val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector)
val hashAlgorithms =
if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId)
else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback)
} }
val signingPrivKey = unlockSecretKey(signingKey, signingKeyProtector)
val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(signingPrivKey, hashAlgorithm, signatureType, false, subpacketsCallback)
} }
/** /**
@ -191,31 +258,13 @@ class SigningOptions {
keyId: Long, keyId: Long,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null subpacketsCallback: Callback? = null
) = apply { ) =
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) addInlineSignature(
val signingPubKeys = keyRingInfo.signingSubkeys signingKeyProtector,
if (signingPubKeys.isEmpty()) { signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId))
throw UnacceptableSigningKeyException(of(signingKey)) ?: throw MissingSecretKeyException(of(signingKey), keyId),
} signatureType,
subpacketsCallback)
for (signingPubKey in signingPubKeys) {
if (!signingPubKey.keyIdentifier.matches(KeyIdentifier(keyId))) {
continue
}
val signingSecKey =
signingKey.getSecretKey(signingPubKey.keyIdentifier)
?: throw MissingSecretKeyException(
of(signingKey), signingPubKey.keyIdentifier.keyId)
val signingSubkey = signingSecKey.unlock(signingKeyProtector)
val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback)
return this
}
throw MissingSecretKeyException(of(signingKey), keyId)
}
/** /**
* Add detached signatures with all key rings from the provided secret key ring collection. * Add detached signatures with all key rings from the provided secret key ring collection.
@ -229,6 +278,7 @@ class SigningOptions {
* method cannot be created * method cannot be created
*/ */
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
@Deprecated("Repeatedly call addDetachedSignature(), passing an OpenPGPKey instead.")
fun addDetachedSignatures( fun addDetachedSignatures(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
signingKeys: Iterable<PGPSecretKeyRing>, signingKeys: Iterable<PGPSecretKeyRing>,
@ -237,6 +287,12 @@ class SigningOptions {
signingKeys.forEach { addDetachedSignature(signingKeyProtector, it, null, signatureType) } signingKeys.forEach { addDetachedSignature(signingKeyProtector, it, null, signatureType) }
} }
fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT
): SigningOptions = addDetachedSignature(signingKeyProtector, signingKey, null, signatureType)
/** /**
* Create a detached signature. Detached signatures are not being added into the PGP message * Create a detached signature. Detached signatures are not being added into the PGP message
* itself. Instead, they can be distributed separately to the message. Detached signatures are * itself. Instead, they can be distributed separately to the message. Detached signatures are
@ -250,6 +306,7 @@ class SigningOptions {
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method * @throws PGPException if the key cannot be validated or unlocked, or if no signature method
* can be created * can be created
*/ */
@Deprecated("Pass an OpenPGPKey instead.")
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
fun addDetachedSignature( fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
@ -257,6 +314,37 @@ class SigningOptions {
signatureType: DocumentSignatureType signatureType: DocumentSignatureType
) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) } ) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) }
fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPKey,
userId: CharSequence? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null
): SigningOptions = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
throw UnboundUserIdException(
of(signingKey.pgpSecretKeyRing),
userId.toString(),
keyRingInfo.getLatestUserIdCertification(userId),
keyRingInfo.getUserIdRevocation(userId))
}
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey.pgpSecretKeyRing))
}
for (signingPubKey in signingPubKeys) {
val signingSecKey: OpenPGPSecretKey =
signingKey.getSecretKey(signingPubKey.keyIdentifier)
?: throw MissingSecretKeyException(
of(signingKey.pgpSecretKeyRing), signingPubKey.keyIdentifier.keyId)
addDetachedSignature(
signingKeyProtector, signingSecKey, userId, signatureType, subpacketCallback)
}
}
/** /**
* Create a detached signature. Detached signatures are not being added into the PGP message * Create a detached signature. Detached signatures are not being added into the PGP message
* itself. Instead, they can be distributed separately to the message. Detached signatures are * itself. Instead, they can be distributed separately to the message. Detached signatures are
@ -275,6 +363,7 @@ class SigningOptions {
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method * @throws PGPException if the key cannot be validated or unlocked, or if no signature method
* can be created * can be created
*/ */
@Deprecated("Pass an OpenPGPKey instead.")
@JvmOverloads @JvmOverloads
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
fun addDetachedSignature( fun addDetachedSignature(
@ -283,34 +372,28 @@ class SigningOptions {
userId: String? = null, userId: String? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null subpacketCallback: Callback? = null
) = apply { ) =
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) addDetachedSignature(
if (userId != null && !keyRingInfo.isUserIdValid(userId)) { signingKeyProtector,
throw UnboundUserIdException( signingKey.toOpenPGPKey(),
of(signingKey), userId,
userId.toString(), signatureType,
keyRingInfo.getLatestUserIdCertification(userId), subpacketCallback)
keyRingInfo.getUserIdRevocation(userId))
}
val signingPubKeys = keyRingInfo.signingSubkeys fun addDetachedSignature(
if (signingPubKeys.isEmpty()) { signingKeyProtector: SecretKeyRingProtector,
throw UnacceptableSigningKeyException(of(signingKey)) signingKey: OpenPGPSecretKey,
} userId: CharSequence? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
for (signingPubKey in signingPubKeys) { subpacketCallback: Callback? = null
val signingSecKey: PGPSecretKey = ): SigningOptions = apply {
signingKey.getSecretKey(signingPubKey.keyIdentifier) val keyRingInfo = inspectKeyRing(signingKey.openPGPKey, evaluationDate)
?: throw MissingSecretKeyException( val signingPrivKey: OpenPGPPrivateKey = signingKey.unlock(signingKeyProtector)
of(signingKey), signingPubKey.keyIdentifier.keyId) val hashAlgorithms =
val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId)
val hashAlgorithms = else keyRingInfo.getPreferredHashAlgorithms(signingKey.keyIdentifier)
if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier) addSigningMethod(signingPrivKey, hashAlgorithm, signatureType, true, subpacketCallback)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketCallback)
}
} }
/** /**
@ -331,65 +414,44 @@ class SigningOptions {
*/ */
@Throws(KeyException::class, PGPException::class) @Throws(KeyException::class, PGPException::class)
@JvmOverloads @JvmOverloads
@Deprecated("Pass an OpenPGPSecretKey instead.")
fun addDetachedSignature( fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector, signingKeyProtector: SecretKeyRingProtector,
signingKey: PGPSecretKeyRing, signingKey: PGPSecretKeyRing,
keyId: Long, keyId: Long,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null subpacketsCallback: Callback? = null
) = apply { ) =
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) addDetachedSignature(
signingKeyProtector,
val signingPubKeys = keyRingInfo.signingSubkeys signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId))
if (signingPubKeys.isEmpty()) { ?: throw MissingSecretKeyException(of(signingKey), keyId),
throw UnacceptableSigningKeyException(of(signingKey)) null,
} signatureType,
subpacketsCallback)
for (signingPubKey in signingPubKeys) {
if (signingPubKey.keyIdentifier.matches(KeyIdentifier(keyId))) {
val signingSecKey: PGPSecretKey =
signingKey.getSecretKey(signingPubKey.keyIdentifier)
?: throw MissingSecretKeyException(
of(signingKey), signingPubKey.keyIdentifier.keyId)
val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector)
val hashAlgorithms =
keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm =
negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingKey,
signingSubkey,
hashAlgorithm,
signatureType,
true,
subpacketsCallback)
return this
}
}
throw MissingSecretKeyException(of(signingKey), keyId)
}
private fun addSigningMethod( private fun addSigningMethod(
signingKey: PGPSecretKeyRing, signingKey: OpenPGPPrivateKey,
signingSubkey: PGPPrivateKey,
hashAlgorithm: HashAlgorithm, hashAlgorithm: HashAlgorithm,
signatureType: DocumentSignatureType, signatureType: DocumentSignatureType,
detached: Boolean, detached: Boolean,
subpacketCallback: Callback? = null subpacketCallback: Callback? = null
) { ) {
val signingKeyIdentifier = SubkeyIdentifier(signingKey, signingSubkey.keyID) val signingSecretKey: PGPSecretKey = signingKey.secretKey.pgpSecretKey
val signingSecretKey: PGPSecretKey = signingKey.getSecretKey(signingSubkey.keyID)
val publicKeyAlgorithm = requireFromId(signingSecretKey.publicKey.algorithm) val publicKeyAlgorithm = requireFromId(signingSecretKey.publicKey.algorithm)
val bitStrength = signingSecretKey.publicKey.bitStrength val bitStrength = signingSecretKey.publicKey.bitStrength
if (!getPolicy().publicKeyAlgorithmPolicy.isAcceptable(publicKeyAlgorithm, bitStrength)) { if (!getPolicy().publicKeyAlgorithmPolicy.isAcceptable(publicKeyAlgorithm, bitStrength)) {
throw UnacceptableSigningKeyException( throw UnacceptableSigningKeyException(
PublicKeyAlgorithmPolicyException( PublicKeyAlgorithmPolicyException(
of(signingKey), signingSecretKey.keyID, publicKeyAlgorithm, bitStrength)) of(signingKey.secretKey.pgpSecretKey),
signingSecretKey.keyID,
publicKeyAlgorithm,
bitStrength))
} }
val generator: PGPSignatureGenerator = val generator: PGPSignatureGenerator =
createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType) createSignatureGenerator(
signingKey.unlockedKey.privateKey, hashAlgorithm, signatureType)
// Subpackets // Subpackets
val hashedSubpackets = val hashedSubpackets =
@ -405,7 +467,7 @@ class SigningOptions {
val signingMethod = val signingMethod =
if (detached) SigningMethod.detachedSignature(generator, hashAlgorithm) if (detached) SigningMethod.detachedSignature(generator, hashAlgorithm)
else SigningMethod.inlineSignature(generator, hashAlgorithm) else SigningMethod.inlineSignature(generator, hashAlgorithm)
(signingMethods as MutableMap)[signingKeyIdentifier] = signingMethod (signingMethods as MutableMap)[signingKey] = signingMethod
} }
/** /**

View file

@ -8,6 +8,7 @@ import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
/** /**
* Tuple class used to identify a subkey by fingerprints of the primary key of the subkeys key ring, * Tuple class used to identify a subkey by fingerprints of the primary key of the subkeys key ring,
@ -32,6 +33,8 @@ class SubkeyIdentifier(
OpenPgpFingerprint.of(key.certificate.pgpPublicKeyRing), OpenPgpFingerprint.of(key.certificate.pgpPublicKeyRing),
OpenPgpFingerprint.of(key.pgpPublicKey)) OpenPgpFingerprint.of(key.pgpPublicKey))
constructor(key: OpenPGPPrivateKey) : this(key.secretKey)
constructor( constructor(
keys: PGPKeyRing, keys: PGPKeyRing,
subkeyFingerprint: OpenPgpFingerprint subkeyFingerprint: OpenPgpFingerprint

View file

@ -10,6 +10,8 @@ import openpgp.openPgpKeyId
import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPrivateKey import org.bouncycastle.openpgp.PGPPrivateKey
import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.bouncycastle.extensions.isEncrypted import org.pgpainless.bouncycastle.extensions.isEncrypted
@ -35,6 +37,36 @@ class UnlockSecretKey {
} }
} }
@JvmStatic
@Throws(PGPException::class)
fun unlockSecretKey(
secretKey: OpenPGPSecretKey,
protector: SecretKeyRingProtector
): OpenPGPPrivateKey {
val privateKey =
try {
secretKey.unlock(protector)
} catch (e: PGPException) {
throw WrongPassphraseException(secretKey.keyIdentifier, e)
}
if (privateKey == null) {
if (secretKey.pgpSecretKey.s2K.type in 100..110) {
throw PGPException(
"Cannot decrypt secret key ${secretKey.keyIdentifier}: \n" +
"Unsupported private S2K type ${secretKey.pgpSecretKey.s2K.type}")
}
throw PGPException("Cannot decrypt secret key.")
}
if (PGPainless.getPolicy().isEnableKeyParameterValidation()) {
PublicKeyParameterValidationUtil.verifyPublicKeyParameterIntegrity(
privateKey.unlockedKey.privateKey, privateKey.unlockedKey.publicKey)
}
return privateKey
}
@JvmStatic @JvmStatic
@Throws(PGPException::class) @Throws(PGPException::class)
fun unlockSecretKey( fun unlockSecretKey(