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.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.pgpainless.PGPainless
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
@ -73,12 +74,20 @@ class ConsumerOptions {
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.
*
* @param verificationCert certificate for signature verification
* @return options
*/
@Deprecated("Pass OpenPGPCertificate instead.")
fun addVerificationCert(verificationCert: PGPPublicKeyRing): ConsumerOptions = apply {
this.certificates.addCertificate(verificationCert)
}
@ -89,6 +98,7 @@ class ConsumerOptions {
* @param verificationCerts certificates for signature verification
* @return options
*/
@Deprecated("Use of methods taking PGPPublicKeyRingCollections is discouraged.")
fun addVerificationCerts(verificationCerts: PGPPublicKeyRingCollection): ConsumerOptions =
apply {
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.
*
@ -178,6 +196,7 @@ class ConsumerOptions {
* @return options
*/
@JvmOverloads
@Deprecated("Pass OpenPGPKey instead.")
fun addDecryptionKey(
key: PGPSecretKeyRing,
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys(),
@ -192,6 +211,7 @@ class ConsumerOptions {
* @return options
*/
@JvmOverloads
@Deprecated("Pass OpenPGPKey instances instead.")
fun addDecryptionKeys(
keys: PGPSecretKeyRingCollection,
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
* messages which were symmetrically encrypted for a passphrase.
@ -255,21 +260,21 @@ class ConsumerOptions {
*
* @return decryption keys
*/
fun getDecryptionKeys() = decryptionKeys.keys.toSet()
fun getDecryptionKeys(): Set<OpenPGPKey> = decryptionKeys.keys.toSet()
/**
* Return the set of available message 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 certificate source
*/
fun getCertificateSource() = certificates
fun getCertificateSource(): CertificateSource = certificates
/**
* Return the callback that gets called when a certificate for signature verification is
@ -277,7 +282,7 @@ class ConsumerOptions {
*
* @return missing public key callback
*/
fun getMissingCertificateCallback() = missingCertificateCallback
fun getMissingCertificateCallback(): MissingPublicKeyCallback? = missingCertificateCallback
/**
* Return the [SecretKeyRingProtector] for the given [PGPSecretKeyRing].
@ -321,7 +326,7 @@ class ConsumerOptions {
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
@ -337,7 +342,7 @@ class ConsumerOptions {
*
* @return true if non-OpenPGP data is forced
*/
fun isForceNonOpenPgpData() = forceNonOpenPgpData
fun isForceNonOpenPgpData(): Boolean = forceNonOpenPgpData
/**
* 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.api.OpenPGPCertificate
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.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
@ -58,6 +59,7 @@ import org.pgpainless.exception.UnacceptableAlgorithmException
import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.CertificateValidator
import org.pgpainless.signature.consumer.OnePassSignatureCheck
@ -420,10 +422,15 @@ class OpenPgpMessageInputStream(
continue
}
val privateKey = secretKey.unlock(protector)
val privateKey =
try {
unlockSecretKey(secretKey, protector)
} catch (e: PGPException) {
throw WrongPassphraseException(secretKey.keyIdentifier, e)
}
if (decryptWithPrivateKey(
esks,
privateKey,
privateKey.unlockedKey,
SubkeyIdentifier(
secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier),
pkesk)) {
@ -451,7 +458,7 @@ class OpenPgpMessageInputStream(
val privateKey = decryptionKey.unlock(protector)
if (decryptWithPrivateKey(
esks, privateKey, SubkeyIdentifier(decryptionKey), pkesk)) {
esks, privateKey.unlockedKey, SubkeyIdentifier(decryptionKey), pkesk)) {
return true
}
}
@ -476,13 +483,13 @@ class OpenPgpMessageInputStream(
LOGGER.debug(
"Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
val privateKey =
val privateKey: OpenPGPPrivateKey =
try {
secretKey.unlock(protector)
unlockSecretKey(secretKey, protector)
} catch (e: PGPException) {
throw WrongPassphraseException(secretKey.keyIdentifier, e)
}
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
if (decryptWithPrivateKey(esks, privateKey.unlockedKey, decryptionKeyId, pkesk)) {
return true
}
}

View file

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

View file

@ -7,19 +7,22 @@ package org.pgpainless.encryption_signing
import java.util.*
import org.bouncycastle.bcpg.KeyIdentifier
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.inspectKeyRing
import org.pgpainless.algorithm.DocumentSignatureType
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.PublicKeyAlgorithm.Companion.requireFromId
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.implementation.ImplementationFactory
import org.pgpainless.key.OpenPgpFingerprint.Companion.of
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.protection.SecretKeyRingProtector
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
import org.pgpainless.policy.Policy
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets.Callback
import org.pgpainless.signature.subpackets.SignatureSubpackets
@ -27,7 +30,7 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper
class SigningOptions {
val signingMethods: Map<SubkeyIdentifier, SigningMethod> = mutableMapOf()
val signingMethods: Map<OpenPGPPrivateKey, SigningMethod> = mutableMapOf()
private var _hashAlgorithmOverride: HashAlgorithm? = null
private var _evaluationDate: Date = Date()
@ -62,18 +65,34 @@ class SigningOptions {
* 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
* @param signingKey OpenPGPKey containing the signing (sub-)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
*/
@Throws(KeyException::class, PGPException::class)
fun addSignature(signingKeyProtector: SecretKeyRingProtector, signingKey: PGPSecretKeyRing) =
apply {
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) =
addSignature(signingKeyProtector, signingKey.toOpenPGPKey())
/**
* Add inline signatures with all secret key rings in the provided secret key ring collection.
*
@ -86,6 +105,7 @@ class SigningOptions {
* created
*/
@Throws(KeyException::class, PGPException::class)
@Deprecated("Repeatedly call addInlineSignature(), passing an OpenPGPKey instead.")
fun addInlineSignatures(
signingKeyProtector: SecretKeyRingProtector,
signingKeys: Iterable<PGPSecretKeyRing>,
@ -94,6 +114,12 @@ class SigningOptions {
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
* 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(KeyException::class, PGPException::class)
@Deprecated("Pass an OpenPGPKey instead.")
fun addInlineSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: PGPSecretKeyRing,
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
@ -129,6 +193,7 @@ class SigningOptions {
* @throws KeyException if the key is invalid
* @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)
@JvmOverloads
fun addInlineSignature(
@ -137,34 +202,36 @@ class SigningOptions {
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),
userId.toString(),
keyRingInfo.getLatestUserIdCertification(userId),
keyRingInfo.getUserIdRevocation(userId))
}
) =
addInlineSignature(
signingKeyProtector,
signingKey.toOpenPGPKey(),
userId,
signatureType,
subpacketsCallback)
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
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey))
throw UnacceptableSigningKeyException(of(openPGPKey.pgpSecretKeyRing))
}
for (signingPubKey in signingPubKeys) {
val signingSecKey: PGPSecretKey =
signingKey.getSecretKey(signingPubKey.keyIdentifier)
?: 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)
if (!signingPubKeys.any { it.keyIdentifier.matches(signingKey.keyIdentifier) }) {
throw MissingSecretKeyException(
of(openPGPKey.pgpSecretKeyRing), signingKey.keyIdentifier.keyId)
}
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,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null
) = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey))
}
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)
}
) =
addInlineSignature(
signingKeyProtector,
signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId))
?: throw MissingSecretKeyException(of(signingKey), keyId),
signatureType,
subpacketsCallback)
/**
* Add detached signatures with all key rings from the provided secret key ring collection.
@ -229,6 +278,7 @@ class SigningOptions {
* method cannot be created
*/
@Throws(KeyException::class, PGPException::class)
@Deprecated("Repeatedly call addDetachedSignature(), passing an OpenPGPKey instead.")
fun addDetachedSignatures(
signingKeyProtector: SecretKeyRingProtector,
signingKeys: Iterable<PGPSecretKeyRing>,
@ -237,6 +287,12 @@ class SigningOptions {
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
* 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
* can be created
*/
@Deprecated("Pass an OpenPGPKey instead.")
@Throws(KeyException::class, PGPException::class)
fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector,
@ -257,6 +314,37 @@ class SigningOptions {
signatureType: DocumentSignatureType
) = 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
* 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
* can be created
*/
@Deprecated("Pass an OpenPGPKey instead.")
@JvmOverloads
@Throws(KeyException::class, PGPException::class)
fun addDetachedSignature(
@ -283,34 +372,28 @@ class SigningOptions {
userId: String? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null
) = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
throw UnboundUserIdException(
of(signingKey),
userId.toString(),
keyRingInfo.getLatestUserIdCertification(userId),
keyRingInfo.getUserIdRevocation(userId))
}
) =
addDetachedSignature(
signingKeyProtector,
signingKey.toOpenPGPKey(),
userId,
signatureType,
subpacketCallback)
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey))
}
for (signingPubKey in signingPubKeys) {
val signingSecKey: PGPSecretKey =
signingKey.getSecretKey(signingPubKey.keyIdentifier)
?: throw MissingSecretKeyException(
of(signingKey), signingPubKey.keyIdentifier.keyId)
val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector)
fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: OpenPGPSecretKey,
userId: CharSequence? = null,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketCallback: Callback? = null
): SigningOptions = apply {
val keyRingInfo = inspectKeyRing(signingKey.openPGPKey, evaluationDate)
val signingPrivKey: OpenPGPPrivateKey = signingKey.unlock(signingKeyProtector)
val hashAlgorithms =
if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId)
else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyIdentifier)
else keyRingInfo.getPreferredHashAlgorithms(signingKey.keyIdentifier)
val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy())
addSigningMethod(
signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketCallback)
}
addSigningMethod(signingPrivKey, hashAlgorithm, signatureType, true, subpacketCallback)
}
/**
@ -331,65 +414,44 @@ class SigningOptions {
*/
@Throws(KeyException::class, PGPException::class)
@JvmOverloads
@Deprecated("Pass an OpenPGPSecretKey instead.")
fun addDetachedSignature(
signingKeyProtector: SecretKeyRingProtector,
signingKey: PGPSecretKeyRing,
keyId: Long,
signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT,
subpacketsCallback: Callback? = null
) = apply {
val keyRingInfo = inspectKeyRing(signingKey, evaluationDate)
val signingPubKeys = keyRingInfo.signingSubkeys
if (signingPubKeys.isEmpty()) {
throw UnacceptableSigningKeyException(of(signingKey))
}
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,
) =
addDetachedSignature(
signingKeyProtector,
signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId))
?: throw MissingSecretKeyException(of(signingKey), keyId),
null,
signatureType,
true,
subpacketsCallback)
return this
}
}
throw MissingSecretKeyException(of(signingKey), keyId)
}
private fun addSigningMethod(
signingKey: PGPSecretKeyRing,
signingSubkey: PGPPrivateKey,
signingKey: OpenPGPPrivateKey,
hashAlgorithm: HashAlgorithm,
signatureType: DocumentSignatureType,
detached: Boolean,
subpacketCallback: Callback? = null
) {
val signingKeyIdentifier = SubkeyIdentifier(signingKey, signingSubkey.keyID)
val signingSecretKey: PGPSecretKey = signingKey.getSecretKey(signingSubkey.keyID)
val signingSecretKey: PGPSecretKey = signingKey.secretKey.pgpSecretKey
val publicKeyAlgorithm = requireFromId(signingSecretKey.publicKey.algorithm)
val bitStrength = signingSecretKey.publicKey.bitStrength
if (!getPolicy().publicKeyAlgorithmPolicy.isAcceptable(publicKeyAlgorithm, bitStrength)) {
throw UnacceptableSigningKeyException(
PublicKeyAlgorithmPolicyException(
of(signingKey), signingSecretKey.keyID, publicKeyAlgorithm, bitStrength))
of(signingKey.secretKey.pgpSecretKey),
signingSecretKey.keyID,
publicKeyAlgorithm,
bitStrength))
}
val generator: PGPSignatureGenerator =
createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType)
createSignatureGenerator(
signingKey.unlockedKey.privateKey, hashAlgorithm, signatureType)
// Subpackets
val hashedSubpackets =
@ -405,7 +467,7 @@ class SigningOptions {
val signingMethod =
if (detached) SigningMethod.detachedSignature(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.PGPPublicKey
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,
@ -32,6 +33,8 @@ class SubkeyIdentifier(
OpenPgpFingerprint.of(key.certificate.pgpPublicKeyRing),
OpenPgpFingerprint.of(key.pgpPublicKey))
constructor(key: OpenPGPPrivateKey) : this(key.secretKey)
constructor(
keys: PGPKeyRing,
subkeyFingerprint: OpenPgpFingerprint

View file

@ -10,6 +10,8 @@ import openpgp.openPgpKeyId
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPrivateKey
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.pgpainless.PGPainless
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
@Throws(PGPException::class)
fun unlockSecretKey(