mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 18:29:39 +02:00
Base PGPainlessCLI on new sop-java module
* Rename pgpainless-sop -> pgpainless-cli * Introduce sop-java (implementation-independent SOP API) * Introduce sop-java-picocli (CLI frontend for sop-java) * Introduce pgpainless-sop (implementation of sop-java using PGPainless) * Rework pgpainless-cli (plugs pgpainless-sop into sop-java-picocli)
This commit is contained in:
parent
2ba782c451
commit
8cf5347b52
112 changed files with 6146 additions and 1303 deletions
17
pgpainless-cli/README.md
Normal file
17
pgpainless-cli/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# PGPainless-SOP
|
||||
|
||||
PGPainless-SOP is an implementation of the [Stateless OpenPGP Command Line Interface](https://tools.ietf.org/html/draft-dkg-openpgp-stateless-cli-01) specification based on PGPainless.
|
||||
|
||||
## Build
|
||||
To build an executable, `gradle jar` should be sufficient. The resulting jar file can be found in `pgpainless-sop/build/libs/`.
|
||||
|
||||
## Execute
|
||||
|
||||
Simply use the provided `./pgpainless` script to execute PGPainless' Stateless Command Line Interface.
|
||||
|
||||
To discover all commands use
|
||||
```
|
||||
./pgpainless help
|
||||
```
|
||||
|
||||
Enjoy!
|
66
pgpainless-cli/build.gradle
Normal file
66
pgpainless-cli/build.gradle
Normal file
|
@ -0,0 +1,66 @@
|
|||
plugins {
|
||||
id 'application'
|
||||
}
|
||||
def generatedVersionDir = "${buildDir}/generated-version"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
output.dir(generatedVersionDir, builtBy: 'generateVersionProperties')
|
||||
}
|
||||
}
|
||||
|
||||
task generateVersionProperties {
|
||||
doLast {
|
||||
def propertiesFile = file "$generatedVersionDir/version.properties"
|
||||
propertiesFile.parentFile.mkdirs()
|
||||
propertiesFile.createNewFile()
|
||||
// Instead of using a Properties object here, we directly write to the file
|
||||
// since Properties adds a timestamp, ruining reproducibility
|
||||
propertiesFile.write("version="+rootProject.version.toString())
|
||||
}
|
||||
}
|
||||
processResources.dependsOn generateVersionProperties
|
||||
|
||||
dependencies {
|
||||
implementation(project(":pgpainless-sop"))
|
||||
implementation(project(":sop-java"))
|
||||
|
||||
implementation(project(":sop-java-picocli"))
|
||||
implementation 'info.picocli:picocli:4.5.2'
|
||||
|
||||
testImplementation(project(":pgpainless-core"))
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
|
||||
// https://todd.ginsberg.com/post/testing-system-exit/
|
||||
testImplementation 'com.ginsberg:junit5-system-exit:1.1.1'
|
||||
|
||||
/*
|
||||
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
|
||||
/*/
|
||||
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
||||
//*/
|
||||
implementation "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.cli.PGPainlessCLI'
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': "$mainClassName"
|
||||
}
|
||||
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
} {
|
||||
exclude "META-INF/*.SF"
|
||||
exclude "META-INF/*.DSA"
|
||||
exclude "META-INF/*.RSA"
|
||||
}
|
||||
}
|
||||
|
||||
tasks."jar".dependsOn(":pgpainless-core:assemble")
|
18
pgpainless-cli/pgpainless
Executable file
18
pgpainless-cli/pgpainless
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Pretty fancy method to get reliable the absolute path of a shell
|
||||
# script, *even if it is sourced*. Credits go to GreenFox on
|
||||
# stackoverflow: http://stackoverflow.com/a/12197518/194894
|
||||
pushd . > /dev/null
|
||||
SCRIPTDIR="${BASH_SOURCE[0]}";
|
||||
while([ -h "${SCRIPTDIR}" ]); do
|
||||
cd "`dirname "${SCRIPTDIR}"`"
|
||||
SCRIPTDIR="$(readlink "`basename "${SCRIPTDIR}"`")";
|
||||
done
|
||||
cd "`dirname "${SCRIPTDIR}"`" > /dev/null
|
||||
SCRIPTDIR="`pwd`";
|
||||
popd > /dev/null
|
||||
|
||||
BASEDIR="$(cd ${SCRIPTDIR}/.. && pwd)"
|
||||
|
||||
exec "${BASEDIR}/gradlew" run --quiet --args="${@}"
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2020 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import org.pgpainless.sop.SOPImpl;
|
||||
import sop.cli.picocli.SopCLI;
|
||||
|
||||
public class PGPainlessCLI {
|
||||
|
||||
static {
|
||||
SopCLI.setSopInstance(new SOPImpl());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int result = execute(args);
|
||||
if (result != 0) {
|
||||
System.exit(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static int execute(String... args) {
|
||||
return SopCLI.execute(args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2020 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* PGPainless SOP implementing a Stateless OpenPGP Command Line Interface.
|
||||
* @see <a href="https://tools.ietf.org/html/draft-dkg-openpgp-stateless-cli-01">
|
||||
* Stateless OpenPGP Command Line Interface
|
||||
* draft-dkg-openpgp-stateless-cli-01</a>
|
||||
*/
|
||||
package org.pgpainless.cli;
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ExitCodeTest {
|
||||
|
||||
@Test
|
||||
@ExpectSystemExitWithStatus(69)
|
||||
public void testUnknownCommand_69() {
|
||||
PGPainlessCLI.main(new String[] {"generate-kex"});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ExpectSystemExitWithStatus(37)
|
||||
public void testCommandWithUnknownOption_37() {
|
||||
PGPainlessCLI.main(new String[] {"generate-key", "-k", "\"k is unknown\""});
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void successfulExecutionDoesNotTerminateJVM() {
|
||||
PGPainlessCLI.main(new String[] {"version"});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public static final String ARMOR_PRIVATE_KEY_HEADER = "-----BEGIN PGP PRIVATE KEY BLOCK-----";
|
||||
public static final byte[] ARMOR_PRIVATE_KEY_HEADER_BYTES =
|
||||
ARMOR_PRIVATE_KEY_HEADER.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
public static File createTempDirectory() throws IOException {
|
||||
String name = randomString(10);
|
||||
File dir = Files.createTempDirectory(name).toFile();
|
||||
// dir.deleteOnExit();
|
||||
return dir;
|
||||
}
|
||||
|
||||
private static String randomString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
|
||||
public class ArmorTest {
|
||||
|
||||
private static PrintStream originalSout;
|
||||
|
||||
@BeforeEach
|
||||
public void saveSout() {
|
||||
originalSout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreSout() {
|
||||
System.setOut(originalSout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorSecretKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
byte[] bytes = secretKey.getEncoded();
|
||||
|
||||
System.setIn(new ByteArrayInputStream(bytes));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
PGPSecretKeyRing armored = PGPainless.readKeyRing().secretKeyRing(armorOut.toString());
|
||||
assertArrayEquals(secretKey.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorPublicKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
||||
byte[] bytes = publicKey.getEncoded();
|
||||
|
||||
System.setIn(new ByteArrayInputStream(bytes));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
PGPPublicKeyRing armored = PGPainless.readKeyRing().publicKeyRing(armorOut.toString());
|
||||
assertArrayEquals(publicKey.getEncoded(), armored.getEncoded());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void armorMessage() {
|
||||
String message = "Hello, World!\n";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(armorOut));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
String armored = armorOut.toString();
|
||||
|
||||
assertTrue(armored.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
||||
assertTrue(armored.contains("SGVsbG8sIFdvcmxkIQo="));
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void doesNotNestArmorByDefault() {
|
||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"Version: BCPG v1.69\n" +
|
||||
"\n" +
|
||||
"SGVsbG8sIFdvcmxkCg==\n" +
|
||||
"=fkLo\n" +
|
||||
"-----END PGP MESSAGE-----";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("armor");
|
||||
|
||||
assertEquals(armored, out.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testAllowNested() {
|
||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"Version: BCPG v1.69\n" +
|
||||
"\n" +
|
||||
"SGVsbG8sIFdvcmxkCg==\n" +
|
||||
"=fkLo\n" +
|
||||
"-----END PGP MESSAGE-----";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("armor", "--allow-nested");
|
||||
|
||||
assertNotEquals(armored, out.toString());
|
||||
assertTrue(out.toString().contains(
|
||||
"LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEJDUEcgdjEuNjkK\n" +
|
||||
"ClNHVnNiRzhzSUZkdmNteGtDZz09Cj1ma0xvCi0tLS0tRU5EIFBHUCBNRVNTQUdF\n" +
|
||||
"LS0tLS0="));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
|
||||
public class DearmorTest {
|
||||
|
||||
private PrintStream originalSout;
|
||||
|
||||
@BeforeEach
|
||||
public void saveSout() {
|
||||
this.originalSout = System.out;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreSout() {
|
||||
System.setOut(originalSout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
String armored = PGPainless.asciiArmor(secretKey);
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertArrayEquals(secretKey.getEncoded(), out.toByteArray());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorCertificate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice@pgpainless.org", null);
|
||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||
String armored = PGPainless.asciiArmor(certificate);
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertArrayEquals(certificate.getEncoded(), out.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void dearmorMessage() {
|
||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"Version: BCPG v1.69\n" +
|
||||
"\n" +
|
||||
"SGVsbG8sIFdvcmxkCg==\n" +
|
||||
"=fkLo\n" +
|
||||
"-----END PGP MESSAGE-----";
|
||||
|
||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("dearmor");
|
||||
|
||||
assertEquals("Hello, World\n", out.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
|
||||
public class EncryptDecryptTest {
|
||||
|
||||
private static File tempDir;
|
||||
private static PrintStream originalSout;
|
||||
|
||||
@BeforeAll
|
||||
public static void prepare() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void encryptAndDecryptAMessage() throws IOException {
|
||||
originalSout = System.out;
|
||||
File julietKeyFile = new File(tempDir, "juliet.key");
|
||||
assertTrue(julietKeyFile.createNewFile());
|
||||
|
||||
File julietCertFile = new File(tempDir, "juliet.asc");
|
||||
assertTrue(julietCertFile.createNewFile());
|
||||
|
||||
File romeoKeyFile = new File(tempDir, "romeo.key");
|
||||
assertTrue(romeoKeyFile.createNewFile());
|
||||
|
||||
File romeoCertFile = new File(tempDir, "romeo.asc");
|
||||
assertTrue(romeoCertFile.createNewFile());
|
||||
|
||||
File msgAscFile = new File(tempDir, "msg.asc");
|
||||
assertTrue(msgAscFile.createNewFile());
|
||||
|
||||
OutputStream julietKeyOut = new FileOutputStream(julietKeyFile);
|
||||
System.setOut(new PrintStream(julietKeyOut));
|
||||
PGPainlessCLI.execute("generate-key", "Juliet Capulet <juliet@capulet.lit>");
|
||||
julietKeyOut.close();
|
||||
|
||||
FileInputStream julietKeyIn = new FileInputStream(julietKeyFile);
|
||||
System.setIn(julietKeyIn);
|
||||
OutputStream julietCertOut = new FileOutputStream(julietCertFile);
|
||||
System.setOut(new PrintStream(julietCertOut));
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
julietKeyIn.close();
|
||||
julietCertOut.close();
|
||||
|
||||
OutputStream romeoKeyOut = new FileOutputStream(romeoKeyFile);
|
||||
System.setOut(new PrintStream(romeoKeyOut));
|
||||
PGPainlessCLI.execute("generate-key", "Romeo Montague <romeo@montague.lit>");
|
||||
romeoKeyOut.close();
|
||||
|
||||
FileInputStream romeoKeyIn = new FileInputStream(romeoKeyFile);
|
||||
System.setIn(romeoKeyIn);
|
||||
OutputStream romeoCertOut = new FileOutputStream(romeoCertFile);
|
||||
System.setOut(new PrintStream(romeoCertOut));
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
romeoKeyIn.close();
|
||||
romeoCertOut.close();
|
||||
|
||||
String msg = "Hello World!\n";
|
||||
ByteArrayInputStream msgIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
|
||||
System.setIn(msgIn);
|
||||
OutputStream msgAscOut = new FileOutputStream(msgAscFile);
|
||||
System.setOut(new PrintStream(msgAscOut));
|
||||
PGPainlessCLI.execute("encrypt",
|
||||
"--sign-with", romeoKeyFile.getAbsolutePath(),
|
||||
julietCertFile.getAbsolutePath());
|
||||
msgAscOut.close();
|
||||
|
||||
File verifyFile = new File(tempDir, "verify.txt");
|
||||
assertTrue(verifyFile.createNewFile());
|
||||
|
||||
FileInputStream msgAscIn = new FileInputStream(msgAscFile);
|
||||
System.setIn(msgAscIn);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PrintStream pOut = new PrintStream(out);
|
||||
System.setOut(pOut);
|
||||
PGPainlessCLI.execute("decrypt",
|
||||
"--verify-out", verifyFile.getAbsolutePath(),
|
||||
"--verify-with", romeoCertFile.getAbsolutePath(),
|
||||
julietKeyFile.getAbsolutePath());
|
||||
msgAscIn.close();
|
||||
|
||||
assertEquals(msg, out.toString());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void after() {
|
||||
System.setOut(originalSout);
|
||||
// CHECKSTYLE:OFF
|
||||
System.out.println(tempDir.getAbsolutePath());
|
||||
// CHECKSTYLE:ON
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
|
||||
public class ExtractCertTest {
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testExtractCert() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.simpleEcKeyRing("Juliet Capulet <juliet@capulet.lit>");
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(secretKeys.getEncoded());
|
||||
System.setIn(inputStream);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
|
||||
PGPainlessCLI.execute("extract-cert");
|
||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(out.toByteArray());
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
|
||||
assertFalse(info.isSecretKey());
|
||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.pgpainless.cli.TestUtils.ARMOR_PRIVATE_KEY_HEADER_BYTES;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
|
||||
public class GenerateCertTest {
|
||||
|
||||
private static File tempDir;
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testKeyGeneration() throws IOException, PGPException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("generate-key", "--armor", "Juliet Capulet <juliet@capulet.lit>");
|
||||
|
||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(out.toByteArray());
|
||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||
|
||||
byte[] outBegin = new byte[37];
|
||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
||||
assertArrayEquals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testNoArmor() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
PGPainlessCLI.execute("generate-key", "--no-armor", "Test <test@test.test>");
|
||||
|
||||
byte[] outBegin = new byte[37];
|
||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
||||
assertFalse(Arrays.equals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.cli.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.cli.PGPainlessCLI;
|
||||
import org.pgpainless.cli.TestUtils;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.info.KeyRingInfo;
|
||||
import org.pgpainless.key.util.KeyRingUtils;
|
||||
|
||||
public class SignVerifyTest {
|
||||
|
||||
private static File tempDir;
|
||||
private static PrintStream originalSout;
|
||||
|
||||
private final String data = "If privacy is outlawed, only outlaws will have privacy.\n";
|
||||
|
||||
@BeforeAll
|
||||
public static void prepare() throws IOException {
|
||||
tempDir = TestUtils.createTempDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailOnSystemExit
|
||||
public void testSignatureCreationAndVerification() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
originalSout = System.out;
|
||||
InputStream originalIn = System.in;
|
||||
|
||||
// Write alice key to disc
|
||||
File aliceKeyFile = new File(tempDir, "alice.key");
|
||||
assertTrue(aliceKeyFile.createNewFile());
|
||||
PGPSecretKeyRing aliceKeys = PGPainless.generateKeyRing()
|
||||
.modernKeyRing("alice", null);
|
||||
OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile);
|
||||
Streams.pipeAll(new ByteArrayInputStream(aliceKeys.getEncoded()), aliceKeyOut);
|
||||
aliceKeyOut.close();
|
||||
|
||||
// Write alice pub key to disc
|
||||
File aliceCertFile = new File(tempDir, "alice.pub");
|
||||
assertTrue(aliceCertFile.createNewFile());
|
||||
PGPPublicKeyRing alicePub = KeyRingUtils.publicKeyRingFrom(aliceKeys);
|
||||
OutputStream aliceCertOut = new FileOutputStream(aliceCertFile);
|
||||
Streams.pipeAll(new ByteArrayInputStream(alicePub.getEncoded()), aliceCertOut);
|
||||
aliceCertOut.close();
|
||||
|
||||
// Write test data to disc
|
||||
File dataFile = new File(tempDir, "data");
|
||||
assertTrue(dataFile.createNewFile());
|
||||
FileOutputStream dataOut = new FileOutputStream(dataFile);
|
||||
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), dataOut);
|
||||
dataOut.close();
|
||||
|
||||
// Sign test data
|
||||
FileInputStream dataIn = new FileInputStream(dataFile);
|
||||
System.setIn(dataIn);
|
||||
File sigFile = new File(tempDir, "sig.asc");
|
||||
assertTrue(sigFile.createNewFile());
|
||||
FileOutputStream sigOut = new FileOutputStream(sigFile);
|
||||
System.setOut(new PrintStream(sigOut));
|
||||
PGPainlessCLI.execute("sign", "--armor", aliceKeyFile.getAbsolutePath());
|
||||
sigOut.close();
|
||||
|
||||
// verify test data signature
|
||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(verifyOut));
|
||||
dataIn = new FileInputStream(dataFile);
|
||||
System.setIn(dataIn);
|
||||
PGPainlessCLI.execute("verify", sigFile.getAbsolutePath(), aliceCertFile.getAbsolutePath());
|
||||
dataIn.close();
|
||||
|
||||
// Test verification output
|
||||
|
||||
// [date] [signing-key-fp] [primary-key-fp] signed by [key.pub]
|
||||
String verification = verifyOut.toString();
|
||||
String[] split = verification.split(" ");
|
||||
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(aliceKeys);
|
||||
OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(new KeyRingInfo(alicePub, new Date()).getSigningSubkeys().get(0));
|
||||
assertEquals(signingKeyFingerprint.toString(), split[1].trim());
|
||||
assertEquals(primaryKeyFingerprint.toString(), split[2].trim());
|
||||
|
||||
System.setIn(originalIn);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void after() {
|
||||
System.setOut(originalSout);
|
||||
// CHECKSTYLE:OFF
|
||||
System.out.println(tempDir.getAbsolutePath());
|
||||
// CHECKSTYLE:ON
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue