From eb9587163d1512cedad24d6b5401936708bb0f3d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 16 Dec 2020 17:31:27 +0100 Subject: [PATCH] SOP: Implement basic signature verification --- .../org/pgpainless/sop/PGPainlessCLI.java | 8 +- .../org/pgpainless/sop/commands/Verify.java | 90 +++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java index e0a25c5a..dcab8773 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java @@ -1,9 +1,6 @@ package org.pgpainless.sop; -import org.pgpainless.sop.commands.ExtractCert; -import org.pgpainless.sop.commands.GenerateKey; -import org.pgpainless.sop.commands.Sign; -import org.pgpainless.sop.commands.Version; +import org.pgpainless.sop.commands.*; import picocli.CommandLine; @CommandLine.Command( @@ -11,7 +8,8 @@ import picocli.CommandLine; Version.class, GenerateKey.class, ExtractCert.class, - Sign.class + Sign.class, + Verify.class } ) public class PGPainlessCLI implements Runnable { diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java new file mode 100644 index 00000000..66fef9e1 --- /dev/null +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java @@ -0,0 +1,90 @@ +package org.pgpainless.sop.commands; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.io.Streams; +import org.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.OpenPgpMetadata; +import org.pgpainless.key.OpenPgpV4Fingerprint; +import picocli.CommandLine; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +@CommandLine.Command(name = "verify") +public class Verify implements Runnable { + + @CommandLine.Parameters(index = "0", description = "The detached signature") + File signature; + + @CommandLine.Parameters(index = "1..*") + File[] certs; + + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); + + @Override + public void run() { + df.setTimeZone(tz); + + if (certs.length == 0) { + System.out.println("No certificates supplied."); + System.exit(19); + } + + Map publicKeys = new HashMap<>(); + for (File cert : certs) { + try(FileInputStream in = new FileInputStream(cert)) { + publicKeys.put(cert, PGPainless.readKeyRing().publicKeyRing(in)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + try(FileInputStream sigIn = new FileInputStream(signature)) { + DecryptionStream verifier = PGPainless.decryptAndOrVerify() + .onInputStream(System.in) + .doNotDecrypt() + .verifyDetachedSignature(sigIn) + .verifyWith(new HashSet<>(publicKeys.values())) + .ignoreMissingPublicKeys() + .build(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.pipeAll(verifier, out); + verifier.close(); + + OpenPgpMetadata metadata = verifier.getResult(); + + for (OpenPgpV4Fingerprint sigKeyFp : metadata.getVerifiedSignatures().keySet()) { + PGPSignature signature = metadata.getVerifiedSignatures().get(sigKeyFp); + for (File file : certs) { + // Search signing key ring + PGPPublicKeyRing publicKeyRing = publicKeys.get(file); + if (publicKeyRing.getPublicKey(sigKeyFp.getKeyId()) == null) { + continue; + } + + String utcSigDate = df.format(signature.getCreationTime()); + OpenPgpV4Fingerprint primaryKeyFp = new OpenPgpV4Fingerprint(publicKeyRing); + System.out.println(utcSigDate + " " + sigKeyFp.toString() + " " + primaryKeyFp.toString() + + " signed by " + file.getName()); + } + } + + if (metadata.getVerifiedSignatures().isEmpty()) { + System.out.println("Signature validation failed."); + System.exit(3); + } + } catch (IOException | PGPException e) { + e.printStackTrace(); + } + } +}