diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java index e41304b7..33cf7faa 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java +++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java @@ -14,6 +14,7 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.decryption_verification.DecryptionBuilder; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.encryption_signing.EncryptionBuilder; @@ -27,6 +28,7 @@ import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterfac import org.pgpainless.key.parsing.KeyRingReader; import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.policy.Policy; +import org.pgpainless.signature.SignatureUtils; import org.pgpainless.util.ArmorUtils; public final class PGPainless { @@ -102,6 +104,19 @@ public final class PGPainless { } } + /** + * Wrap the detached signature in ASCII armor. + * + * @param signature detached signature + * @return ascii armored string + * + * @throws IOException in case of an error in the {@link ArmoredOutputStream} + */ + public static String asciiArmor(@Nonnull PGPSignature signature) + throws IOException { + return ArmorUtils.toAsciiArmoredString(signature); + } + /** * Wrap a key of certificate in ASCII armor and write the result into the given {@link OutputStream}. * diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/ArmorUtils.java b/pgpainless-core/src/main/java/org/pgpainless/util/ArmorUtils.java index 160d8d0a..8a51ff3d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/util/ArmorUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/util/ArmorUtils.java @@ -156,6 +156,38 @@ public final class ArmorUtils { return sb.toString(); } + /** + * Return the ASCII armored representation of the given detached signature. + * The signature will not be stripped of non-exportable subpackets or trust-packets. + * If you need to strip those (e.g. because the signature is intended to be sent to a third party), use + * {@link #toAsciiArmoredString(PGPSignature, boolean)} and provide
true
as boolean value. + * + * @param signature signature + * @return ascii armored string + * + * @throws IOException in case of an error in the {@link ArmoredOutputStream} + */ + @Nonnull + public static String toAsciiArmoredString(@Nonnull PGPSignature signature) throws IOException { + return toAsciiArmoredString(signature, false); + } + + /** + * Return the ASCII armored representation of the given detached signature. + * If
export
is true, the signature will be stripped of non-exportable subpackets or trust-packets. + * If it is
false
, the signature will be encoded as-is. + * + * @param signature signature + * @return ascii armored string + * + * @throws IOException in case of an error in the {@link ArmoredOutputStream} + */ + @Nonnull + public static String toAsciiArmoredString(@Nonnull PGPSignature signature, boolean export) + throws IOException { + return toAsciiArmoredString(signature.getEncoded(export)); + } + /** * Return the ASCII armored encoding of the given OpenPGP data bytes. * 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 5eb32179..b83c0d03 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/ArmorUtilsTest.java @@ -24,6 +24,7 @@ import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.AfterAll; @@ -39,6 +40,7 @@ import org.pgpainless.key.TestKeys; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.type.ecc.EllipticCurve; import org.pgpainless.key.generation.type.ecc.ecdsa.ECDSA; +import org.pgpainless.signature.SignatureUtils; public class ArmorUtilsTest { @@ -128,6 +130,21 @@ public class ArmorUtilsTest { assertTrue(ascii.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n")); } + @Test + public void signatureToAsciiArmoredString() throws PGPException, IOException { + String SIG = "-----BEGIN PGP SIGNATURE-----\n" + + "Version: PGPainless\n" + + "\n" + + "iHUEARMKAB0WIQRPZlxNwsRmC8ZCXkFXNuaTGs83DAUCYJ/x5gAKCRBXNuaTGs83\n" + + "DFRwAP9/4wMvV3WcX59Clo7mkRce6iwW3VBdiN+yMu3tjmHB2wD/RfE28Q1v4+eo\n" + + "ySNgbyvqYYsNr0fnBwaG3aaj+u5ExiE=\n" + + "=Z2SO\n" + + "-----END PGP SIGNATURE-----\n"; + PGPSignature signature = SignatureUtils.readSignatures(SIG).get(0); + String armored = PGPainless.asciiArmor(signature); + assertEquals(SIG, armored); + } + @Test public void testAsciiArmorToStream() throws IOException, PGPException { PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();