mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2024-11-17 21:12:07 +01:00
WiP: Implement first prototypes of all SOP commands for external binaries
This commit is contained in:
parent
efec4d9110
commit
a63b29fe80
21 changed files with 1473 additions and 146 deletions
|
@ -20,6 +20,9 @@ dependencies {
|
||||||
|
|
||||||
api "org.slf4j:slf4j-api:$slf4jVersion"
|
api "org.slf4j:slf4j-api:$slf4jVersion"
|
||||||
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||||
|
|
||||||
|
// Compare version strings
|
||||||
|
implementation 'org.apache.maven:maven-artifact:3.6.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
@ -4,10 +4,20 @@
|
||||||
|
|
||||||
package sop.external;
|
package sop.external;
|
||||||
|
|
||||||
|
import sop.Ready;
|
||||||
import sop.SOP;
|
import sop.SOP;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.operation.ArmorExternal;
|
||||||
|
import sop.external.operation.DearmorExternal;
|
||||||
|
import sop.external.operation.DecryptExternal;
|
||||||
|
import sop.external.operation.DetachedSignExternal;
|
||||||
|
import sop.external.operation.DetachedVerifyExternal;
|
||||||
|
import sop.external.operation.EncryptExternal;
|
||||||
import sop.external.operation.ExtractCertExternal;
|
import sop.external.operation.ExtractCertExternal;
|
||||||
import sop.external.operation.GenerateKeyExternal;
|
import sop.external.operation.GenerateKeyExternal;
|
||||||
|
import sop.external.operation.InlineDetachExternal;
|
||||||
|
import sop.external.operation.InlineSignExternal;
|
||||||
|
import sop.external.operation.InlineVerifyExternal;
|
||||||
import sop.external.operation.VersionExternal;
|
import sop.external.operation.VersionExternal;
|
||||||
import sop.operation.Armor;
|
import sop.operation.Armor;
|
||||||
import sop.operation.Dearmor;
|
import sop.operation.Dearmor;
|
||||||
|
@ -25,9 +35,9 @@ import sop.operation.Version;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class ExternalSOP implements SOP {
|
public class ExternalSOP implements SOP {
|
||||||
|
@ -61,47 +71,47 @@ public class ExternalSOP implements SOP {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign detachedSign() {
|
public DetachedSign detachedSign() {
|
||||||
return null;
|
return new DetachedSignExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineSign inlineSign() {
|
public InlineSign inlineSign() {
|
||||||
return null;
|
return new InlineSignExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedVerify detachedVerify() {
|
public DetachedVerify detachedVerify() {
|
||||||
return null;
|
return new DetachedVerifyExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineVerify inlineVerify() {
|
public InlineVerify inlineVerify() {
|
||||||
return null;
|
return new InlineVerifyExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InlineDetach inlineDetach() {
|
public InlineDetach inlineDetach() {
|
||||||
return null;
|
return new InlineDetachExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Encrypt encrypt() {
|
public Encrypt encrypt() {
|
||||||
return null;
|
return new EncryptExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Decrypt decrypt() {
|
public Decrypt decrypt() {
|
||||||
return null;
|
return new DecryptExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Armor armor() {
|
public Armor armor() {
|
||||||
return null;
|
return new ArmorExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dearmor dearmor() {
|
public Dearmor dearmor() {
|
||||||
return null;
|
return new DearmorExternal(binaryName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void finish(Process process) throws IOException {
|
public static void finish(Process process) throws IOException {
|
||||||
|
@ -112,7 +122,7 @@ public class ExternalSOP implements SOP {
|
||||||
ByteArrayOutputStream errOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream errOut = new ByteArrayOutputStream();
|
||||||
byte[] buf = new byte[512];
|
byte[] buf = new byte[512];
|
||||||
int r;
|
int r;
|
||||||
while ((r = errIn.read(buf)) > 0 ) {
|
while ((r = errIn.read(buf)) > 0) {
|
||||||
errOut.write(buf, 0, r);
|
errOut.write(buf, 0, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +145,7 @@ public class ExternalSOP implements SOP {
|
||||||
ByteArrayOutputStream errOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream errOut = new ByteArrayOutputStream();
|
||||||
byte[] buf = new byte[512];
|
byte[] buf = new byte[512];
|
||||||
int r;
|
int r;
|
||||||
while ((r = errIn.read(buf)) > 0 ) {
|
while ((r = errIn.read(buf)) > 0) {
|
||||||
errOut.write(buf, 0, r);
|
errOut.write(buf, 0, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,4 +233,74 @@ public class ExternalSOP implements SOP {
|
||||||
}
|
}
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String readFully(InputStream inputStream) throws IOException {
|
||||||
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = inputStream.read(buf)) > 0) {
|
||||||
|
bOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
return bOut.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Ready ready(Runtime runtime, List<String> commandList, List<String> envList) {
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = runtime.exec(command, env);
|
||||||
|
InputStream stdIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = stdIn.read(buf)) >= 0) {
|
||||||
|
outputStream.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static Ready ready(Runtime runtime, List<String> commandList, List<String> envList, InputStream standardIn) {
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
try {
|
||||||
|
Process process = runtime.exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = standardIn.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
standardIn.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
while ((r = processIn.read(buf)) > 0) {
|
||||||
|
outputStream.write(buf, 0 , r);
|
||||||
|
}
|
||||||
|
|
||||||
|
processIn.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
finish(process);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
40
external-sop/src/main/java/sop/external/operation/ArmorExternal.java
vendored
Normal file
40
external-sop/src/main/java/sop/external/operation/ArmorExternal.java
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.enums.ArmorLabel;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.Armor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ArmorExternal implements Armor {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
public ArmorExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("armor");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Armor label(ArmorLabel label) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--label=" + label);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ready data(InputStream data) throws SOPGPException.BadData, IOException {
|
||||||
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList);
|
||||||
|
}
|
||||||
|
}
|
33
external-sop/src/main/java/sop/external/operation/DearmorExternal.java
vendored
Normal file
33
external-sop/src/main/java/sop/external/operation/DearmorExternal.java
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.Dearmor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class DearmorExternal implements Dearmor {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
public DearmorExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("dearmor");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ready data(InputStream data) throws SOPGPException.BadData, IOException {
|
||||||
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList);
|
||||||
|
}
|
||||||
|
}
|
139
external-sop/src/main/java/sop/external/operation/DecryptExternal.java
vendored
Normal file
139
external-sop/src/main/java/sop/external/operation/DecryptExternal.java
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.DecryptionResult;
|
||||||
|
import sop.ReadyWithResult;
|
||||||
|
import sop.SessionKey;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.Decrypt;
|
||||||
|
import sop.util.UTCUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class DecryptExternal implements Decrypt {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
private int verifyWithCounter = 0;
|
||||||
|
private int withSessionKeyCounter = 0;
|
||||||
|
private int withPasswordCounter = 0;
|
||||||
|
private int keyCounter = 0;
|
||||||
|
private int withKeyPasswordCounter = 0;
|
||||||
|
|
||||||
|
public DecryptExternal(String binary, Properties environment) {
|
||||||
|
this.commandList.add(binary);
|
||||||
|
this.commandList.add("decrypt");
|
||||||
|
this.envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt verifyNotBefore(Date timestamp)
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
this.commandList.add("--not-before=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt verifyNotAfter(Date timestamp)
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
this.commandList.add("--not-after=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt verifyWithCert(InputStream cert)
|
||||||
|
throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
|
||||||
|
String envVar = "VERIFY_WITH_" + verifyWithCounter++;
|
||||||
|
commandList.add("--verify-with=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(cert));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt withSessionKey(SessionKey sessionKey)
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
String envVar = "SESSION_KEY_" + withSessionKeyCounter++;
|
||||||
|
commandList.add("--with-session-key=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + sessionKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt withPassword(String password)
|
||||||
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
|
String envVar = "PASSWORD_" + withPasswordCounter++;
|
||||||
|
commandList.add("--with-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt withKey(InputStream key)
|
||||||
|
throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
|
||||||
|
String envVar = "KEY_" + keyCounter++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(key));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Decrypt withKeyPassword(byte[] password)
|
||||||
|
throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
|
||||||
|
String envVar = "KEY_PASSWORD_" + withKeyPasswordCounter++;
|
||||||
|
commandList.add("--with-key-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + new String(password));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<DecryptionResult> ciphertext(InputStream ciphertext)
|
||||||
|
throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt,
|
||||||
|
SOPGPException.KeyIsProtected, IOException {
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new ReadyWithResult<DecryptionResult>() {
|
||||||
|
@Override
|
||||||
|
public DecryptionResult writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = ciphertext.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
while ((r = processIn.read(buf)) > 0) {
|
||||||
|
outputStream.write(buf, 0 , r);
|
||||||
|
}
|
||||||
|
|
||||||
|
processIn.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
|
||||||
|
return new DecryptionResult(null, Collections.emptyList()); // TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,43 +1,102 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package sop.external.operation;
|
package sop.external.operation;
|
||||||
|
|
||||||
import sop.ReadyWithResult;
|
import sop.ReadyWithResult;
|
||||||
import sop.SigningResult;
|
import sop.SigningResult;
|
||||||
import sop.enums.SignAs;
|
import sop.enums.SignAs;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
import sop.operation.DetachedSign;
|
import sop.operation.DetachedSign;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
public class DetachedSignExternal implements DetachedSign {
|
public class DetachedSignExternal implements DetachedSign {
|
||||||
|
|
||||||
private boolean noArmor;
|
private final List<String> commandList = new ArrayList<>();
|
||||||
private byte[] keyPassword;
|
private final List<String> envList;
|
||||||
|
|
||||||
|
private int withKeyPasswordCounter = 0;
|
||||||
|
private int keyCounter = 0;
|
||||||
|
|
||||||
|
public DetachedSignExternal(String binary, Properties properties) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("sign");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(properties);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign noArmor() {
|
public DetachedSign noArmor() {
|
||||||
this.noArmor = true;
|
commandList.add("--no-armor");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign key(InputStream key) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
|
public DetachedSign key(InputStream key) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
|
||||||
return null;
|
String envVar = "KEY_" + keyCounter++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(key));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign withKeyPassword(byte[] password) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
|
public DetachedSign withKeyPassword(byte[] password) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
|
||||||
this.keyPassword = password;
|
String envVar = "WITH_KEY_PASSWORD_" + withKeyPasswordCounter++;
|
||||||
|
commandList.add("--with-key-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + new String(password));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DetachedSign mode(SignAs mode) throws SOPGPException.UnsupportedOption {
|
public DetachedSign mode(SignAs mode) throws SOPGPException.UnsupportedOption {
|
||||||
return null;
|
commandList.add("--as=" + mode);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReadyWithResult<SigningResult> data(InputStream data) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
|
public ReadyWithResult<SigningResult> data(InputStream data)
|
||||||
return null;
|
throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
|
||||||
|
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new ReadyWithResult<SigningResult>() {
|
||||||
|
@Override
|
||||||
|
public SigningResult writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = data.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
while ((r = processIn.read(buf)) > 0) {
|
||||||
|
outputStream.write(buf, 0 , r);
|
||||||
|
}
|
||||||
|
|
||||||
|
processIn.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
|
||||||
|
return SigningResult.builder().build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
108
external-sop/src/main/java/sop/external/operation/DetachedVerifyExternal.java
vendored
Normal file
108
external-sop/src/main/java/sop/external/operation/DetachedVerifyExternal.java
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.Verification;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.DetachedVerify;
|
||||||
|
import sop.operation.VerifySignatures;
|
||||||
|
import sop.util.UTCUtil;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class DetachedVerifyExternal implements DetachedVerify {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
private Set<InputStream> certs = new HashSet<>();
|
||||||
|
private InputStream signatures;
|
||||||
|
private int certCounter = 0;
|
||||||
|
|
||||||
|
public DetachedVerifyExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("verify");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--not-before=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--not-after=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DetachedVerify cert(InputStream cert) throws SOPGPException.BadData, IOException {
|
||||||
|
this.certs.add(cert);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifySignatures signatures(InputStream signatures) throws SOPGPException.BadData, IOException {
|
||||||
|
this.signatures = signatures;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Verification> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||||
|
commandList.add("@ENV:SIGNATURE");
|
||||||
|
envList.add("SIGNATURE=" + ExternalSOP.readFully(signatures));
|
||||||
|
|
||||||
|
for (InputStream cert : certs) {
|
||||||
|
String envVar = "CERT_" + certCounter++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(cert));
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = data.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(processIn));
|
||||||
|
List<Verification> verifications = new ArrayList<>();
|
||||||
|
|
||||||
|
String line = null;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
verifications.add(Verification.fromString(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
|
||||||
|
return verifications;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
external-sop/src/main/java/sop/external/operation/EncryptExternal.java
vendored
Normal file
90
external-sop/src/main/java/sop/external/operation/EncryptExternal.java
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.enums.EncryptAs;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.Encrypt;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class EncryptExternal implements Encrypt {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
private int SIGN_WITH_COUNTER = 0;
|
||||||
|
private int KEY_PASSWORD_COUNTER = 0;
|
||||||
|
private int PASSWORD_COUNTER = 0;
|
||||||
|
private int CERT_COUNTER = 0;
|
||||||
|
|
||||||
|
public EncryptExternal(String binary, Properties environment) {
|
||||||
|
this.commandList.add(binary);
|
||||||
|
this.commandList.add("encrypt");
|
||||||
|
this.envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt noArmor() {
|
||||||
|
this.commandList.add("--no-armor");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt mode(EncryptAs mode)
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
this.commandList.add("--as=" + mode);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt signWith(InputStream key)
|
||||||
|
throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData,
|
||||||
|
IOException {
|
||||||
|
String envVar = "SIGN_WITH_" + SIGN_WITH_COUNTER++;
|
||||||
|
commandList.add("--sign-with=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(key));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt withKeyPassword(byte[] password)
|
||||||
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
|
String envVar = "KEY_PASSWORD_" + KEY_PASSWORD_COUNTER++;
|
||||||
|
commandList.add("--with-key-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + new String(password));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt withPassword(String password)
|
||||||
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
|
String envVar = "PASSWORD_" + PASSWORD_COUNTER++;
|
||||||
|
commandList.add("--with-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Encrypt withCert(InputStream cert)
|
||||||
|
throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData,
|
||||||
|
IOException {
|
||||||
|
String envVar = "CERT_" + CERT_COUNTER++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(cert));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ready plaintext(InputStream plaintext)
|
||||||
|
throws IOException, SOPGPException.KeyIsProtected {
|
||||||
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList, plaintext);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,77 +9,30 @@ import sop.exception.SOPGPException;
|
||||||
import sop.external.ExternalSOP;
|
import sop.external.ExternalSOP;
|
||||||
import sop.operation.ExtractCert;
|
import sop.operation.ExtractCert;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class ExtractCertExternal implements ExtractCert {
|
public class ExtractCertExternal implements ExtractCert {
|
||||||
|
|
||||||
private final String binary;
|
private final List<String> commandList = new ArrayList<>();
|
||||||
private final Runtime runtime = Runtime.getRuntime();
|
private final List<String> envList;
|
||||||
private final Properties environment;
|
|
||||||
|
|
||||||
private boolean noArmor;
|
|
||||||
|
|
||||||
public ExtractCertExternal(String binary, Properties properties) {
|
public ExtractCertExternal(String binary, Properties properties) {
|
||||||
this.binary = binary;
|
this.commandList.add(binary);
|
||||||
this.environment = properties;
|
this.commandList.add("extract-cert");
|
||||||
|
this.envList = ExternalSOP.propertiesToEnv(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtractCert noArmor() {
|
public ExtractCert noArmor() {
|
||||||
this.noArmor = true;
|
this.commandList.add("--no-armor");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Ready key(InputStream keyInputStream) throws SOPGPException.BadData {
|
public Ready key(InputStream keyInputStream) throws SOPGPException.BadData {
|
||||||
List<String> commandList = new ArrayList<>();
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList, keyInputStream);
|
||||||
|
|
||||||
commandList.add(binary);
|
|
||||||
commandList.add("extract-cert");
|
|
||||||
|
|
||||||
if (noArmor) {
|
|
||||||
commandList.add("--no-armor");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> envList = ExternalSOP.propertiesToEnv(environment);
|
|
||||||
|
|
||||||
String[] command = commandList.toArray(new String[0]);
|
|
||||||
String[] env = envList.toArray(new String[0]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Process process = runtime.exec(command, env);
|
|
||||||
OutputStream extractOut = process.getOutputStream();
|
|
||||||
InputStream extractIn = process.getInputStream();
|
|
||||||
|
|
||||||
return new Ready() {
|
|
||||||
@Override
|
|
||||||
public void writeTo(OutputStream outputStream) throws IOException {
|
|
||||||
byte[] buf = new byte[4096];
|
|
||||||
int r;
|
|
||||||
while ((r = keyInputStream.read(buf)) > 0) {
|
|
||||||
extractOut.write(buf, 0, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyInputStream.close();
|
|
||||||
extractOut.close();
|
|
||||||
|
|
||||||
while ((r = extractIn.read(buf)) > 0) {
|
|
||||||
outputStream.write(buf, 0 , r);
|
|
||||||
}
|
|
||||||
|
|
||||||
extractIn.close();
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
ExternalSOP.finish(process);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,93 +9,48 @@ import sop.exception.SOPGPException;
|
||||||
import sop.external.ExternalSOP;
|
import sop.external.ExternalSOP;
|
||||||
import sop.operation.GenerateKey;
|
import sop.operation.GenerateKey;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class GenerateKeyExternal implements GenerateKey {
|
public class GenerateKeyExternal implements GenerateKey {
|
||||||
|
|
||||||
private final String binary;
|
private final List<String> commandList = new ArrayList<>();
|
||||||
private boolean noArmor = false;
|
private final List<String> envList;
|
||||||
private List<String> userIds = new ArrayList<>();
|
|
||||||
private String keyPassword;
|
|
||||||
|
|
||||||
private final Runtime runtime = Runtime.getRuntime();
|
private int keyPasswordCounter = 0;
|
||||||
private final Properties properties;
|
|
||||||
|
|
||||||
public GenerateKeyExternal(String binary, Properties environment) {
|
public GenerateKeyExternal(String binary, Properties environment) {
|
||||||
this.binary = binary;
|
this.commandList.add(binary);
|
||||||
this.properties = environment;
|
this.commandList.add("generate-key");
|
||||||
|
this.envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenerateKey noArmor() {
|
public GenerateKey noArmor() {
|
||||||
this.noArmor = true;
|
this.commandList.add("--no-armor");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenerateKey userId(String userId) {
|
public GenerateKey userId(String userId) {
|
||||||
this.userIds.add(userId);
|
this.commandList.add(userId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenerateKey withKeyPassword(String password)
|
public GenerateKey withKeyPassword(String password)
|
||||||
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
this.keyPassword = password;
|
this.commandList.add("--with-key-password=@ENV:KEY_PASSWORD_" + keyPasswordCounter);
|
||||||
|
this.envList.add("KEY_PASSWORD_" + keyPasswordCounter + "=" + password);
|
||||||
|
keyPasswordCounter++;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Ready generate()
|
public Ready generate()
|
||||||
throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo {
|
throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo {
|
||||||
List<String> commandList = new ArrayList<>();
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList);
|
||||||
|
|
||||||
commandList.add(binary);
|
|
||||||
commandList.add("generate-key");
|
|
||||||
|
|
||||||
if (noArmor) {
|
|
||||||
commandList.add("--no-armor");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPassword != null) {
|
|
||||||
commandList.add("--with-key-password");
|
|
||||||
commandList.add("@ENV:key_password");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String userId : userIds) {
|
|
||||||
commandList.add(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> envList = ExternalSOP.propertiesToEnv(properties);
|
|
||||||
if (keyPassword != null) {
|
|
||||||
envList.add("key_password=" + keyPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] command = commandList.toArray(new String[0]);
|
|
||||||
String[] env = envList.toArray(new String[0]);
|
|
||||||
try {
|
|
||||||
Process process = runtime.exec(command, env);
|
|
||||||
InputStream stdIn = process.getInputStream();
|
|
||||||
|
|
||||||
return new Ready() {
|
|
||||||
@Override
|
|
||||||
public void writeTo(OutputStream outputStream) throws IOException {
|
|
||||||
byte[] buf = new byte[4096];
|
|
||||||
int r;
|
|
||||||
while ((r = stdIn.read(buf)) >= 0) {
|
|
||||||
outputStream.write(buf, 0, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalSOP.finish(process);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
75
external-sop/src/main/java/sop/external/operation/InlineDetachExternal.java
vendored
Normal file
75
external-sop/src/main/java/sop/external/operation/InlineDetachExternal.java
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.ReadyWithResult;
|
||||||
|
import sop.Signatures;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.InlineDetach;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class InlineDetachExternal implements InlineDetach {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
public InlineDetachExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("inline-detach");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineDetach noArmor() {
|
||||||
|
commandList.add("--no-armor");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<Signatures> message(InputStream messageInputStream) throws IOException, SOPGPException.BadData {
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new ReadyWithResult<Signatures>() {
|
||||||
|
@Override
|
||||||
|
public Signatures writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = messageInputStream.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageInputStream.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
while ((r = processIn.read(buf)) > 0) {
|
||||||
|
outputStream.write(buf, 0 , r);
|
||||||
|
}
|
||||||
|
|
||||||
|
processIn.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
external-sop/src/main/java/sop/external/operation/InlineSignExternal.java
vendored
Normal file
65
external-sop/src/main/java/sop/external/operation/InlineSignExternal.java
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.enums.InlineSignAs;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.InlineSign;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class InlineSignExternal implements InlineSign {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
private int keyCounter = 0;
|
||||||
|
private int withKeyPasswordCounter = 0;
|
||||||
|
|
||||||
|
public InlineSignExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("inline-sign");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign noArmor() {
|
||||||
|
commandList.add("--no-armor");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign key(InputStream key) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
|
||||||
|
String envVar = "KEY_" + keyCounter++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(key));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign withKeyPassword(byte[] password) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
|
||||||
|
String envVar = "WITH_KEY_PASSWORD_" + withKeyPasswordCounter++;
|
||||||
|
commandList.add("--with-key-password=@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + new String(password));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineSign mode(InlineSignAs mode) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--as=" + mode);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ready data(InputStream data) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
|
||||||
|
return ExternalSOP.ready(Runtime.getRuntime(), commandList, envList, data);
|
||||||
|
}
|
||||||
|
}
|
94
external-sop/src/main/java/sop/external/operation/InlineVerifyExternal.java
vendored
Normal file
94
external-sop/src/main/java/sop/external/operation/InlineVerifyExternal.java
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external.operation;
|
||||||
|
|
||||||
|
import sop.ReadyWithResult;
|
||||||
|
import sop.Verification;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.external.ExternalSOP;
|
||||||
|
import sop.operation.InlineVerify;
|
||||||
|
import sop.util.UTCUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class InlineVerifyExternal implements InlineVerify {
|
||||||
|
|
||||||
|
private final List<String> commandList = new ArrayList<>();
|
||||||
|
private final List<String> envList;
|
||||||
|
|
||||||
|
private int certCounter = 0;
|
||||||
|
|
||||||
|
public InlineVerifyExternal(String binary, Properties environment) {
|
||||||
|
commandList.add(binary);
|
||||||
|
commandList.add("inline-verify");
|
||||||
|
envList = ExternalSOP.propertiesToEnv(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--not-before=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
|
||||||
|
commandList.add("--not-after=" + UTCUtil.formatUTCDate(timestamp));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InlineVerify cert(InputStream cert) throws SOPGPException.BadData, IOException {
|
||||||
|
String envVar = "CERT_" + certCounter++;
|
||||||
|
commandList.add("@ENV:" + envVar);
|
||||||
|
envList.add(envVar + "=" + ExternalSOP.readFully(cert));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadyWithResult<List<Verification>> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||||
|
String[] command = commandList.toArray(new String[0]);
|
||||||
|
String[] env = envList.toArray(new String[0]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(command, env);
|
||||||
|
OutputStream processOut = process.getOutputStream();
|
||||||
|
InputStream processIn = process.getInputStream();
|
||||||
|
|
||||||
|
return new ReadyWithResult<List<Verification>>() {
|
||||||
|
@Override
|
||||||
|
public List<Verification> writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature {
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int r;
|
||||||
|
while ((r = data.read(buf)) > 0) {
|
||||||
|
processOut.write(buf, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.close();
|
||||||
|
processOut.close();
|
||||||
|
|
||||||
|
|
||||||
|
while ((r = processIn.read(buf)) > 0) {
|
||||||
|
outputStream.write(buf, 0 , r);
|
||||||
|
}
|
||||||
|
|
||||||
|
processIn.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
ExternalSOP.finish(process);
|
||||||
|
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package sop.external;
|
package sop.external;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sop.SOP;
|
import sop.SOP;
|
||||||
|
@ -14,6 +15,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
||||||
|
|
||||||
public abstract class AbstractExternalSOPTest {
|
public abstract class AbstractExternalSOPTest {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExternalSOPTest.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExternalSOPTest.class);
|
||||||
|
@ -26,10 +29,20 @@ public abstract class AbstractExternalSOPTest {
|
||||||
sop = new ExternalSOP(backend, environment);
|
sop = new ExternalSOP(backend, environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the SOP backend.
|
||||||
|
*
|
||||||
|
* @return SOP backend
|
||||||
|
*/
|
||||||
public SOP getSop() {
|
public SOP getSop() {
|
||||||
return sop;
|
return sop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return <pre>true</pre> iff the specified SOP backend binary is available and accessible.
|
||||||
|
*
|
||||||
|
* @return true if external SOP backend is usable
|
||||||
|
*/
|
||||||
public static boolean isExternalSopInstalled() {
|
public static boolean isExternalSopInstalled() {
|
||||||
String binary = readSopBackendFromProperties();
|
String binary = readSopBackendFromProperties();
|
||||||
if (binary == null) {
|
if (binary == null) {
|
||||||
|
@ -38,6 +51,69 @@ public abstract class AbstractExternalSOPTest {
|
||||||
return new File(binary).exists();
|
return new File(binary).exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Is {
|
||||||
|
le("<"),
|
||||||
|
leq("<="),
|
||||||
|
eq("=="),
|
||||||
|
geq(">="),
|
||||||
|
ge(">"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String display;
|
||||||
|
|
||||||
|
Is(String display) {
|
||||||
|
this.display = display;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore a test if the tested binary version matches a version criterion.
|
||||||
|
* Example:
|
||||||
|
* If the installed version of example-sop is 0.1.3, <pre>ignoreIf("example-sop", Is.le, "0.1.4")</pre> will
|
||||||
|
* make the test be ignored.
|
||||||
|
* <pre>ignoreIf("example-sop", Is.eq, "0.1.3")</pre> will skip the test as well.
|
||||||
|
* <pre>ignoreIf("another-sop", Is.gt, "0.0.0")</pre> will not skip the test, since the binary name does not match.
|
||||||
|
*
|
||||||
|
* @param name name of the binary
|
||||||
|
* @param is relation of the version
|
||||||
|
* @param version the reference version
|
||||||
|
*/
|
||||||
|
public void ignoreIf(String name, Is is, String version) {
|
||||||
|
String actualName = getSop().version().getName();
|
||||||
|
String actualVersion = getSop().version().getVersion();
|
||||||
|
|
||||||
|
if (!name.matches(actualName)) {
|
||||||
|
// Name mismatch, do not ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComparableVersion reference = new ComparableVersion(version);
|
||||||
|
ComparableVersion actual = new ComparableVersion(actualVersion);
|
||||||
|
|
||||||
|
int res = actual.compareTo(reference);
|
||||||
|
String msg = "Skip since installed " + name + " " + actual + " " + is.toDisplay() + " " + reference;
|
||||||
|
switch (is) {
|
||||||
|
case le:
|
||||||
|
assumeFalse(res < 0, msg);
|
||||||
|
break;
|
||||||
|
case leq:
|
||||||
|
assumeFalse(res <= 0, msg);
|
||||||
|
case eq:
|
||||||
|
assumeFalse(res == 0, msg);
|
||||||
|
break;
|
||||||
|
case geq:
|
||||||
|
assumeFalse(res >= 0, msg);
|
||||||
|
break;
|
||||||
|
case ge:
|
||||||
|
assumeFalse(res > 0, msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String readSopBackendFromProperties() {
|
private static String readSopBackendFromProperties() {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
try {
|
try {
|
||||||
|
|
70
external-sop/src/test/java/sop/external/EncryptDecryptRoundTripTest.java
vendored
Normal file
70
external-sop/src/test/java/sop/external/EncryptDecryptRoundTripTest.java
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
|
||||||
|
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
||||||
|
public class EncryptDecryptRoundTripTest extends AbstractExternalSOPTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripAliceTest() throws IOException {
|
||||||
|
byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] ciphertext = getSop().encrypt()
|
||||||
|
.withCert(TestKeys.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.plaintext(message)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = getSop().decrypt()
|
||||||
|
.withKey(TestKeys.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.ciphertext(ciphertext)
|
||||||
|
.toByteArrayAndResult()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayEquals(message, plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripBobTest() throws IOException {
|
||||||
|
byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] ciphertext = getSop().encrypt()
|
||||||
|
.withCert(TestKeys.BOB_CERT.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.plaintext(message)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = getSop().decrypt()
|
||||||
|
.withKey(TestKeys.BOB_KEY.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.ciphertext(ciphertext)
|
||||||
|
.toByteArrayAndResult()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayEquals(message, plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptDecryptRoundTripCarolTest() throws IOException {
|
||||||
|
ignoreIf("sqop", Is.geq, "0.0.0"); // sqop reports cert not encryption capable
|
||||||
|
|
||||||
|
byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] ciphertext = getSop().encrypt()
|
||||||
|
.withCert(TestKeys.CAROL_CERT.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.plaintext(message)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
byte[] plaintext = getSop().decrypt()
|
||||||
|
.withKey(TestKeys.CAROL_KEY.getBytes(StandardCharsets.UTF_8))
|
||||||
|
.ciphertext(ciphertext)
|
||||||
|
.toByteArrayAndResult()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayEquals(message, plaintext);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,16 +9,72 @@ import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static sop.external.JUtils.arrayStartsWith;
|
||||||
|
import static sop.external.JUtils.assertArrayStartsWith;
|
||||||
|
|
||||||
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
||||||
public class ExternalExtractCertTest extends AbstractExternalSOPTest {
|
public class ExternalExtractCertTest extends AbstractExternalSOPTest {
|
||||||
|
|
||||||
|
private static final String BEGIN_PGP_PUBLIC_KEY_BLOCK = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
private static final byte[] BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES = BEGIN_PGP_PUBLIC_KEY_BLOCK.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void extractCertTest() throws IOException {
|
public void extractArmoredCertFromArmoredKeyTest() throws IOException {
|
||||||
InputStream keyIn = getSop().generateKey().userId("Alice").generate().getInputStream();
|
InputStream keyIn = getSop().generateKey()
|
||||||
String cert = new String(getSop().extractCert().key(keyIn).getBytes());
|
.userId("Alice <alice@openpgp.org>")
|
||||||
assertTrue(cert.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
.generate()
|
||||||
|
.getInputStream();
|
||||||
|
|
||||||
|
byte[] cert = getSop().extractCert().key(keyIn).getBytes();
|
||||||
|
assertArrayStartsWith(cert, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractUnarmoredCertFromArmoredKeyTest() throws IOException {
|
||||||
|
InputStream keyIn = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.generate()
|
||||||
|
.getInputStream();
|
||||||
|
|
||||||
|
byte[] cert = getSop().extractCert()
|
||||||
|
.noArmor()
|
||||||
|
.key(keyIn)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertFalse(arrayStartsWith(cert, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractArmoredCertFromUnarmoredKeyTest() throws IOException {
|
||||||
|
InputStream keyIn = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.noArmor()
|
||||||
|
.generate()
|
||||||
|
.getInputStream();
|
||||||
|
|
||||||
|
byte[] cert = getSop().extractCert()
|
||||||
|
.key(keyIn)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(cert, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractUnarmoredCertFromUnarmoredKeyTest() throws IOException {
|
||||||
|
InputStream keyIn = getSop().generateKey()
|
||||||
|
.noArmor()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.generate()
|
||||||
|
.getInputStream();
|
||||||
|
|
||||||
|
byte[] cert = getSop().extractCert()
|
||||||
|
.noArmor()
|
||||||
|
.key(keyIn)
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertFalse(arrayStartsWith(cert, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,23 +8,88 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.EnabledIf;
|
import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static sop.external.JUtils.assertArrayStartsWith;
|
||||||
|
|
||||||
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
||||||
public class ExternalGenerateKeyTest extends AbstractExternalSOPTest {
|
public class ExternalGenerateKeyTest extends AbstractExternalSOPTest {
|
||||||
|
|
||||||
|
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||||
|
private static final String BEGIN_PGP_PRIVATE_KEY_BLOCK = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
byte[] BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES = BEGIN_PGP_PRIVATE_KEY_BLOCK.getBytes(UTF8);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generateKeyTest() throws IOException {
|
public void generateKeyTest() throws IOException {
|
||||||
String key = new String(getSop().generateKey().userId("Alice").generate().getBytes());
|
byte[] key = getSop().generateKey()
|
||||||
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateKeyNoArmor() throws IOException {
|
||||||
|
byte[] key = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.noArmor()
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertFalse(JUtils.arrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateKeyWithMultipleUserIdsTest() throws IOException {
|
||||||
|
byte[] key = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.userId("Bob <bob@openpgp.org>")
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateKeyWithoutUserIdTest() throws IOException {
|
||||||
|
ignoreIf("pgpainless-cli", Is.le, "1.3.15");
|
||||||
|
|
||||||
|
byte[] key = getSop().generateKey()
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generateKeyWithPasswordTest() throws IOException {
|
public void generateKeyWithPasswordTest() throws IOException {
|
||||||
String key = new String(getSop().generateKey().userId("Alice").withKeyPassword("swßrdf1sh").generate().getBytes());
|
ignoreIf("sqop", Is.le, "0.27.0");
|
||||||
assertEquals("asd", key);
|
ignoreIf("pgpainless-cli", Is.le, "1.3.0");
|
||||||
|
|
||||||
|
byte[] key = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.withKeyPassword("sw0rdf1sh")
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateKeyWithMultipleUserIdsAndPassword() throws IOException {
|
||||||
|
ignoreIf("sqop", Is.le, "0.27.0");
|
||||||
|
ignoreIf("pgpainless-cli", Is.le, "1.3.15");
|
||||||
|
|
||||||
|
byte[] key = getSop().generateKey()
|
||||||
|
.userId("Alice <alice@openpgp.org>")
|
||||||
|
.userId("Bob <bob@openpgp.org>")
|
||||||
|
.withKeyPassword("sw0rdf1sh")
|
||||||
|
.generate()
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
assertArrayStartsWith(key, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ package sop.external;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.EnabledIf;
|
import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
@EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled")
|
||||||
|
@ -16,13 +16,15 @@ public class ExternalVersionTest extends AbstractExternalSOPTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void versionNameTest() {
|
public void versionNameTest() {
|
||||||
assertEquals("sqop", getSop().version().getName());
|
String name = getSop().version().getName();
|
||||||
|
assertNotNull(name);
|
||||||
|
assertFalse(name.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void versionVersionTest() {
|
public void versionVersionTest() {
|
||||||
String version = getSop().version().getVersion();
|
String version = getSop().version().getVersion();
|
||||||
assertTrue(version.matches("\\d+(\\.\\d+)*"));
|
assertTrue(version.matches("\\d+(\\.\\d+)*\\S*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
53
external-sop/src/test/java/sop/external/JUtils.java
vendored
Normal file
53
external-sop/src/test/java/sop/external/JUtils.java
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
public class JUtils {
|
||||||
|
|
||||||
|
public static boolean arrayStartsWith(byte[] array, byte[] start) {
|
||||||
|
return arrayStartsWith(array, start, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean arrayStartsWith(byte[] array, byte[] start, int offset) {
|
||||||
|
if (offset < 0) {
|
||||||
|
throw new IllegalArgumentException("Offset cannot be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start.length + offset > array.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < start.length; i++) {
|
||||||
|
if (array[offset + i] != start[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertArrayStartsWith(byte[] array, byte[] start) {
|
||||||
|
if (!arrayStartsWith(array, start)) {
|
||||||
|
byte[] actual = new byte[Math.min(start.length, array.length)];
|
||||||
|
System.arraycopy(array, 0, actual, 0, actual.length);
|
||||||
|
fail("Array does not start with expected bytes.\n" +
|
||||||
|
"Expected: <" + Arrays.toString(start) + ">\n" +
|
||||||
|
"Actual: <" + Arrays.toString(actual) + ">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertArrayStartsWith(byte[] array, byte[] start, int offset) {
|
||||||
|
if (!arrayStartsWith(array, start, offset)) {
|
||||||
|
byte[] actual = new byte[Math.min(start.length, array.length - offset)];
|
||||||
|
System.arraycopy(array, offset, actual, 0, actual.length);
|
||||||
|
fail("Array does not start with expected bytes at offset " + offset + ".\n" +
|
||||||
|
"Expected: <" + Arrays.toString(start) + ">\n" +
|
||||||
|
"Actual: <" + Arrays.toString(actual) + ">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
307
external-sop/src/test/java/sop/external/TestKeys.java
vendored
Normal file
307
external-sop/src/test/java/sop/external/TestKeys.java
vendored
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.external;
|
||||||
|
|
||||||
|
public class TestKeys {
|
||||||
|
|
||||||
|
// 'Alice' key from draft-bre-openpgp-samples-00
|
||||||
|
public static final String ALICE_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E\n" +
|
||||||
|
"Comment: Alice Lovelace <alice@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xjMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" +
|
||||||
|
"b7O1u13NJkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+wpAE\n" +
|
||||||
|
"ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy\n" +
|
||||||
|
"MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO\n" +
|
||||||
|
"dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gLO\n" +
|
||||||
|
"OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s\n" +
|
||||||
|
"E9+eviIDAQgHwngEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb\n" +
|
||||||
|
"DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn\n" +
|
||||||
|
"0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=\n" +
|
||||||
|
"=QX3Q\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
public static final String ALICE_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E\n" +
|
||||||
|
"Comment: Alice Lovelace <alice@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xVgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" +
|
||||||
|
"b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RzSZBbGlj\n" +
|
||||||
|
"ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPsKQBBMWCAA4AhsDBQsJ\n" +
|
||||||
|
"CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l\n" +
|
||||||
|
"nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf\n" +
|
||||||
|
"a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICx10EXEcE6RIKKwYB\n" +
|
||||||
|
"BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA\n" +
|
||||||
|
"/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK7CeAQYFggAIBYhBOuF\n" +
|
||||||
|
"u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM\n" +
|
||||||
|
"hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb\n" +
|
||||||
|
"Pnn+We1aTBhaGa86AQ==\n" +
|
||||||
|
"=3GfK\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
// 'Bob' key from draft-bre-openpgp-samples-00
|
||||||
|
public static final String BOB_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330\n" +
|
||||||
|
"Comment: Bob Babbage <bob@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" +
|
||||||
|
"/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz\n" +
|
||||||
|
"/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/\n" +
|
||||||
|
"5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3\n" +
|
||||||
|
"X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv\n" +
|
||||||
|
"9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0\n" +
|
||||||
|
"qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb\n" +
|
||||||
|
"SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb\n" +
|
||||||
|
"vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w\n" +
|
||||||
|
"bGU+wsEOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE0aZuGiOx\n" +
|
||||||
|
"gsmYD3iM+/zIKgFeczAFAl2lnvoACgkQ+/zIKgFeczBvbAv/VNk90a6hG8Od9xTz\n" +
|
||||||
|
"XxH5YRFUSGfIA1yjPIVOnKqhMwps2U+sWE3urL+MvjyQRlyRV8oY9IOhQ5Esm6DO\n" +
|
||||||
|
"ZYrTnE7qVETm1ajIAP2OFChEc55uH88x/anpPOXOJY7S8jbn3naC9qad75BrZ+3g\n" +
|
||||||
|
"9EBUWiy5p8TykP05WSnSxNRt7vFKLfEB4nGkehpwHXOVF0CRNwYle42bg8lpmdXF\n" +
|
||||||
|
"DcCZCi+qEbafmTQzkAqyzS3nCh3IAqq6Y0kBuaKLm2tSNUOlZbD+OHYQNZ5Jix7c\n" +
|
||||||
|
"ZUzs6Xh4+I55NRWl5smrLq66yOQoFPy9jot/Qxikx/wP3MsAzeGaZSEPc0fHp5G1\n" +
|
||||||
|
"6rlGbxQ3vl8/usUV7W+TMEMljgwd5x8POR6HC8EaCDfVnUBCPi/Gv+egLjsIbPJZ\n" +
|
||||||
|
"ZEroiE40e6/UoCiQtlpQB5exPJYSd1Q1txCwueih99PHepsDhmUQKiACszNU+RRo\n" +
|
||||||
|
"zAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpGzsDNBF2lnPIBDADW\n" +
|
||||||
|
"ML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI\n" +
|
||||||
|
"DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+\n" +
|
||||||
|
"Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AO\n" +
|
||||||
|
"baifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT\n" +
|
||||||
|
"86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/wGlQ01rh\n" +
|
||||||
|
"827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+paLNDdVPL6\n" +
|
||||||
|
"vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUnR76U\n" +
|
||||||
|
"qVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48A\n" +
|
||||||
|
"EQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzyAhsMAAoJ\n" +
|
||||||
|
"EPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+41IL4rVcS\n" +
|
||||||
|
"KhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZQanYmtSx\n" +
|
||||||
|
"cVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zpf3u0k14i\n" +
|
||||||
|
"tcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn3OWjCPHV\n" +
|
||||||
|
"dTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK2b0vk/+w\n" +
|
||||||
|
"qMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFAExaEK6Vy\n" +
|
||||||
|
"jP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWif9RSK4xj\n" +
|
||||||
|
"zRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj5KjhX2PV\n" +
|
||||||
|
"NEJd3XZRzaXZE2aAMQ==\n" +
|
||||||
|
"=F9yX\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
public static final String BOB_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330\n" +
|
||||||
|
"Comment: Bob Babbage <bob@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xcSYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" +
|
||||||
|
"/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz\n" +
|
||||||
|
"/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/\n" +
|
||||||
|
"5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3\n" +
|
||||||
|
"X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv\n" +
|
||||||
|
"9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0\n" +
|
||||||
|
"qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb\n" +
|
||||||
|
"SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb\n" +
|
||||||
|
"vLIwa3T4CyshfT0AEQEAAQAL/RZqbJW2IqQDCnJi4Ozm++gPqBPiX1RhTWSjwxfM\n" +
|
||||||
|
"cJKUZfzLj414rMKm6Jh1cwwGY9jekROhB9WmwaaKT8HtcIgrZNAlYzANGRCM4TLK\n" +
|
||||||
|
"3VskxfSwKKna8l+s+mZglqbAjUg3wmFuf9Tj2xcUZYmyRm1DEmcN2ZzpvRtHgX7z\n" +
|
||||||
|
"Wn1mAKUlSDJZSQks0zjuMNbupcpyJokdlkUg2+wBznBOTKzgMxVNC9b2g5/tMPUs\n" +
|
||||||
|
"hGGWmF1UH+7AHMTaS6dlmr2ZBIyogdnfUqdNg5sZwsxSNrbglKP4sqe7X61uEAIQ\n" +
|
||||||
|
"bD7rT3LonLbhkrj3I8wilUD8usIwt5IecoHhd9HziqZjRCc1BUBkboUEoyedbDV4\n" +
|
||||||
|
"i4qfsFZ6CEWoLuD5pW7dEp0M+WeuHXO164Rc+LnH6i1VQrpb1Okl4qO6ejIpIjBI\n" +
|
||||||
|
"1t3GshtUu/mwGBBxs60KBX5g77mFQ9lLCRj8lSYqOsHRKBhUp4qM869VA+fD0BRP\n" +
|
||||||
|
"fqPT0I9IH4Oa/A3jYJcg622GwQYA1LhnP208Waf6PkQSJ6kyr8ymY1yVh9VBE/g6\n" +
|
||||||
|
"fRDYA+pkqKnw9wfH2Qho3ysAA+OmVOX8Hldg+Pc0Zs0e5pCavb0En8iFLvTA0Q2E\n" +
|
||||||
|
"LR5rLue9uD7aFuKFU/VdcddY9Ww/vo4k5p/tVGp7F8RYCFn9rSjIWbfvvZi1q5Tx\n" +
|
||||||
|
"+akoZbga+4qQ4WYzB/obdX6SCmi6BndcQ1QdjCCQU6gpYx0MddVERbIp9+2SXDyL\n" +
|
||||||
|
"hpxjSyz+RGsZi/9UAshT4txP4+MZBgDfK3ZqtW+h2/eMRxkANqOJpxSjMyLO/FXN\n" +
|
||||||
|
"WxzTDYeWtHNYiAlOwlQZEPOydZFty9IVzzNFQCIUCGjQ/nNyhw7adSgUk3+BXEx/\n" +
|
||||||
|
"MyJPYY0BYuhLxLYcrfQ9nrhaVKxRJj25SVHj2ASsiwGJRZW4CC3uw40OYxfKEvNC\n" +
|
||||||
|
"mer/VxM3kg8qqGf9KUzJ1dVdAvjyx2Hz6jY2qWCyRQ6IMjWHyd43C4r3jxooYKUC\n" +
|
||||||
|
"YnstRQyb/gCSKahveSEjo07CiXMr88UGALwzEr3npFAsPW3osGaFLj49y1oRe11E\n" +
|
||||||
|
"he9gCHFm+fuzbXrWmdPjYU5/ZdqdojzDqfu4ThfnipknpVUM1o6MQqkjM896FHm8\n" +
|
||||||
|
"zbKVFSMhEP6DPHSCexMFrrSgN03PdwHTO6iBaIBBFqmGY01tmJ03SxvSpiBPON9P\n" +
|
||||||
|
"NVvy/6UZFedTq8A07OUAxO62YUSNtT5pmK2vzs3SAZJmbFbMh+NN204TRI72GlqT\n" +
|
||||||
|
"t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qizSFCb2IgQmFiYmFnZSA8Ym9iQG9w\n" +
|
||||||
|
"ZW5wZ3AuZXhhbXBsZT7CwQ4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC\n" +
|
||||||
|
"F4AWIQTRpm4aI7GCyZgPeIz7/MgqAV5zMAUCXaWe+gAKCRD7/MgqAV5zMG9sC/9U\n" +
|
||||||
|
"2T3RrqEbw533FPNfEflhEVRIZ8gDXKM8hU6cqqEzCmzZT6xYTe6sv4y+PJBGXJFX\n" +
|
||||||
|
"yhj0g6FDkSyboM5litOcTupURObVqMgA/Y4UKERznm4fzzH9qek85c4ljtLyNufe\n" +
|
||||||
|
"doL2pp3vkGtn7eD0QFRaLLmnxPKQ/TlZKdLE1G3u8Uot8QHicaR6GnAdc5UXQJE3\n" +
|
||||||
|
"BiV7jZuDyWmZ1cUNwJkKL6oRtp+ZNDOQCrLNLecKHcgCqrpjSQG5oouba1I1Q6Vl\n" +
|
||||||
|
"sP44dhA1nkmLHtxlTOzpeHj4jnk1FaXmyasurrrI5CgU/L2Oi39DGKTH/A/cywDN\n" +
|
||||||
|
"4ZplIQ9zR8enkbXquUZvFDe+Xz+6xRXtb5MwQyWODB3nHw85HocLwRoIN9WdQEI+\n" +
|
||||||
|
"L8a/56AuOwhs8llkSuiITjR7r9SgKJC2WlAHl7E8lhJ3VDW3ELC56KH308d6mwOG\n" +
|
||||||
|
"ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikbH\n" +
|
||||||
|
"xJgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD\n" +
|
||||||
|
"bUdZqZeef2xJe8gMQg05DoD1DF3GipZ0Ies65beh+d5hegb7N4pzh0LzrBrVNHar\n" +
|
||||||
|
"29b5ExdI7i4iYD5TO6Vr/qTUOiAN/byqELEzAb+L+b2DVz/RoCm4PIp1DU9ewcc2\n" +
|
||||||
|
"WB38Ofqut3nLYA5tqJ9XvAiEQme+qAVcM3ZFcaMt4I4dXhDZZNg+D9LiTWcxdUPB\n" +
|
||||||
|
"leu8iwDRjAgyAhPzpFp+nWoqWA81uIiULWD1Fj+IVoY3ZvgivoYOiEFBJ9lbb4te\n" +
|
||||||
|
"g9m5UT/AaVDTWuHzbspVlbiVe+qyB77C2daWzNyx6UYBPLOo4r0t0c91kbNE5lgj\n" +
|
||||||
|
"Z7xz6los0N1U8vq91EFSeQJoSQ62XWavYmlCLmdNT6BNfgh4icLsT7Vr1QMX9jzn\n" +
|
||||||
|
"JtTPxdXytSdHvpSpULsqJ016l0dtmONcK3z9mj5N5z0k1tg1AH970TGYOe2aUcSx\n" +
|
||||||
|
"IRDMXDOPyzEfjwARAQABAAv9F2CwsjS+Sjh1M1vegJbZjei4gF1HHpEM0K0PSXsp\n" +
|
||||||
|
"SfVvpR4AoSJ4He6CXSMWg0ot8XKtDuZoV9jnJaES5UL9pMAD7JwIOqZm/DYVJM5h\n" +
|
||||||
|
"OASCh1c356/wSbFbzRHPtUdZO9Q30WFNJM5pHbCJPjtNoRmRGkf71RxtvHBzy7np\n" +
|
||||||
|
"Ga+W6U/NVKHw0i0CYwMI0YlKDakYW3Pm+QL+gHZFvngGweTod0f9l2VLLAmeQR/c\n" +
|
||||||
|
"+EZs7lNumhuZ8mXcwhUc9JQIhOkpO+wreDysEFkAcsKbkQP3UDUsA1gFx9pbMzT0\n" +
|
||||||
|
"tr1oZq2a4QBtxShHzP/ph7KLpN+6qtjks3xB/yjTgaGmtrwM8tSe0wD1RwXS+/1o\n" +
|
||||||
|
"BHpXTnQ7TfeOGUAu4KCoOQLv6ELpKWbRBLWuiPwMdbGpvVFALO8+kvKAg9/r+/ny\n" +
|
||||||
|
"zM2GQHY+J3Jh5JxPiJnHfXNZjIKLbFbIPdSKNyJBuazXW8xIa//mEHMI5OcvsZBK\n" +
|
||||||
|
"clAIp7LXzjEjKXIwHwDcTn9pBgDpdOKTHOtJ3JUKx0rWVsDH6wq6iKV/FTVSY5jl\n" +
|
||||||
|
"zN+puOEsskF1Lfxn9JsJihAVO3yNsp6RvkKtyNlFazaCVKtDAmkjoh60XNxcNRqr\n" +
|
||||||
|
"gCnwdpbgdHP6v/hvZY54ZaJjz6L2e8unNEkYLxDt8cmAyGPgH2XgL7giHIp9jrsQ\n" +
|
||||||
|
"aS381gnYwNX6wE1aEikgtY91nqJjwPlibF9avSyYQoMtEqM/1UjTjB2KdD/MitK5\n" +
|
||||||
|
"fP0VpvuXpNYZedmyq4UOMwdkiNMGAOrfmOeT0olgLrTMT5H97Cn3Yxbk13uXHNu/\n" +
|
||||||
|
"ZUZZNe8s+QtuLfUlKAJtLEUutN33TlWQY522FV0m17S+b80xJib3yZVJteVurrh5\n" +
|
||||||
|
"HSWHAM+zghQAvCesg5CLXa2dNMkTCmZKgCBvfDLZuZbjFwnwCI6u/NhOY9egKuUf\n" +
|
||||||
|
"SA/je/RXaT8m5VxLYMxwqQXKApzD87fv0tLPlVIEvjEsaf992tFEFSNPcG1l/jpd\n" +
|
||||||
|
"5AVXw6kKuf85UkJtYR1x2MkQDrqY1QX/XMw00kt8y9kMZUre19aCArcmor+hDhRJ\n" +
|
||||||
|
"E3Gt4QJrD9z/bICESw4b4z2DbgD/Xz9IXsA/r9cKiM1h5QMtXvuhyfVeM01enhxM\n" +
|
||||||
|
"GbOH3gjqqGNKysx0UODGEwr6AV9hAd8RWXMchJLaExK9J5SRawSg671ObAU24SdY\n" +
|
||||||
|
"vMQ9Z4kAQ2+1ReUZzf3ogSMRZtMT+d18gT6L90/y+APZIaoArLPhebIAGq39HLmJ\n" +
|
||||||
|
"26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hrCwPYEGAEKACAWIQTRpm4aI7GCyZgP\n" +
|
||||||
|
"eIz7/MgqAV5zMAUCXaWc8gIbDAAKCRD7/MgqAV5zMOn/C/9ugt+HZIwX308zI+QX\n" +
|
||||||
|
"c5vDLReuzmJ3ieE0DMO/uNSC+K1XEioSIZP91HeZJ2kbT9nn9fuReuoff0T0Dief\n" +
|
||||||
|
"rbwcIQQHFFkrqSp1K3VWmUGp2JrUsXFVdjy/fkBIjTd7c5boWljv/6wAsSfiv2V0\n" +
|
||||||
|
"JSM8EFU6TYXxswGjFVfc6X97tJNeIrXL+mpSmPPqy2bztcCCHkWS5lNLWQw+R7Vg\n" +
|
||||||
|
"71Fe6yBSNVrqC2/imYG2J9zlowjx1XU63Wdgqp2Wxt0l8OmsB/W80S1fRF5G4SDH\n" +
|
||||||
|
"s9HXglXXqPsBRZJYfP+VStm9L5P/sKjCcX6WtZR7yS6G8zj/X767MLK/djANvpPd\n" +
|
||||||
|
"NVniEke6hM3CNBXYPAMhQBMWhCulcoz+0lxi8L34rMN+Dsbma96psdUrn7uLaB91\n" +
|
||||||
|
"6we0CTfF8qqm7BsVAgalon/UUiuMY80U3ueoj3okiSTiHIjD/YtpXSPioC8nMng7\n" +
|
||||||
|
"xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE=\n" +
|
||||||
|
"=FAzO\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
// 'Carol' key from draft-bre-openpgp-samples-00
|
||||||
|
public static final String CAROL_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Comment: 71FF DA00 4409 E5DD B0C3 E8F1 9BA7 89DC 76D6 849A\n" +
|
||||||
|
"Comment: Carol Oldstyle <carol@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xsPuBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0\n" +
|
||||||
|
"OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh\n" +
|
||||||
|
"yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj\n" +
|
||||||
|
"REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG\n" +
|
||||||
|
"zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7\n" +
|
||||||
|
"MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9\n" +
|
||||||
|
"+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX\n" +
|
||||||
|
"duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0\n" +
|
||||||
|
"SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH\n" +
|
||||||
|
"5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS\n" +
|
||||||
|
"KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp\n" +
|
||||||
|
"dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP\n" +
|
||||||
|
"xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8\n" +
|
||||||
|
"2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo\n" +
|
||||||
|
"mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4\n" +
|
||||||
|
"xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU\n" +
|
||||||
|
"yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL\n" +
|
||||||
|
"/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl\n" +
|
||||||
|
"5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb\n" +
|
||||||
|
"zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb\n" +
|
||||||
|
"f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq\n" +
|
||||||
|
"paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0\n" +
|
||||||
|
"XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz\n" +
|
||||||
|
"GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W\n" +
|
||||||
|
"ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP\n" +
|
||||||
|
"IcKKBB8RCAA8BQJd/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYh\n" +
|
||||||
|
"BHH/2gBECeXdsMPo8Zunidx21oSaAABihQD/VWnF1HbBhP+kLwWsqxuYjEslEsM2\n" +
|
||||||
|
"UQPeKGK9an8HZ78BAJPaiL3OpuOmsIoCfOghhMZOKXjIV+Z57LwaMw7FQfPgzSZD\n" +
|
||||||
|
"YXJvbCBPbGRzdHlsZSA8Y2Fyb2xAb3BlbnBncC5leGFtcGxlPsKKBBMRCAA8BQJd\n" +
|
||||||
|
"/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYhBHH/2gBECeXdsMPo\n" +
|
||||||
|
"8Zunidx21oSaAABQTAD/ZMXAvSbKaMJJpAfwp1C7KAj6K2k2CAz5jwUXyGf1+jUA\n" +
|
||||||
|
"/2iAMiX1XcLy3n0L8ytzge8/UAFHafBl4rn4DmUugfhjzsPMBF3+CmgQDADZhdKT\n" +
|
||||||
|
"M3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzgI//Y\n" +
|
||||||
|
"1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587\n" +
|
||||||
|
"KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZk\n" +
|
||||||
|
"eJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxo\n" +
|
||||||
|
"euXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG\n" +
|
||||||
|
"976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9+4dq6ybUM65tnozRyyN+\n" +
|
||||||
|
"1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UVNH5/KXMV\n" +
|
||||||
|
"czVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sMAIfl\n" +
|
||||||
|
"/H7JQB1nxW7/bNZMfHBYn9fqAZMupr0KZ8OrlQOpgUXO5bA3gcn6vI65qTUIbBIo\n" +
|
||||||
|
"lQFIDvkcTFu/SdpaD6y7L6kQO8XRUAs9T1VSRJC0fJHXRg7YVY57cAS2ltgNHCl2\n" +
|
||||||
|
"vVnARtvcvogZDmL/gI0dsna7fJR5ewM0C+ulVIRwiMDTVE8I4qZ/nxINmnjIN0/E\n" +
|
||||||
|
"aEzzDprXz591CvbZ/ZwnTGB8+VvMVs74VSwSAq+fpBMuFtpjDjOzut1AN6NYdXza\n" +
|
||||||
|
"E/gr6tv0XCSdh1X26jibvsyAaVT7jK8mcYRhovePCMjdsf1qig06Xpdu9UDM3OiZ\n" +
|
||||||
|
"iZpM7uanrEUC7jfK4bJ30r7UTiTsJBNE7FNn5F21CNX3mFKwSYyDv3adC8NIFbjH\n" +
|
||||||
|
"B85Dul/eQLuv1+by72cGUQ3XYextDxi+7H+V3mrlFoiUPX2PN9VHr6EnNuPZmdTJ\n" +
|
||||||
|
"CziSwB8gdPNN0u21HFL2VNFORXHa9tSehIHLpNgXWZ/qdE+lKbWuJnGeRHj4FAv+\n" +
|
||||||
|
"MQaafW0uHF+N8MDm8UWPvf4Vd0UJ0UpIjRWl2hTV+BHkNfvZlBRhhQIphNiKRe/W\n" +
|
||||||
|
"ap0f/lW2Gm2uS0KgByjjNXEzTiwrte2GX65M6F6Lz8N31kt1Iig1xGOuv+6HmxTN\n" +
|
||||||
|
"R8gL2K5PdJeJn8PTJWrRS7+BY8Hdkgb+wVpzE5cCvpFiG/P0yqfBdLWxVPlPI7dc\n" +
|
||||||
|
"hDkmx4iAhHJX9J/gX/hC6L3AzPNJqNPAKy20wYp/ruTbbwBolW/4ikWij460JrvB\n" +
|
||||||
|
"sm6Sp81A3ebaiN9XkJygLOyhGyhMieGulCYz6AahAFcECtPXGTcordV1mJth8yjF\n" +
|
||||||
|
"4gZfDQyg0nMW4Yr49yeFXcRMUw1yzN3Q9v2zzqDuFi2lGYTXYmVqLYzM9KbLO2Wx\n" +
|
||||||
|
"E/21xnBjLsl09l/FdA/bhdZq3t4/apbFOeQQ/j/AphvzWbsJnhG9Q7+d3VoDlz0g\n" +
|
||||||
|
"FiSduCYIAAq8dUOJNjrUTkZsL1pOIjhYjCMi2uiKS6RQkT6nvuumPF/D/VTnUGeZ\n" +
|
||||||
|
"wooEGBEIADwFAl3+CmkDCwkKCRCbp4ncdtaEmgQVCgkIAhYBAheAAhsMAh4BFiEE\n" +
|
||||||
|
"cf/aAEQJ5d2ww+jxm6eJ3HbWhJoAAEEpAP91hFqmcb2ZqVcaRDMSVmhkEcFIRmpH\n" +
|
||||||
|
"vDoQtVn8sArWqwEAi8HwbMhL+YwRItRZDknpC4vFjTHVMd1zMrz/JyeuT9k=\n" +
|
||||||
|
"=pa/S\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
public static final String CAROL_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Comment: 71FF DA00 4409 E5DD B0C3 E8F1 9BA7 89DC 76D6 849A\n" +
|
||||||
|
"Comment: Carol Oldstyle <carol@openpgp.example>\n" +
|
||||||
|
"\n" +
|
||||||
|
"xcQTBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0\n" +
|
||||||
|
"OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh\n" +
|
||||||
|
"yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj\n" +
|
||||||
|
"REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG\n" +
|
||||||
|
"zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7\n" +
|
||||||
|
"MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9\n" +
|
||||||
|
"+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX\n" +
|
||||||
|
"duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0\n" +
|
||||||
|
"SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH\n" +
|
||||||
|
"5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS\n" +
|
||||||
|
"KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp\n" +
|
||||||
|
"dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP\n" +
|
||||||
|
"xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8\n" +
|
||||||
|
"2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo\n" +
|
||||||
|
"mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4\n" +
|
||||||
|
"xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU\n" +
|
||||||
|
"yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL\n" +
|
||||||
|
"/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl\n" +
|
||||||
|
"5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb\n" +
|
||||||
|
"zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb\n" +
|
||||||
|
"f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq\n" +
|
||||||
|
"paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0\n" +
|
||||||
|
"XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz\n" +
|
||||||
|
"GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W\n" +
|
||||||
|
"ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP\n" +
|
||||||
|
"IQAA/2BCN5HryGjVff2t7Q6fVrQQS9hsMisszZl5rWwUOO6zETHCigQfEQgAPAUC\n" +
|
||||||
|
"Xf4KaQMLCQoJEJunidx21oSaBBUKCQgCFgECF4ACGwMCHgEWIQRx/9oARAnl3bDD\n" +
|
||||||
|
"6PGbp4ncdtaEmgAAYoUA/1VpxdR2wYT/pC8FrKsbmIxLJRLDNlED3ihivWp/B2e/\n" +
|
||||||
|
"AQCT2oi9zqbjprCKAnzoIYTGTil4yFfmeey8GjMOxUHz4M0mQ2Fyb2wgT2xkc3R5\n" +
|
||||||
|
"bGUgPGNhcm9sQG9wZW5wZ3AuZXhhbXBsZT7CigQTEQgAPAUCXf4KaQMLCQoJEJun\n" +
|
||||||
|
"idx21oSaBBUKCQgCFgECF4ACGwMCHgEWIQRx/9oARAnl3bDD6PGbp4ncdtaEmgAA\n" +
|
||||||
|
"UEwA/2TFwL0mymjCSaQH8KdQuygI+itpNggM+Y8FF8hn9fo1AP9ogDIl9V3C8t59\n" +
|
||||||
|
"C/Mrc4HvP1ABR2nwZeK5+A5lLoH4Y8fD8QRd/gpoEAwA2YXSkzN5rN16V50JHvNx\n" +
|
||||||
|
"YGiAbT9YNaoaqQn4OdFoj0tJI4jAtDic9r4efZ7rGwS84CP/2NVTISnyFmG6jHCG\n" +
|
||||||
|
"PpVm7Hh45edq6lugGidEx+DYFbe74clXibdJPzZ8bzYTHdOfOyl5n6Q8a8AanP5e\n" +
|
||||||
|
"XFQfqdKy/L7PJMaIx1wIuVd5KDNFI0RFrOSaY/11PS4RKMl2ZHiQv6XrNbulCqBW\n" +
|
||||||
|
"J+3RSD+PSpHdZG/tWzX3T2LQNCaXBs2IHjDTr3VicJ+N3TYcaHrl35gBIQPC3c09\n" +
|
||||||
|
"AtDvu2pFzilq34VyfDEwarz4FmWMezDbkMf3oyDGR5fiGn+4Rve+iCx/jQhoipIY\n" +
|
||||||
|
"nXfRiLgP1rXh4kG1y8n4kOJ/D9dqvfuHausm1DOubZ6M0csjftZt61Nmv/i8tyQo\n" +
|
||||||
|
"eE3jtu8PnMTFpGnh8k0GiVTGzGw6V3blXd9jAN91FTR+fylzFXM1YuWrFY7ig0qI\n" +
|
||||||
|
"yQ1dUMF/Is2TZdbfgCNC922pQmm1dEhYZX5wRFI9ZstbDACH5fx+yUAdZ8Vu/2zW\n" +
|
||||||
|
"THxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwSKJUBSA75HExbv0na\n" +
|
||||||
|
"Wg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwpdr1ZwEbb3L6IGQ5i\n" +
|
||||||
|
"/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdPxGhM8w6a18+fdQr2\n" +
|
||||||
|
"2f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV82hP4K+rb9FwknYdV\n" +
|
||||||
|
"9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzomYmaTO7mp6xFAu43\n" +
|
||||||
|
"yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4xwfOQ7pf3kC7r9fm\n" +
|
||||||
|
"8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnUyQs4ksAfIHTzTdLt\n" +
|
||||||
|
"tRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL/jEGmn1tLhxfjfDA\n" +
|
||||||
|
"5vFFj73+FXdFCdFKSI0VpdoU1fgR5DX72ZQUYYUCKYTYikXv1mqdH/5VthptrktC\n" +
|
||||||
|
"oAco4zVxM04sK7Xthl+uTOhei8/Dd9ZLdSIoNcRjrr/uh5sUzUfIC9iuT3SXiZ/D\n" +
|
||||||
|
"0yVq0Uu/gWPB3ZIG/sFacxOXAr6RYhvz9MqnwXS1sVT5TyO3XIQ5JseIgIRyV/Sf\n" +
|
||||||
|
"4F/4Qui9wMzzSajTwCsttMGKf67k228AaJVv+IpFoo+OtCa7wbJukqfNQN3m2ojf\n" +
|
||||||
|
"V5CcoCzsoRsoTInhrpQmM+gGoQBXBArT1xk3KK3VdZibYfMoxeIGXw0MoNJzFuGK\n" +
|
||||||
|
"+PcnhV3ETFMNcszd0Pb9s86g7hYtpRmE12Jlai2MzPSmyztlsRP9tcZwYy7JdPZf\n" +
|
||||||
|
"xXQP24XWat7eP2qWxTnkEP4/wKYb81m7CZ4RvUO/nd1aA5c9IBYknbgmCAAKvHVD\n" +
|
||||||
|
"iTY61E5GbC9aTiI4WIwjItroikukUJE+p77rpjxfw/1U51BnmQAA/ih5jIthn2ZE\n" +
|
||||||
|
"r1YoOsUs8CBhylTsRZK6VS4ZCErcyl2tD2LCigQYEQgAPAUCXf4KaQMLCQoJEJun\n" +
|
||||||
|
"idx21oSaBBUKCQgCFgECF4ACGwwCHgEWIQRx/9oARAnl3bDD6PGbp4ncdtaEmgAA\n" +
|
||||||
|
"QSkA/3WEWqZxvZmpVxpEMxJWaGQRwUhGake8OhC1WfywCtarAQCLwfBsyEv5jBEi\n" +
|
||||||
|
"1FkOSekLi8WNMdUx3XMyvP8nJ65P2Q==\n" +
|
||||||
|
"=Xj8h\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
}
|
|
@ -219,6 +219,10 @@ public abstract class SOPGPException extends RuntimeException {
|
||||||
|
|
||||||
public static final int EXIT_CODE = 53;
|
public static final int EXIT_CODE = 53;
|
||||||
|
|
||||||
|
public ExpectedText() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public ExpectedText(String message) {
|
public ExpectedText(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue