1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 11:27:57 +01:00

SOP: Add documentation and parse not-{before|after} dates in verify

This commit is contained in:
Paul Schaub 2020-12-16 18:35:25 +01:00
parent eb9587163d
commit 930448b02b
3 changed files with 87 additions and 33 deletions

View file

@ -17,10 +17,10 @@ public class GenerateKey implements Runnable {
@CommandLine.Option(names = {"--armor"}, description = "ASCII Armor the output") @CommandLine.Option(names = {"--armor"}, description = "ASCII Armor the output")
boolean armor = false; boolean armor = false;
@CommandLine.Option(names = {"--no-armor"}) @CommandLine.Option(names = {"--no-armor"}, description = "Non-armored, binary output")
boolean noArmor = false; boolean noArmor = false;
@CommandLine.Parameters @CommandLine.Parameters(description = "User-ID, eg. \"Alice <alice@example.com>\"")
String userId; String userId;
@Override @Override

View file

@ -15,39 +15,48 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
@CommandLine.Command(name = "verify") @CommandLine.Command(name = "verify", description = "Verify a detached signature.\nThe signed data is being read from standard input.")
public class Verify implements Runnable { public class Verify implements Runnable {
@CommandLine.Parameters(index = "0", description = "The detached signature") @CommandLine.Parameters(index = "0", description = "Detached signature")
File signature; File signature;
@CommandLine.Parameters(index = "1..*") @CommandLine.Parameters(index = "1..*", arity = "1..*", description = "Public key certificates")
File[] certs; File[] certificates;
TimeZone tz = TimeZone.getTimeZone("UTC"); @CommandLine.Option(names = {"--not-before"}, description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" +
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); "Reject signatures with a creation date not in range.\n" +
"Defaults to beginning of time (\"-\").")
String notBefore = "-";
@CommandLine.Option(names = {"--not-after"}, description = "ISO-8601 formatted UTC date (eg. '2020-11-23T16:35Z)\n" +
"Reject signatures with a creation date not in range.\n" +
"Defaults to current system time (\"now\").\n" +
"Accepts special value \"-\" for end of time.")
String notAfter = "now";
private final TimeZone tz = TimeZone.getTimeZone("UTC");
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
private final Date beginningOfTime = new Date(0);
private final Date endOfTime = new Date(8640000000000000L);
@Override @Override
public void run() { public void run() {
df.setTimeZone(tz); df.setTimeZone(tz);
Date notBeforeDate = parseNotBefore();
Date notAfterDate = parseNotAfter();
if (certs.length == 0) { Map<File, PGPPublicKeyRing> publicKeys = readCertificatesFromFiles();
if (publicKeys.isEmpty()) {
System.out.println("No certificates supplied."); System.out.println("No certificates supplied.");
System.exit(19); System.exit(19);
} }
Map<File, PGPPublicKeyRing> 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)) { try(FileInputStream sigIn = new FileInputStream(signature)) {
DecryptionStream verifier = PGPainless.decryptAndOrVerify() DecryptionStream verifier = PGPainless.decryptAndOrVerify()
.onInputStream(System.in) .onInputStream(System.in)
@ -63,28 +72,73 @@ public class Verify implements Runnable {
OpenPgpMetadata metadata = verifier.getResult(); OpenPgpMetadata metadata = verifier.getResult();
for (OpenPgpV4Fingerprint sigKeyFp : metadata.getVerifiedSignatures().keySet()) { Map<OpenPgpV4Fingerprint, PGPSignature> signaturesInTimeRange = new HashMap<>();
PGPSignature signature = metadata.getVerifiedSignatures().get(sigKeyFp); for (OpenPgpV4Fingerprint fingerprint : metadata.getVerifiedSignatures().keySet()) {
for (File file : certs) { PGPSignature signature = metadata.getVerifiedSignatures().get(fingerprint);
// Search signing key ring Date creationTime = signature.getCreationTime();
PGPPublicKeyRing publicKeyRing = publicKeys.get(file); if (!creationTime.before(notBeforeDate) && !creationTime.after(notAfterDate)) {
if (publicKeyRing.getPublicKey(sigKeyFp.getKeyId()) == null) { signaturesInTimeRange.put(fingerprint, signature);
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()) { if (signaturesInTimeRange.isEmpty()) {
System.out.println("Signature validation failed."); System.out.println("Signature validation failed.");
System.exit(3); System.exit(3);
} }
printValidSignatures(signaturesInTimeRange, publicKeys);
} catch (IOException | PGPException e) { } catch (IOException | PGPException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void printValidSignatures(Map<OpenPgpV4Fingerprint, PGPSignature> validSignatures, Map<File, PGPPublicKeyRing> publicKeys) {
for (OpenPgpV4Fingerprint sigKeyFp : validSignatures.keySet()) {
PGPSignature signature = validSignatures.get(sigKeyFp);
for (File file : publicKeys.keySet()) {
// 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());
}
}
}
private Map<File, PGPPublicKeyRing> readCertificatesFromFiles() {
Map<File, PGPPublicKeyRing> publicKeys = new HashMap<>();
for (File cert : certificates) {
try(FileInputStream in = new FileInputStream(cert)) {
publicKeys.put(cert, PGPainless.readKeyRing().publicKeyRing(in));
} catch (IOException e) {
e.printStackTrace();
}
}
return publicKeys;
}
private Date parseNotAfter() {
try {
return notAfter.equals("now") ? new Date() : notAfter.equals("-") ? endOfTime : df.parse(notAfter);
} catch (ParseException e) {
System.out.println("Invalid date string supplied as value of --not-after.");
System.exit(1);
return null;
}
}
private Date parseNotBefore() {
try {
return notBefore.equals("now") ? new Date() : notBefore.equals("-") ? beginningOfTime: df.parse(notBefore);
} catch (ParseException e) {
System.out.println("Invalid date string supplied as value of --not-before.");
System.exit(1);
return null;
}
}
} }

View file

@ -2,7 +2,7 @@ package org.pgpainless.sop.commands;
import picocli.CommandLine; import picocli.CommandLine;
@CommandLine.Command(name = "version") @CommandLine.Command(name = "version", description = "Display version information about the tool")
public class Version implements Runnable { public class Version implements Runnable {
@Override @Override