mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 10:19:39 +02:00
More code cleanup
This commit is contained in:
parent
62f3a35c02
commit
1967483984
6 changed files with 51 additions and 74 deletions
|
@ -190,7 +190,7 @@ class PGPainless(
|
|||
*
|
||||
* @return builder
|
||||
*/
|
||||
@JvmStatic fun encryptAndOrSign() = EncryptionBuilder()
|
||||
@JvmStatic fun encryptAndOrSign() = EncryptionBuilder(getInstance())
|
||||
|
||||
/**
|
||||
* Create a [DecryptionBuilder], which can be used to decrypt and/or verify data using
|
||||
|
|
|
@ -5,65 +5,25 @@
|
|||
package org.pgpainless.encryption_signing
|
||||
|
||||
import java.io.OutputStream
|
||||
import org.pgpainless.PGPainless.Companion.getPolicy
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.util.NullOutputStream
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class EncryptionBuilder : EncryptionBuilderInterface {
|
||||
class EncryptionBuilder(private val api: PGPainless) : EncryptionBuilderInterface {
|
||||
override fun onOutputStream(
|
||||
outputStream: OutputStream
|
||||
): EncryptionBuilderInterface.WithOptions {
|
||||
return WithOptionsImpl(outputStream)
|
||||
return WithOptionsImpl(outputStream, api)
|
||||
}
|
||||
|
||||
override fun discardOutput(): EncryptionBuilderInterface.WithOptions {
|
||||
return onOutputStream(NullOutputStream())
|
||||
}
|
||||
|
||||
class WithOptionsImpl(val outputStream: OutputStream) : EncryptionBuilderInterface.WithOptions {
|
||||
class WithOptionsImpl(val outputStream: OutputStream, private val api: PGPainless) :
|
||||
EncryptionBuilderInterface.WithOptions {
|
||||
|
||||
override fun withOptions(options: ProducerOptions): EncryptionStream {
|
||||
return EncryptionStream(outputStream, options)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic val LOGGER: Logger = LoggerFactory.getLogger(EncryptionBuilder::class.java)
|
||||
|
||||
/**
|
||||
* Negotiate the [SymmetricKeyAlgorithm] used for message encryption.
|
||||
*
|
||||
* @param encryptionOptions encryption options
|
||||
* @return negotiated symmetric key algorithm
|
||||
*/
|
||||
@JvmStatic
|
||||
fun negotiateSymmetricEncryptionAlgorithm(
|
||||
encryptionOptions: EncryptionOptions
|
||||
): SymmetricKeyAlgorithm {
|
||||
val preferences =
|
||||
encryptionOptions.keyViews.values
|
||||
.map { it.preferredSymmetricKeyAlgorithms }
|
||||
.toList()
|
||||
val algorithm =
|
||||
byPopularity()
|
||||
.negotiate(
|
||||
getPolicy().symmetricKeyEncryptionAlgorithmPolicy,
|
||||
encryptionOptions.encryptionAlgorithmOverride,
|
||||
preferences)
|
||||
LOGGER.debug(
|
||||
"Negotiation resulted in {} being the symmetric encryption algorithm of choice.",
|
||||
algorithm)
|
||||
return algorithm
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun negotiateCompressionAlgorithm(producerOptions: ProducerOptions): CompressionAlgorithm {
|
||||
return producerOptions.compressionAlgorithmOverride
|
||||
return EncryptionStream(outputStream, options, api)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
|||
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
|
||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.PGPainless.Companion.inspectKeyRing
|
||||
import org.pgpainless.algorithm.EncryptionPurpose
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
|
||||
import org.pgpainless.authentication.CertificateAuthority
|
||||
import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate
|
||||
import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector
|
||||
import org.pgpainless.exception.KeyException.*
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
|
@ -22,7 +23,10 @@ import org.pgpainless.key.info.KeyAccessor
|
|||
import org.pgpainless.key.info.KeyRingInfo
|
||||
import org.pgpainless.util.Passphrase
|
||||
|
||||
class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
||||
class EncryptionOptions(
|
||||
private val purpose: EncryptionPurpose,
|
||||
private val api: PGPainless = PGPainless.getInstance()
|
||||
) {
|
||||
private val _encryptionMethods: MutableSet<PGPKeyEncryptionMethodGenerator> = mutableSetOf()
|
||||
private val _encryptionKeys: MutableSet<OpenPGPComponentKey> = mutableSetOf()
|
||||
private val _encryptionKeyIdentifiers: MutableSet<SubkeyIdentifier> = mutableSetOf()
|
||||
|
@ -87,7 +91,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
.lookupByUserId(userId, email, evaluationDate, targetAmount)
|
||||
.filter { it.isAuthenticated() }
|
||||
.forEach {
|
||||
addRecipient(it.certificate.toOpenPGPCertificate()).also { foundAcceptable = true }
|
||||
addRecipient(api.toCertificate(it.certificate)).also { foundAcceptable = true }
|
||||
}
|
||||
require(foundAcceptable) {
|
||||
"Could not identify any trust-worthy certificates for '$userId' and target trust amount $targetAmount."
|
||||
|
@ -225,7 +229,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
key: PGPPublicKeyRing,
|
||||
userId: CharSequence,
|
||||
encryptionKeySelector: EncryptionKeySelector
|
||||
) = addRecipient(key.toOpenPGPCertificate(), userId, encryptionKeySelector)
|
||||
) = addRecipient(api.toCertificate(key), userId, encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the given recipients [OpenPGPCertificate], filtering encryption
|
||||
|
@ -251,7 +255,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
replaceWith =
|
||||
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)"))
|
||||
fun addRecipient(key: PGPPublicKeyRing, encryptionKeySelector: EncryptionKeySelector) =
|
||||
addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)
|
||||
addRecipient(api.toCertificate(key), encryptionKeySelector)
|
||||
|
||||
/**
|
||||
* Encrypt the message for the recipients [OpenPGPCertificate], keeping the recipient anonymous
|
||||
|
@ -282,7 +286,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
fun addHiddenRecipient(
|
||||
key: PGPPublicKeyRing,
|
||||
selector: EncryptionKeySelector = encryptionKeySelector
|
||||
) = addHiddenRecipient(key.toOpenPGPCertificate(), selector)
|
||||
) = addHiddenRecipient(api.toCertificate(key), selector)
|
||||
|
||||
private fun addAsRecipient(
|
||||
cert: OpenPGPCertificate,
|
||||
|
@ -406,6 +410,17 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
|
|||
|
||||
fun hasEncryptionMethod() = _encryptionMethods.isNotEmpty()
|
||||
|
||||
internal fun negotiateSymmetricEncryptionAlgorithm(): SymmetricKeyAlgorithm {
|
||||
val preferences = keyViews.values.map { it.preferredSymmetricKeyAlgorithms }.toList()
|
||||
val algorithm =
|
||||
byPopularity()
|
||||
.negotiate(
|
||||
api.algorithmPolicy.symmetricKeyEncryptionAlgorithmPolicy,
|
||||
encryptionAlgorithmOverride,
|
||||
preferences)
|
||||
return algorithm
|
||||
}
|
||||
|
||||
fun interface EncryptionKeySelector {
|
||||
fun selectEncryptionSubkeys(
|
||||
encryptionCapableKeys: List<OpenPGPComponentKey>
|
||||
|
|
|
@ -13,13 +13,12 @@ import org.bouncycastle.openpgp.PGPCompressedDataGenerator
|
|||
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator
|
||||
import org.bouncycastle.openpgp.PGPException
|
||||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator
|
||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||
import org.pgpainless.PGPainless
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm
|
||||
import org.pgpainless.algorithm.StreamEncoding
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
|
||||
import org.pgpainless.key.SubkeyIdentifier
|
||||
import org.pgpainless.util.ArmoredOutputStreamFactory
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
// 1 << 8 causes wrong partial body length encoding
|
||||
// 1 << 9 fixes this.
|
||||
|
@ -38,6 +37,7 @@ const val BUFFER_SIZE = 1 shl 9
|
|||
class EncryptionStream(
|
||||
private var outermostStream: OutputStream,
|
||||
private val options: ProducerOptions,
|
||||
private val api: PGPainless
|
||||
) : OutputStream() {
|
||||
|
||||
private val resultBuilder: EncryptionResult.Builder = EncryptionResult.builder()
|
||||
|
@ -63,12 +63,10 @@ class EncryptionStream(
|
|||
|
||||
private fun prepareArmor() {
|
||||
if (!options.isAsciiArmor) {
|
||||
LOGGER.debug("Output will be unarmored.")
|
||||
return
|
||||
}
|
||||
|
||||
outermostStream = BufferedOutputStream(outermostStream)
|
||||
LOGGER.debug("Wrap encryption output in ASCII armor.")
|
||||
armorOutputStream =
|
||||
ArmoredOutputStreamFactory.get(outermostStream, options).also { outermostStream = it }
|
||||
}
|
||||
|
@ -84,14 +82,13 @@ class EncryptionStream(
|
|||
"If EncryptionOptions are provided, at least one encryption method MUST be provided as well."
|
||||
}
|
||||
|
||||
EncryptionBuilder.negotiateSymmetricEncryptionAlgorithm(options.encryptionOptions).let {
|
||||
options.encryptionOptions.negotiateSymmetricEncryptionAlgorithm().let {
|
||||
resultBuilder.setEncryptionAlgorithm(it)
|
||||
LOGGER.debug("Encrypt message using symmetric algorithm $it.")
|
||||
val encryptedDataGenerator =
|
||||
PGPEncryptedDataGenerator(
|
||||
OpenPGPImplementation.getInstance()
|
||||
.pgpDataEncryptorBuilder(it.algorithmId)
|
||||
.apply { setWithIntegrityPacket(true) })
|
||||
api.implementation.pgpDataEncryptorBuilder(it.algorithmId).apply {
|
||||
setWithIntegrityPacket(true)
|
||||
})
|
||||
options.encryptionOptions.encryptionMethods.forEach { m ->
|
||||
encryptedDataGenerator.addMethod(m)
|
||||
}
|
||||
|
@ -109,12 +106,11 @@ class EncryptionStream(
|
|||
|
||||
@Throws(IOException::class)
|
||||
private fun prepareCompression() {
|
||||
EncryptionBuilder.negotiateCompressionAlgorithm(options).let {
|
||||
options.negotiateCompressionAlgorithm().let {
|
||||
resultBuilder.setCompressionAlgorithm(it)
|
||||
compressedDataGenerator = PGPCompressedDataGenerator(it.algorithmId)
|
||||
if (it == CompressionAlgorithm.UNCOMPRESSED) return
|
||||
|
||||
LOGGER.debug("Compress using $it.")
|
||||
basicCompressionStream =
|
||||
BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)).also { stream ->
|
||||
outermostStream = stream
|
||||
|
@ -267,8 +263,4 @@ class EncryptionStream(
|
|||
|
||||
val isClosed
|
||||
get() = closed
|
||||
|
||||
companion object {
|
||||
@JvmStatic private val LOGGER = LoggerFactory.getLogger(EncryptionStream::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ import org.pgpainless.PGPainless
|
|||
import org.pgpainless.algorithm.CompressionAlgorithm
|
||||
import org.pgpainless.algorithm.StreamEncoding
|
||||
|
||||
class ProducerOptions
|
||||
private constructor(
|
||||
class ProducerOptions(
|
||||
val encryptionOptions: EncryptionOptions?,
|
||||
val signingOptions: SigningOptions?
|
||||
val signingOptions: SigningOptions?,
|
||||
val api: PGPainless = PGPainless.getInstance()
|
||||
) {
|
||||
|
||||
private var _fileName: String = ""
|
||||
|
@ -25,7 +25,7 @@ private constructor(
|
|||
var isDisableAsciiArmorCRC = false
|
||||
|
||||
private var _compressionAlgorithmOverride: CompressionAlgorithm =
|
||||
PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm
|
||||
api.algorithmPolicy.compressionAlgorithmPolicy.defaultCompressionAlgorithm
|
||||
private var asciiArmor = true
|
||||
private var _comment: String? = null
|
||||
private var _version: String? = null
|
||||
|
@ -104,6 +104,13 @@ private constructor(
|
|||
*/
|
||||
fun hasVersion() = version != null
|
||||
|
||||
/**
|
||||
* Configure the resulting OpenPGP message to make use of the Cleartext Signature Framework
|
||||
* (CSF). A CSF message MUST be signed using detached signatures only and MUST NOT be encrypted.
|
||||
*
|
||||
* @see
|
||||
* [RFC9580: OpenPGP - Cleartext Signature Framework](https://www.rfc-editor.org/rfc/rfc9580.html#name-cleartext-signature-framewo)
|
||||
*/
|
||||
fun setCleartextSigned() = apply {
|
||||
require(signingOptions != null) {
|
||||
"Signing Options cannot be null if cleartext signing is enabled."
|
||||
|
@ -230,6 +237,10 @@ private constructor(
|
|||
_hideArmorHeaders = hideArmorHeaders
|
||||
}
|
||||
|
||||
internal fun negotiateCompressionAlgorithm(): CompressionAlgorithm {
|
||||
return compressionAlgorithmOverride
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Sign and encrypt some data.
|
||||
|
|
|
@ -17,7 +17,6 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||
import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||
|
@ -51,7 +50,7 @@ public class TestTwoSubkeysEncryption {
|
|||
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
|
||||
.onOutputStream(out)
|
||||
.withOptions(
|
||||
ProducerOptions.encrypt(new EncryptionOptions(EncryptionPurpose.ANY)
|
||||
ProducerOptions.encrypt(EncryptionOptions.get()
|
||||
.addRecipient(publicKeys, EncryptionOptions.encryptToAllCapableSubkeys())
|
||||
)
|
||||
.setAsciiArmor(false)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue