diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index 74323d6b..602510cb 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -4,8 +4,12 @@ package org.pgpainless +import java.io.ByteArrayOutputStream import java.io.OutputStream import java.util.* +import org.bouncycastle.bcpg.ArmoredOutputStream +import org.bouncycastle.bcpg.BCPGOutputStream +import org.bouncycastle.bcpg.PacketFormat import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPSecretKeyRing @@ -16,6 +20,7 @@ import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator import org.bouncycastle.openpgp.api.OpenPGPKeyReader +import org.bouncycastle.openpgp.api.OpenPGPSignature import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.bouncycastle.PolicyAdapter @@ -59,6 +64,46 @@ class PGPainless( api = BcOpenPGPApi(implementation) } + @JvmOverloads + fun toAsciiArmor( + certOrKey: OpenPGPCertificate, + packetFormat: PacketFormat = PacketFormat.ROUNDTRIP + ): String { + val armorBuilder = ArmoredOutputStream.builder().clearHeaders() + ArmorUtils.keyToHeader(certOrKey.primaryKey.pgpPublicKey) + .getOrDefault(ArmorUtils.HEADER_COMMENT, setOf()) + .forEach { armorBuilder.addComment(it) } + return certOrKey.toAsciiArmoredString(packetFormat, armorBuilder) + } + + @JvmOverloads + fun toAsciiArmor( + signature: OpenPGPSignature, + packetFormat: PacketFormat = PacketFormat.ROUNDTRIP + ): String { + val armorBuilder = ArmoredOutputStream.builder().clearHeaders() + armorBuilder.addComment(signature.keyIdentifier.toPrettyPrint()) + return signature.toAsciiArmoredString(packetFormat, armorBuilder) + } + + @JvmOverloads + fun toAsciiArmor( + signature: PGPSignature, + packetFormat: PacketFormat = PacketFormat.ROUNDTRIP + ): String { + val armorBuilder = ArmoredOutputStream.builder().clearHeaders() + OpenPGPSignature.getMostExpressiveIdentifier(signature.keyIdentifiers)?.let { + armorBuilder.addComment(it.toPrettyPrint()) + } + val bOut = ByteArrayOutputStream() + val aOut = armorBuilder.build(bOut) + val pOut = BCPGOutputStream(aOut, packetFormat) + signature.encode(pOut) + pOut.close() + aOut.close() + return bOut.toString() + } + /** * Generate a new [OpenPGPKey] from predefined templates. * @@ -290,10 +335,13 @@ class PGPainless( */ @JvmStatic fun asciiArmor(key: PGPKeyRing): String = - if (key is PGPSecretKeyRing) ArmorUtils.toAsciiArmoredString(key) - else ArmorUtils.toAsciiArmoredString(key as PGPPublicKeyRing) + getInstance().toAsciiArmor(getInstance().toKeyOrCertificate(key)) - @JvmStatic fun asciiArmor(cert: OpenPGPCertificate) = asciiArmor(cert.pgpKeyRing) + @JvmStatic + @Deprecated( + "Call getInstance().toAsciiArmor(cert) instead.", + replaceWith = ReplaceWith("getInstance().toAsciiArmor(cert)")) + fun asciiArmor(cert: OpenPGPCertificate): String = getInstance().toAsciiArmor(cert) /** * Wrap a key of certificate in ASCII armor and write the result into the given @@ -318,8 +366,10 @@ class PGPainless( * @throws IOException in case of an error during the armoring process */ @JvmStatic - @Deprecated("Covert to OpenPGPSignature and call .toAsciiArmoredString() instead.") - fun asciiArmor(signature: PGPSignature): String = ArmorUtils.toAsciiArmoredString(signature) + @Deprecated( + "Call toAsciiArmor(signature) on an instance of PGPainless instead.", + replaceWith = ReplaceWith("getInstance().toAsciiArmor(signature)")) + fun asciiArmor(signature: PGPSignature): String = getInstance().toAsciiArmor(signature) /** * Create an [EncryptionBuilder], which can be used to encrypt and/or sign data using diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt index b5d5b839..1c326f31 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt @@ -229,7 +229,7 @@ class ArmorUtils { * @return header map */ @JvmStatic - private fun keyToHeader(publicKey: PGPPublicKey): Map> { + fun keyToHeader(publicKey: PGPPublicKey): Map> { val headerMap = mutableMapOf>() val userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey) val first: String? = userIds.firstOrNull() diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java index e9f2332d..7b586277 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CanonicalizedDataEncryptionTest.java @@ -113,10 +113,11 @@ public class CanonicalizedDataEncryptionTest { @BeforeAll public static void readKeys() throws IOException { - secretKeys = PGPainless.getInstance().readKey().parseKey(KEY); + PGPainless api = PGPainless.getInstance(); + secretKeys = api.readKey().parseKey(KEY); publicKeys = secretKeys.toCertificate(); // CHECKSTYLE:OFF - System.out.println(PGPainless.asciiArmor(secretKeys)); + System.out.println(api.toAsciiArmor(secretKeys)); // CHECKSTYLE:ON } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/parsing/KeyRingReaderTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/parsing/KeyRingReaderTest.java index 7f0862d7..f16375a5 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/parsing/KeyRingReaderTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/parsing/KeyRingReaderTest.java @@ -599,7 +599,7 @@ class KeyRingReaderTest { public void testReadKeyRingWithArmoredPublicKey() throws IOException { OpenPGPKey secretKeys = api.generateKey().modernKeyRing("Alice "); OpenPGPCertificate publicKeys = secretKeys.toCertificate(); - String armored = PGPainless.asciiArmor(publicKeys); + String armored = api.toAsciiArmor(publicKeys); PGPKeyRing keyRing = PGPainless.readKeyRing() .keyRing(armored); diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java index 05a955f8..9d3ad35a 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java @@ -132,7 +132,7 @@ public class ArmorUtilsTest { @Test public void signatureToAsciiArmoredString() { String SIG = "-----BEGIN PGP SIGNATURE-----\n" + - "Version: PGPainless\n" + + "Comment: 4F66 5C4D C2C4 660B C642 5E41 5736 E693 1ACF 370C\n" + "\n" + "iHUEARMKAB0WIQRPZlxNwsRmC8ZCXkFXNuaTGs83DAUCYJ/x5gAKCRBXNuaTGs83\n" + "DFRwAP9/4wMvV3WcX59Clo7mkRce6iwW3VBdiN+yMu3tjmHB2wD/RfE28Q1v4+eo\n" + diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ExtractCertImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ExtractCertImpl.kt index 3a5993b4..b4677a56 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ExtractCertImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/ExtractCertImpl.kt @@ -7,7 +7,6 @@ package org.pgpainless.sop import java.io.InputStream import java.io.OutputStream import org.pgpainless.PGPainless -import org.pgpainless.util.ArmorUtils import org.pgpainless.util.ArmoredOutputStreamFactory import sop.Ready import sop.operation.ExtractCert @@ -26,10 +25,8 @@ class ExtractCertImpl(private val api: PGPainless) : ExtractCert { if (certs.size == 1) { val cert = certs[0] // This way we have a nice armor header with fingerprint and user-ids - val armorOut = - ArmorUtils.toAsciiArmoredStream(cert.pgpKeyRing, outputStream) - armorOut.write(cert.encoded) - armorOut.close() + val armored = cert.toAsciiArmoredString() + outputStream.write(armored.toByteArray()) } else { // for multiple certs, add no info headers to the ASCII armor val armorOut = ArmoredOutputStreamFactory.get(outputStream) diff --git a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/GenerateKeyImpl.kt b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/GenerateKeyImpl.kt index 3d914946..d343652d 100644 --- a/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/GenerateKeyImpl.kt +++ b/pgpainless-sop/src/main/kotlin/org/pgpainless/sop/GenerateKeyImpl.kt @@ -18,7 +18,6 @@ import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve import org.pgpainless.key.generation.type.rsa.RsaLength import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec -import org.pgpainless.util.ArmorUtils import org.pgpainless.util.Passphrase import sop.Profile import sop.Ready @@ -50,9 +49,8 @@ class GenerateKeyImpl(private val api: PGPainless) : GenerateKey { return object : Ready() { override fun writeTo(outputStream: OutputStream) { if (armor) { - val armorOut = ArmorUtils.toAsciiArmoredStream(key.pgpKeyRing, outputStream) - key.pgpKeyRing.encode(armorOut) - armorOut.close() + val armored = key.toAsciiArmoredString() + outputStream.write(armored.toByteArray()) } else { key.pgpKeyRing.encode(outputStream) }