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

Improve API for signatures in results

This commit is contained in:
Paul Schaub 2025-04-07 16:03:01 +02:00
parent 654756c919
commit d1861e51cd
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
9 changed files with 91 additions and 50 deletions

View file

@ -15,8 +15,8 @@ import java.nio.charset.StandardCharsets;
import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.CompressionAlgorithm;
@ -350,12 +350,13 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest {
@Test @Test
public void createMalformedMessage() throws IOException, PGPException { public void createMalformedMessage() throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
String msg = "Hello, World!\n"; String msg = "Hello, World!\n";
PGPSecretKeyRing key = PGPainless.readKeyRing().secretKeyRing(KEY_2); OpenPGPKey key = api.readKey().parseKey(KEY_2);
ByteArrayOutputStream ciphertext = new ByteArrayOutputStream(); ByteArrayOutputStream ciphertext = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() EncryptionStream encryptionStream = api.generateMessage()
.onOutputStream(ciphertext) .onOutputStream(ciphertext)
.withOptions(ProducerOptions.sign(SigningOptions.get() .withOptions(ProducerOptions.sign(SigningOptions.get(api)
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key) .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key)
).overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED) ).overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
.setAsciiArmor(false)); .setAsciiArmor(false));

View file

@ -152,7 +152,9 @@ class PGPainless(
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
@Deprecated("Call buildKey() on an instance of PGPainless instead.") @Deprecated(
"Call buildKey() on an instance of PGPainless instead.",
replaceWith = ReplaceWith("buildKey(version)"))
fun buildKeyRing(version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4): KeyRingBuilder = fun buildKeyRing(version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4): KeyRingBuilder =
getInstance().buildKey(version) getInstance().buildKey(version)
@ -186,7 +188,8 @@ class PGPainless(
* @throws PGPException in case of an error * @throws PGPException in case of an error
*/ */
@JvmStatic @JvmStatic
@Deprecated("Use mergeCertificate() instead.") @Deprecated(
"Use mergeCertificate() instead.", replaceWith = ReplaceWith("mergeCertificate()"))
fun mergeCertificate( fun mergeCertificate(
originalCopy: PGPPublicKeyRing, originalCopy: PGPPublicKeyRing,
updatedCopy: PGPPublicKeyRing updatedCopy: PGPPublicKeyRing

View file

@ -779,34 +779,37 @@ class OpenPgpMessageInputStream(
fun addDetachedSignature(signature: PGPSignature) { fun addDetachedSignature(signature: PGPSignature) {
val check = initializeSignature(signature) val check = initializeSignature(signature)
val keyId = signature.issuerKeyId val keyId = signature.issuerKeyId
if (check != null) { if (check.issuer != null) {
detachedSignatures.add(check) detachedSignatures.add(check)
} else { } else {
LOGGER.debug( LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
detachedSignaturesWithMissingCert.add( detachedSignaturesWithMissingCert.add(
SignatureVerification.Failure( SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key."))) check, SignatureValidationException("Missing verification key.")))
} }
} }
fun addPrependedSignature(signature: PGPSignature) { fun addPrependedSignature(signature: PGPSignature) {
val check = initializeSignature(signature) val check = initializeSignature(signature)
val keyId = signature.issuerKeyId val keyId = signature.issuerKeyId
if (check != null) { if (check.issuer != null) {
prependedSignatures.add(check) prependedSignatures.add(check)
} else { } else {
LOGGER.debug( LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
prependedSignaturesWithMissingCert.add( prependedSignaturesWithMissingCert.add(
SignatureVerification.Failure( SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key"))) check, SignatureValidationException("Missing verification key")))
} }
} }
fun initializeSignature(signature: PGPSignature): OpenPGPDocumentSignature? { fun initializeSignature(signature: PGPSignature): OpenPGPDocumentSignature {
val certificate = findCertificate(signature) ?: return null val certificate =
val publicKey = certificate.getSigningKeyFor(signature) ?: return null findCertificate(signature) ?: return OpenPGPDocumentSignature(signature, null)
val publicKey =
certificate.getSigningKeyFor(signature)
?: return OpenPGPDocumentSignature(signature, null)
initialize(signature, publicKey.pgpPublicKey) initialize(signature, publicKey.pgpPublicKey)
return OpenPGPDocumentSignature(signature, publicKey) return OpenPGPDocumentSignature(signature, publicKey)
} }
@ -845,12 +848,7 @@ class OpenPgpMessageInputStream(
val documentSignature = val documentSignature =
OpenPGPDocumentSignature( OpenPGPDocumentSignature(
signature, check.verificationKeys.getSigningKeyFor(signature)) signature, check.verificationKeys.getSigningKeyFor(signature))
val verification = val verification = SignatureVerification(documentSignature)
SignatureVerification(
signature,
SubkeyIdentifier(
check.verificationKeys.pgpPublicKeyRing,
check.onePassSignature.keyIdentifier))
try { try {
signature.assertCreatedInBounds( signature.assertCreatedInBounds(
@ -879,7 +877,8 @@ class OpenPgpMessageInputStream(
"No suitable certificate for verification of signature by key ${signature.issuerKeyId.openPgpKeyId()} found.") "No suitable certificate for verification of signature by key ${signature.issuerKeyId.openPgpKeyId()} found.")
inbandSignaturesWithMissingCert.add( inbandSignaturesWithMissingCert.add(
SignatureVerification.Failure( SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key."))) OpenPGPDocumentSignature(signature, null),
SignatureValidationException("Missing verification key.")))
} }
} }
@ -967,8 +966,7 @@ class OpenPgpMessageInputStream(
fun finish(layer: Layer) { fun finish(layer: Layer) {
for (detached in detachedSignatures) { for (detached in detachedSignatures) {
val verification = val verification = SignatureVerification(detached)
SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer))
try { try {
detached.signature.assertCreatedInBounds( detached.signature.assertCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter()) options.getVerifyNotBefore(), options.getVerifyNotAfter())
@ -988,8 +986,7 @@ class OpenPgpMessageInputStream(
} }
for (prepended in prependedSignatures) { for (prepended in prependedSignatures) {
val verification = val verification = SignatureVerification(prepended)
SignatureVerification(prepended.signature, SubkeyIdentifier(prepended.issuer))
try { try {
prepended.signature.assertCreatedInBounds( prepended.signature.assertCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter()) options.getVerifyNotBefore(), options.getVerifyNotAfter())

View file

@ -5,6 +5,7 @@
package org.pgpainless.decryption_verification package org.pgpainless.decryption_verification
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.pgpainless.decryption_verification.SignatureVerification.Failure import org.pgpainless.decryption_verification.SignatureVerification.Failure
import org.pgpainless.exception.SignatureValidationException import org.pgpainless.exception.SignatureValidationException
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
@ -20,7 +21,10 @@ import org.pgpainless.signature.SignatureUtils
* @param signingKey [SubkeyIdentifier] of the (sub-) key that is used for signature verification. * @param signingKey [SubkeyIdentifier] of the (sub-) key that is used for signature verification.
* Note, that this might be null, e.g. in case of a [Failure] due to missing verification key. * Note, that this might be null, e.g. in case of a [Failure] due to missing verification key.
*/ */
data class SignatureVerification(val signature: PGPSignature, val signingKey: SubkeyIdentifier) { data class SignatureVerification(val documentSignature: OpenPGPDocumentSignature) {
val signature: PGPSignature = documentSignature.signature
val signingKey: SubkeyIdentifier = SubkeyIdentifier(documentSignature.issuer)
override fun toString(): String { override fun toString(): String {
return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)};" + return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)};" +
@ -36,15 +40,16 @@ data class SignatureVerification(val signature: PGPSignature, val signingKey: Su
* @param validationException exception that caused the verification to fail * @param validationException exception that caused the verification to fail
*/ */
data class Failure( data class Failure(
val signature: PGPSignature, val documentSignature: OpenPGPDocumentSignature,
val signingKey: SubkeyIdentifier?,
val validationException: SignatureValidationException val validationException: SignatureValidationException
) { ) {
val signature: PGPSignature = documentSignature.signature
val signingKey: SubkeyIdentifier? = documentSignature.issuer?.let { SubkeyIdentifier(it) }
constructor( constructor(
verification: SignatureVerification, verification: SignatureVerification,
validationException: SignatureValidationException validationException: SignatureValidationException
) : this(verification.signature, verification.signingKey, validationException) ) : this(verification.documentSignature, validationException)
override fun toString(): String { override fun toString(): String {
return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)}; Key: ${signingKey?.toString() ?: "null"}; Failure: ${validationException.message}" return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)}; Key: ${signingKey?.toString() ?: "null"}; Failure: ${validationException.message}"

View file

@ -9,6 +9,7 @@ import org.bouncycastle.openpgp.PGPLiteralData
import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPPublicKeyRing
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.OpenPGPSignature.OpenPGPDocumentSignature
import org.pgpainless.algorithm.CompressionAlgorithm 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
@ -19,13 +20,25 @@ import org.pgpainless.util.MultiMap
data class EncryptionResult( data class EncryptionResult(
val encryptionAlgorithm: SymmetricKeyAlgorithm, val encryptionAlgorithm: SymmetricKeyAlgorithm,
val compressionAlgorithm: CompressionAlgorithm, val compressionAlgorithm: CompressionAlgorithm,
val detachedSignatures: MultiMap<SubkeyIdentifier, PGPSignature>, val detachedDocumentSignatures: OpenPGPSignatureSet<OpenPGPDocumentSignature>,
val recipients: Set<SubkeyIdentifier>, val recipients: Set<SubkeyIdentifier>,
val fileName: String, val fileName: String,
val modificationDate: Date, val modificationDate: Date,
val fileEncoding: StreamEncoding val fileEncoding: StreamEncoding
) { ) {
@Deprecated(
"Use detachedSignatures instead", replaceWith = ReplaceWith("detachedDocumentSignatures"))
// TODO: Remove in 2.1
val detachedSignatures: MultiMap<SubkeyIdentifier, PGPSignature>
get() {
val map = MultiMap<SubkeyIdentifier, PGPSignature>()
detachedDocumentSignatures.signatures
.map { SubkeyIdentifier(it.issuer) to it.signature }
.forEach { map.put(it.first, it.second) }
return map
}
/** /**
* Return true, if the message is marked as for-your-eyes-only. This is typically done by * Return true, if the message is marked as for-your-eyes-only. This is typically done by
* setting the filename "_CONSOLE". * setting the filename "_CONSOLE".
@ -59,7 +72,7 @@ data class EncryptionResult(
var _encryptionAlgorithm: SymmetricKeyAlgorithm? = null var _encryptionAlgorithm: SymmetricKeyAlgorithm? = null
var _compressionAlgorithm: CompressionAlgorithm? = null var _compressionAlgorithm: CompressionAlgorithm? = null
val detachedSignatures: MultiMap<SubkeyIdentifier, PGPSignature> = MultiMap() val detachedSignatures: MutableList<OpenPGPDocumentSignature> = mutableListOf()
val recipients: Set<SubkeyIdentifier> = mutableSetOf() val recipients: Set<SubkeyIdentifier> = mutableSetOf()
private var _fileName = "" private var _fileName = ""
private var _modificationDate = Date(0) private var _modificationDate = Date(0)
@ -85,10 +98,9 @@ data class EncryptionResult(
(recipients as MutableSet).add(recipient) (recipients as MutableSet).add(recipient)
} }
fun addDetachedSignature( fun addDetachedSignature(signature: OpenPGPDocumentSignature): Builder = apply {
signingSubkeyIdentifier: SubkeyIdentifier, detachedSignatures.add(signature)
detachedSignature: PGPSignature }
) = apply { detachedSignatures.put(signingSubkeyIdentifier, detachedSignature) }
fun build(): EncryptionResult { fun build(): EncryptionResult {
checkNotNull(_encryptionAlgorithm) { "Encryption algorithm not set." } checkNotNull(_encryptionAlgorithm) { "Encryption algorithm not set." }
@ -97,7 +109,7 @@ data class EncryptionResult(
return EncryptionResult( return EncryptionResult(
_encryptionAlgorithm!!, _encryptionAlgorithm!!,
_compressionAlgorithm!!, _compressionAlgorithm!!,
detachedSignatures, OpenPGPSignatureSet(detachedSignatures),
recipients, recipients,
_fileName, _fileName,
_modificationDate, _modificationDate,

View file

@ -13,11 +13,11 @@ import org.bouncycastle.openpgp.PGPCompressedDataGenerator
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator import org.bouncycastle.openpgp.PGPEncryptedDataGenerator
import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPLiteralDataGenerator import org.bouncycastle.openpgp.PGPLiteralDataGenerator
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm 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.key.SubkeyIdentifier
import org.pgpainless.util.ArmoredOutputStreamFactory import org.pgpainless.util.ArmoredOutputStreamFactory
// 1 << 8 causes wrong partial body length encoding // 1 << 8 causes wrong partial body length encoding
@ -246,8 +246,9 @@ 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 ->
val documentSignature = OpenPGPDocumentSignature(sig, key.publicKey)
if (method.isDetached) { if (method.isDetached) {
resultBuilder.addDetachedSignature(SubkeyIdentifier(key), sig) resultBuilder.addDetachedSignature(documentSignature)
} }
if (!method.isDetached || options.isCleartextSigned) { if (!method.isDetached || options.isCleartextSigned) {
sig.encode(signatureLayerStream) sig.encode(signatureLayerStream)

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.encryption_signing
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPSignature
class OpenPGPSignatureSet<S : OpenPGPSignature>(val signatures: List<S>) : Iterable<S> {
fun getSignaturesBy(cert: OpenPGPCertificate): List<S> =
signatures.filter { sig -> sig.signature.keyIdentifiers.any { cert.getKey(it) != null } }
fun getSignaturesBy(componentKey: OpenPGPCertificate.OpenPGPComponentKey): List<S> =
signatures.filter { sig ->
sig.signature.keyIdentifiers.any { componentKey.keyIdentifier.matches(it) }
}
override fun iterator(): Iterator<S> {
return signatures.iterator()
}
}

View file

@ -14,9 +14,9 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
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.OpenPGPSignature;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -26,7 +26,6 @@ import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions; import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions; import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.util.ArmorUtils; import org.pgpainless.util.ArmorUtils;
@ -70,7 +69,7 @@ public class Sign {
/** /**
* Demonstration of how to create a detached signature for a message. * Demonstration of how to create a detached signature for a message.
* A detached signature can be distributed alongside the message/file itself. * A detached signature can be distributed alongside the message/file itself.
* * <p>
* The message/file doesn't need to be altered for detached signature creation. * The message/file doesn't need to be altered for detached signature creation.
*/ */
@Test @Test
@ -82,9 +81,9 @@ public class Sign {
// After signing, you want to distribute the original value of 'message' along with the 'detachedSignature' // After signing, you want to distribute the original value of 'message' along with the 'detachedSignature'
// from below. // from below.
ByteArrayOutputStream ignoreMe = new ByteArrayOutputStream(); ByteArrayOutputStream ignoreMe = new ByteArrayOutputStream();
EncryptionStream signingStream = PGPainless.encryptAndOrSign() EncryptionStream signingStream = api.generateMessage()
.onOutputStream(ignoreMe) .onOutputStream(ignoreMe)
.withOptions(ProducerOptions.sign(SigningOptions.get() .withOptions(ProducerOptions.sign(SigningOptions.get(api)
.addDetachedSignature(protector, key, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)) .addDetachedSignature(protector, key, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT))
.setAsciiArmor(false) .setAsciiArmor(false)
); );
@ -94,9 +93,9 @@ public class Sign {
EncryptionResult result = signingStream.getResult(); EncryptionResult result = signingStream.getResult();
OpenPGPCertificate.OpenPGPComponentKey signingKey = PGPainless.inspectKeyRing(key).getSigningSubkeys().get(0); OpenPGPCertificate.OpenPGPComponentKey signingKey = api.inspect(key).getSigningSubkeys().get(0);
PGPSignature signature = result.getDetachedSignatures().get(new SubkeyIdentifier(signingKey)).iterator().next(); OpenPGPSignature.OpenPGPDocumentSignature signature = result.getDetachedDocumentSignatures().getSignaturesBy(signingKey).get(0);
String detachedSignature = ArmorUtils.toAsciiArmoredString(signature.getEncoded()); String detachedSignature = ArmorUtils.toAsciiArmoredString(signature.getSignature().getEncoded());
assertTrue(detachedSignature.startsWith("-----BEGIN PGP SIGNATURE-----")); assertTrue(detachedSignature.startsWith("-----BEGIN PGP SIGNATURE-----"));
@ -126,9 +125,9 @@ public class Sign {
"limitations under the License."; "limitations under the License.";
InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)); InputStream messageIn = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream signedOut = new ByteArrayOutputStream(); ByteArrayOutputStream signedOut = new ByteArrayOutputStream();
EncryptionStream signingStream = PGPainless.encryptAndOrSign() EncryptionStream signingStream = api.generateMessage()
.onOutputStream(signedOut) .onOutputStream(signedOut)
.withOptions(ProducerOptions.sign(SigningOptions.get() .withOptions(ProducerOptions.sign(SigningOptions.get(api)
.addDetachedSignature(protector, key, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)) // Human-readable text document .addDetachedSignature(protector, key, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)) // Human-readable text document
.setCleartextSigned() // <- Explicitly use Cleartext Signature Framework!!! .setCleartextSigned() // <- Explicitly use Cleartext Signature Framework!!!
); );

View file

@ -73,16 +73,16 @@ class DetachedSignImpl(private val api: PGPainless) : DetachedSign {
// forget passphrases // forget passphrases
protector.clear() protector.clear()
val signatures = result.detachedSignatures.map { it.value }.flatten() val signatures = result.detachedDocumentSignatures
val out = val out =
if (armor) ArmoredOutputStreamFactory.get(outputStream) else outputStream if (armor) ArmoredOutputStreamFactory.get(outputStream) else outputStream
signatures.forEach { it.encode(out) } signatures.forEach { it.signature.encode(out) }
out.close() out.close()
outputStream.close() outputStream.close()
return SigningResult.builder() return SigningResult.builder()
.setMicAlg(micAlgFromSignatures(signatures)) .setMicAlg(micAlgFromSignatures(signatures.map { it.signature }))
.build() .build()
} }
} }