Wip: Start implementing a SOP client

This commit is contained in:
Paul Schaub 2020-11-26 11:00:48 +01:00
parent ff8c6d8b6d
commit 9d3ad01dfc
8 changed files with 254 additions and 1 deletions

View File

@ -0,0 +1,30 @@
plugins {
id 'com.github.johnrengelman.shadow' version '1.2.2'
}
apply plugin: 'com.github.johnrengelman.shadow'
dependencies {
implementation(project(":pgpainless-core"))
compile 'info.picocli:picocli:3.9.6'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
/*
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
/*/
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
//*/
api "org.bouncycastle:bcpg-jdk15on:$bouncyCastleVersion"
// https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
}
mainClassName = 'org.pgpainless.sop.PGPainlessCLI'
shadowJar {
baseName = 'pgpcli'
}

View File

@ -0,0 +1,48 @@
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 picocli.CommandLine;
@CommandLine.Command(
subcommands = {
Version.class,
GenerateKey.class,
ExtractCert.class,
Sign.class
}
)
public class PGPainlessCLI implements Runnable {
public static void main(String[] args) {
interpret(args);
// generateKey();
}
public static void interpret(String... args) {
CommandLine.run(new PGPainlessCLI(), args);
}
private static void version() {
CommandLine.run(new PGPainlessCLI(), "version");
}
private static void generateKey() {
interpret("generate-key", "--armor", "Alice Example <alice@wonderland.lit>");
}
private static void extractCert() {
CommandLine.run(new PGPainlessCLI(), "extract-cert");
}
private static void sign() {
interpret("sign", "--armor", "--as=text", "alice.sec");
}
@Override
public void run() {
}
}

View File

@ -0,0 +1,16 @@
package org.pgpainless.sop;
import java.io.IOException;
import org.pgpainless.util.ArmorUtils;
public class Print {
public static String toString(byte[] bytes, boolean armor) throws IOException {
if (armor) {
return ArmorUtils.toAsciiArmoredString(bytes);
} else {
return new String(bytes, "UTF-8");
}
}
}

View File

@ -0,0 +1,35 @@
package org.pgpainless.sop.commands;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.PGPainless;
import org.pgpainless.sop.Print;
import org.pgpainless.util.BCUtil;
import picocli.CommandLine;
@CommandLine.Command(name = "extract-cert")
public class ExtractCert implements Runnable {
@CommandLine.Option(names = {"--armor"}, description = "ASCII Armor the output")
boolean armor = false;
@CommandLine.Option(names = {"--no-armor"})
boolean noArmor = false;
@Override
public void run() {
try {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(System.in);
PGPPublicKeyRing publicKeys = BCUtil.publicKeyRingFromSecretKeyRing(secretKeys);
System.out.println(Print.toString(publicKeys.getEncoded(), !noArmor));
} catch (IOException | PGPException e) {
System.err.println("Error extracting certificate from keys;");
System.err.println(e.getMessage());
System.exit(1);
}
}
}

View File

@ -0,0 +1,39 @@
package org.pgpainless.sop.commands;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.PGPainless;
import org.pgpainless.sop.Print;
import org.pgpainless.util.ArmorUtils;
import picocli.CommandLine;
@CommandLine.Command(name = "generate-key")
public class GenerateKey implements Runnable {
@CommandLine.Option(names = {"--armor"}, description = "ASCII Armor the output")
boolean armor = false;
@CommandLine.Option(names = {"--no-armor"})
boolean noArmor = false;
@CommandLine.Parameters
String userId;
@Override
public void run() {
try {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing(userId).getSecretKeys();
System.out.println(Print.toString(secretKeys.getEncoded(), !noArmor));
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | PGPException | IOException e) {
System.err.println("Error creating OpenPGP key:");
System.err.println(e.getMessage());
System.exit(1);
}
}
}

View File

@ -0,0 +1,72 @@
package org.pgpainless.sop.commands;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.io.Streams;
import org.pgpainless.PGPainless;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
import org.pgpainless.sop.Print;
import picocli.CommandLine;
@CommandLine.Command(name = "sign")
public class Sign implements Runnable {
public enum Type {
binary,
text
}
@CommandLine.Option(names = {"--armor"}, description = "ASCII Armor the output")
boolean armor = false;
@CommandLine.Option(names = {"--no-armor"})
boolean noArmor = false;
@CommandLine.Option(names = "--as", description = "Defaults to 'binary'. If '--as=text' and the input data is not valid UTF-8, sign fails with return code 53.")
Type type;
@CommandLine.Parameters
File secretKeyFile;
@Override
public void run() {
PGPSecretKeyRing secretKeys;
try {
secretKeys = PGPainless.readKeyRing().secretKeyRing(new FileInputStream(secretKeyFile));
} catch (IOException | PGPException e) {
System.err.println("Error reading secret key ring.");
System.err.println(e.getMessage());
System.exit(1);
return;
}
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.createEncryptor()
.onOutputStream(out)
.doNotEncrypt()
.createDetachedSignature()
.signWith(new UnprotectedKeysProtector(), secretKeys)
.noArmor();
Streams.pipeAll(System.in, encryptionStream);
encryptionStream.close();
PGPSignature signature = encryptionStream.getResult().getSignatures().iterator().next();
System.out.println(Print.toString(signature.getEncoded(), !noArmor));
} catch (PGPException | IOException e) {
System.err.println("Error signing data.");
System.err.println(e.getMessage());
System.exit(1);
}
}
}

View File

@ -0,0 +1,12 @@
package org.pgpainless.sop.commands;
import picocli.CommandLine;
@CommandLine.Command(name = "version")
public class Version implements Runnable {
@Override
public void run() {
System.out.println("PGPainless CLI version 0.0.1");
}
}

View File

@ -1,4 +1,5 @@
rootProject.name = 'PGPainless'
include 'pgpainless-core'
include 'pgpainless-core',
'pgpainless-sop'