diff --git a/bcsop-cli/pom.xml b/bcsop-cli/pom.xml
index 2f14c57..03173ba 100644
--- a/bcsop-cli/pom.xml
+++ b/bcsop-cli/pom.xml
@@ -31,6 +31,47 @@
org.bouncycastle
bcprov-jdk18on
+
+ org.bouncycastle
+ bcpg-jdk18on
+
+
+ org.bouncycastle
+ bcutil-jdk18on
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ ${maven.assembly.plugin.version}
+
+
+ jar-with-dependencies
+
+
+
+ org.pgpainless.BcSopCLI
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bcsop/pom.xml b/bcsop/pom.xml
index ea8203c..c945daf 100644
--- a/bcsop/pom.xml
+++ b/bcsop/pom.xml
@@ -10,6 +10,7 @@
bcsop
+ jar
21
@@ -30,6 +31,23 @@
org.bouncycastle
bcpg-jdk18on
+
+ org.bouncycastle
+ bcutil-jdk18on
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+
\ No newline at end of file
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java
index b50d8ba..7fc22a9 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/BouncyCastleSOP.java
@@ -1,12 +1,21 @@
package org.pgpainless.bouncycastle.sop;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jetbrains.annotations.NotNull;
import org.pgpainless.bouncycastle.sop.operation.*;
import sop.SOP;
import sop.operation.*;
+import java.security.Security;
+
public class BouncyCastleSOP implements SOP {
+ public BouncyCastleSOP()
+ {
+ Security.removeProvider("BC");
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
@NotNull
@Override
public Armor armor() {
@@ -16,7 +25,7 @@ public class BouncyCastleSOP implements SOP {
@NotNull
@Override
public GenerateKey generateKey() {
- return null;
+ return new BCGenerateKey();
}
@NotNull
@@ -28,13 +37,13 @@ public class BouncyCastleSOP implements SOP {
@NotNull
@Override
public DetachedSign detachedSign() {
- return null;
+ return new BCDetachedSign();
}
@NotNull
@Override
public InlineSign inlineSign() {
- return null;
+ return new BCInlineSign();
}
@NotNull
@@ -46,13 +55,13 @@ public class BouncyCastleSOP implements SOP {
@NotNull
@Override
public Encrypt encrypt() {
- return null;
+ return new BCEncrypt();
}
@NotNull
@Override
public Decrypt decrypt() {
- return null;
+ return new BCDecrypt();
}
@NotNull
@@ -88,12 +97,12 @@ public class BouncyCastleSOP implements SOP {
@NotNull
@Override
public DetachedVerify detachedVerify() {
- return null;
+ return new BCDetachedVerify();
}
@NotNull
@Override
public InlineVerify inlineVerify() {
- return null;
+ return new BCInlineVerify();
}
}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java
new file mode 100644
index 0000000..8ca11c0
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/DetachedVerifyTest.java
@@ -0,0 +1,97 @@
+package org.pgpainless.bouncycastle.sop;
+
+import sop.Verification;
+import sop.operation.DetachedVerify;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+public class DetachedVerifyTest {
+
+ static String SIG = "-----BEGIN PGP SIGNATURE-----\n" +
+ "\n" +
+ "wnUEABYKAB0WIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCZ1mi4QAKCRDyMVUMT0fj\n" +
+ "jun3AP9ZiRPXlow7ywR3D7nC4/GGUC+PueZ1GkcXDtE8zbAiTgEArkOQO8qhC0QG\n" +
+ "K6TKEPJM07Zie/kgcGyh8HfSpbunwAXCwPMEAAEKAB0WIQTRpm4aI7GCyZgPeIz7\n" +
+ "/MgqAV5zMAUCZ1mi4QAKCRD7/MgqAV5zMI51C/sFv6vjBbxdgNMp1qwJ3AqMhbtV\n" +
+ "WJ7z8HJraT+dW0VKYmib7H7lL3x3gTC85VaOWGU3mqhxZLQVi6j99pbbZjGXJADF\n" +
+ "e5E1S+tp6js56OI4fY/kchLMcz1JxgjSJPJnlyEXy5C2/2RlI1JmpUn6Y8YOlNhZ\n" +
+ "Vbov36QB2ILt+k62ElQeHgWQYqWWMmxmdk9wipNKYfBoQNVwJNICDjI+XlwL4cGw\n" +
+ "nyg3cdgkKXikPFEBhlApI5HmX4jOv68gReEMMTaZoQoRD8kzg9m7KC6BqHpG0TAZ\n" +
+ "Bz5TzZrxW1LdXj/tKqNONcldHHzO4mZIUkdOowGMBmEf0kZ2nVHZGaWE1T7IqJPo\n" +
+ "cIPtZB+EpDCcPQN35ekC0B1ilDd07IulNHOdbhI7xu0/eQb6dGpkzkak3OpWbGtX\n" +
+ "rKfxljqLFTnnJfM95UlrlCWwEplRZ2q4QhXi8/mRM/Szz5D3KGd7hnLRnr42J4Dr\n" +
+ "j/XWKoSXmFZ5t6CBq7BgubE1uO2ToRWOlas+jNE=\n" +
+ "=bJ8+\n" +
+ "-----END PGP SIGNATURE-----";
+ static String ALICE = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
+ "Comment: Alice's OpenPGP certificate\n" +
+ "\n" +
+ "mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" +
+ "b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE\n" +
+ "ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy\n" +
+ "MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO\n" +
+ "dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4\n" +
+ "OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s\n" +
+ "E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb\n" +
+ "DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn\n" +
+ "0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=\n" +
+ "=iIGO\n" +
+ "-----END PGP PUBLIC KEY BLOCK-----";
+ static String BOB = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
+ "Comment: Bob's OpenPGP certificate\n" +
+ "\n" +
+ "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\n" +
+ "NEJd3XZRzaXZE2aAMQ==\n" +
+ "=NXei\n" +
+ "-----END PGP PUBLIC KEY BLOCK-----";
+ static String MSG = "Hello World :)";
+
+ public static void main(String[] args) throws IOException {
+ BouncyCastleSOP sop = new BouncyCastleSOP();
+ ByteArrayInputStream sigIn = new ByteArrayInputStream(SIG.getBytes(StandardCharsets.UTF_8));
+ DetachedVerify verify = sop.detachedVerify();
+ verify.signatures(sigIn);
+ verify.cert(new ByteArrayInputStream(ALICE.getBytes(StandardCharsets.UTF_8)))
+ .cert(new ByteArrayInputStream(BOB.getBytes(StandardCharsets.UTF_8)));
+ List verif = verify.data(new ByteArrayInputStream(MSG.getBytes(StandardCharsets.UTF_8)));
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java
new file mode 100644
index 0000000..9a37617
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/AbstractBCOperation.java
@@ -0,0 +1,41 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.PGPSessionKey;
+import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream;
+import org.bouncycastle.openpgp.api.OpenPGPSignature;
+import org.bouncycastle.util.encoders.Hex;
+import sop.SessionKey;
+import sop.Verification;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractBCOperation
+{
+
+ protected SessionKey getSessionKey(OpenPGPMessageInputStream.Result result)
+ {
+ PGPSessionKey sessionKey = result.getSessionKey();
+ if (sessionKey == null)
+ {
+ return null;
+ }
+ return new SessionKey((byte) sessionKey.getAlgorithm(), sessionKey.getKey());
+ }
+
+ protected List getVerifications(OpenPGPMessageInputStream.Result result)
+ {
+ List verifications = new ArrayList<>();
+ for (OpenPGPSignature.OpenPGPDocumentSignature sig : result.getSignatures())
+ {
+ if (sig.isValid())
+ {
+ Verification verification = new Verification(sig.getCreationTime(),
+ Hex.toHexString(sig.getIssuer().getKeyIdentifier().getFingerprint()),
+ Hex.toHexString(sig.getIssuerCertificate().getFingerprint()));
+ verifications.add(verification);
+ }
+ }
+ return verifications;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java
index 9de9904..cdc1a16 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCArmor.java
@@ -12,7 +12,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-public class BCArmor implements Armor {
+public class BCArmor
+ extends AbstractBCOperation
+ implements Armor {
@NotNull
@Override
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java
index d1b2287..78f3de7 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDearmor.java
@@ -11,7 +11,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-public class BCDearmor implements Dearmor {
+public class BCDearmor
+ extends AbstractBCOperation
+ implements Dearmor {
@NotNull
@Override
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java
index 251c48f..7b65fad 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDecrypt.java
@@ -1,9 +1,11 @@
package org.pgpainless.bouncycastle.sop.operation;
import org.bouncycastle.openpgp.*;
-import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
-import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory;
-import org.bouncycastle.openpgp.operator.bc.BcSessionKeyDataDecryptorFactory;
+import org.bouncycastle.openpgp.api.OpenPGPCertificate;
+import org.bouncycastle.openpgp.api.OpenPGPKey;
+import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream;
+import org.bouncycastle.openpgp.api.OpenPGPMessageProcessor;
+import org.bouncycastle.util.io.Streams;
import org.jetbrains.annotations.NotNull;
import sop.DecryptionResult;
import sop.ReadyWithResult;
@@ -14,21 +16,21 @@ import sop.operation.Decrypt;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-public class BCDecrypt implements Decrypt {
+public class BCDecrypt
+ extends AbstractBCOperation
+ implements Decrypt {
private Date notBefore = new Date(Long.MAX_VALUE); // end of time
private Date notAfter = new Date(); // now
- private final List sessionKeys = new ArrayList<>();
- private final List messagePassphrases = new ArrayList<>();
- private final List verificationCerts = new ArrayList<>();
private final List encryptionKeys = new ArrayList<>();
private final List encryptionKeyPassphrases = new ArrayList<>();
+ private final OpenPGPMessageProcessor processor = new OpenPGPMessageProcessor();
+
@NotNull
@Override
@@ -36,32 +38,17 @@ public class BCDecrypt implements Decrypt {
return new ReadyWithResult() {
@Override
public DecryptionResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException {
- InputStream in = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory objFac = new BcPGPObjectFactory(in);
- Object next = objFac.nextObject();
-
- if (next instanceof PGPEncryptedDataList) {
- PGPEncryptedDataList encDataList = (PGPEncryptedDataList) next;
-
- if (!sessionKeys.isEmpty()) {
- PGPSessionKeyEncryptedData sesKeyEnc = encDataList.extractSessionKeyEncryptedData();
- SessionKeyDataDecryptorFactory decFac = new BcSessionKeyDataDecryptorFactory(sessionKeys.get(0));
- try {
- in = sesKeyEnc.getDataStream(decFac);
- objFac = new BcPGPObjectFactory(in);
- next = objFac.nextObject();
- } catch (PGPException e) {
- throw new RuntimeException(e);
- }
- } else {
- for (PGPEncryptedData encData : encDataList) {
- if (encData instanceof PGPPublicKeyEncryptedData) {
-
- }
- }
- }
+ try {
+ OpenPGPMessageInputStream mIn = processor.process(inputStream);
+ Streams.pipeAll(mIn, outputStream);
+ mIn.close();
+ OpenPGPMessageInputStream.Result result = mIn.getResult();
+ return new DecryptionResult(
+ getSessionKey(result),
+ getVerifications(result));
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
}
- return null;
}
};
}
@@ -83,34 +70,30 @@ public class BCDecrypt implements Decrypt {
@NotNull
@Override
public Decrypt verifyWithCert(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
- InputStream decodeIn = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn);
- PGPPublicKeyRing cert = (PGPPublicKeyRing) objFac.nextObject();
- verificationCerts.add(cert);
+ OpenPGPCertificate cert = OpenPGPCertificate.fromInputStream(inputStream);
+ processor.addVerificationCertificate(cert);
return this;
}
@NotNull
@Override
public Decrypt withSessionKey(@NotNull SessionKey sessionKey) throws SOPGPException.UnsupportedOption {
- this.sessionKeys.add(new PGPSessionKey(sessionKey.getAlgorithm(), sessionKey.getKey()));
+ processor.setSessionKey(new PGPSessionKey(sessionKey.getAlgorithm(), sessionKey.getKey()));
return this;
}
@NotNull
@Override
public Decrypt withPassword(@NotNull String s) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
- this.messagePassphrases.add(s);
+ processor.addMessagePassphrase(s.toCharArray());
return this;
}
@NotNull
@Override
public Decrypt withKey(@NotNull InputStream inputStream) throws SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
- InputStream decodeIn = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn);
- PGPSecretKeyRing key = (PGPSecretKeyRing) objFac.nextObject();
- this.encryptionKeys.add(key);
+ OpenPGPKey key = OpenPGPKey.fromInputStream(inputStream);
+ processor.addDecryptionKey(key);
return this;
}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java
new file mode 100644
index 0000000..87562cd
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedSign.java
@@ -0,0 +1,116 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.BCPGOutputStream;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.api.OpenPGPDetachedSignatureGenerator;
+import org.bouncycastle.openpgp.api.OpenPGPKey;
+import org.bouncycastle.openpgp.api.OpenPGPSignature;
+import org.bouncycastle.openpgp.api.exception.InvalidSigningKeyException;
+import org.jetbrains.annotations.NotNull;
+import sop.MicAlg;
+import sop.ReadyWithResult;
+import sop.SigningResult;
+import sop.enums.SignAs;
+import sop.exception.SOPGPException;
+import sop.operation.DetachedSign;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+public class BCDetachedSign
+ extends AbstractBCOperation
+ implements DetachedSign {
+
+ private final OpenPGPDetachedSignatureGenerator sigGen = new OpenPGPDetachedSignatureGenerator();
+ private boolean armored = true;
+
+ @NotNull
+ @Override
+ public ReadyWithResult data(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
+ return new ReadyWithResult() {
+ @Override
+ public SigningResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException {
+ try
+ {
+ List signatures = sigGen.sign(inputStream);
+ OutputStream aOut = null;
+ OutputStream pOut;
+ if (armored)
+ {
+ aOut = ArmoredOutputStream.builder()
+ .clearHeaders()
+ .build(outputStream);
+ pOut = BCPGOutputStream.wrap(aOut);
+ }
+ else
+ {
+ pOut = BCPGOutputStream.wrap(outputStream);
+ }
+
+ for (OpenPGPSignature.OpenPGPDocumentSignature sig : signatures)
+ {
+ pOut.write(sig.getSignature().getEncoded());
+ }
+
+ pOut.close();
+ if (aOut != null)
+ {
+ aOut.close();
+ }
+
+ return new SigningResult(MicAlg.fromHashAlgorithmId(signatures.get(0).getSignature().getHashAlgorithm()));
+ }
+ catch (PGPException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @NotNull
+ @Override
+ public DetachedSign mode(@NotNull SignAs signAs) throws SOPGPException.UnsupportedOption {
+ switch (signAs)
+ {
+ case text:
+ sigGen.setCanonicalTextDocument();
+ break;
+ case binary:
+ sigGen.setBinarySignature();
+ break;
+ }
+ return this;
+ }
+
+ @Override
+ public DetachedSign noArmor() {
+ armored = false;
+ return this;
+ }
+
+ @Override
+ public DetachedSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
+ try
+ {
+ sigGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream), null);
+ }
+ catch (InvalidSigningKeyException e)
+ {
+ throw new SOPGPException.KeyCannotSign("Key cannot sign", e);
+ }
+ catch (PGPException e)
+ {
+ throw new RuntimeException(e);
+ }
+ return this;
+ }
+
+ @Override
+ public DetachedSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
+ return this;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java
new file mode 100644
index 0000000..e3d3a28
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCDetachedVerify.java
@@ -0,0 +1,72 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.api.OpenPGPCertificate;
+import org.bouncycastle.openpgp.api.OpenPGPDetachedSignatureProcessor;
+import org.bouncycastle.openpgp.api.OpenPGPSignature;
+import org.bouncycastle.util.encoders.Hex;
+import org.jetbrains.annotations.NotNull;
+import sop.Verification;
+import sop.exception.SOPGPException;
+import sop.operation.DetachedVerify;
+import sop.operation.VerifySignatures;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class BCDetachedVerify
+ extends AbstractBCOperation
+ implements DetachedVerify
+{
+
+ OpenPGPDetachedSignatureProcessor processor = new OpenPGPDetachedSignatureProcessor();
+
+ public BCDetachedVerify()
+ {
+ processor.setExceptionCallback(e -> System.err.println(e.getMessage()));
+ }
+
+ @NotNull
+ @Override
+ public VerifySignatures signatures(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
+ processor.addSignatures(inputStream);
+ return this;
+ }
+
+ @Override
+ public DetachedVerify notBefore(@NotNull Date date) throws SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @Override
+ public DetachedVerify notAfter(@NotNull Date date) throws SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @Override
+ public DetachedVerify cert(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
+ processor.addVerificationCertificate(OpenPGPCertificate.fromInputStream(inputStream));
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public List data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
+ List signatures = processor.verify(inputStream);
+
+ List verifications = new ArrayList<>();
+ for (OpenPGPSignature.OpenPGPDocumentSignature signature : signatures)
+ {
+ if (signature.isValidAt(signature.getCreationTime()))
+ {
+ verifications.add(new Verification(
+ signature.getCreationTime(),
+ Hex.toHexString(signature.getIssuer().getKeyIdentifier().getFingerprint()),
+ Hex.toHexString(signature.getIssuerCertificate().getFingerprint())));
+ }
+ }
+ return verifications;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java
new file mode 100644
index 0000000..f34cca8
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCEncrypt.java
@@ -0,0 +1,97 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.api.OpenPGPCertificate;
+import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator;
+import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream;
+import org.bouncycastle.util.io.Streams;
+import org.jetbrains.annotations.NotNull;
+import sop.EncryptionResult;
+import sop.ReadyWithResult;
+import sop.SessionKey;
+import sop.enums.EncryptAs;
+import sop.exception.SOPGPException;
+import sop.operation.Encrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BCEncrypt
+ extends AbstractBCOperation
+ implements Encrypt {
+
+ private final OpenPGPMessageGenerator mGen;
+
+ public BCEncrypt()
+ {
+ this.mGen = new OpenPGPMessageGenerator();
+ }
+
+ @NotNull
+ @Override
+ public Encrypt mode(@NotNull EncryptAs encryptAs) throws SOPGPException.UnsupportedOption {
+ // TODO: Implement
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt noArmor() {
+ mGen.setArmored(false);
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt signWith(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException {
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt withPassword(@NotNull String s) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
+ mGen.addEncryptionPassphrase(s.toCharArray());
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt withCert(@NotNull InputStream inputStream) throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData, IOException {
+ mGen.addEncryptionCertificate(OpenPGPCertificate.fromInputStream(inputStream));
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public Encrypt profile(@NotNull String s) {
+ // TODO: Implement
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public ReadyWithResult plaintext(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected {
+ return new ReadyWithResult() {
+ SessionKey sessionKey = null;
+ @Override
+ public EncryptionResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException {
+ mGen.setSessionKeyExtractionCallback(sk -> this.sessionKey = new SessionKey((byte) sk.getAlgorithm(), sk.getKey()));
+ try {
+ OpenPGPMessageOutputStream mOut = mGen.open(outputStream);
+ Streams.pipeAll(inputStream, mOut);
+ mOut.close();
+ return new EncryptionResult(sessionKey);
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java
index d0ad5a2..2f43c31 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCExtractCert.java
@@ -15,7 +15,9 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-public class BCExtractCert implements ExtractCert {
+public class BCExtractCert
+ extends AbstractBCOperation
+ implements ExtractCert {
private boolean armor = true;
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java
new file mode 100644
index 0000000..946b868
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java
@@ -0,0 +1,90 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.api.OpenPGPKey;
+import org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator;
+import org.bouncycastle.openpgp.api.bc.BcOpenPGPV6KeyGenerator;
+import org.jetbrains.annotations.NotNull;
+import sop.Ready;
+import sop.exception.SOPGPException;
+import sop.operation.GenerateKey;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class BCGenerateKey
+ extends AbstractBCOperation
+ implements GenerateKey {
+
+ private boolean armor = true;
+ private boolean signOnly = false;
+ private String userId;
+ private char[] passphrase;
+
+ @NotNull
+ @Override
+ public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
+ return new Ready()
+ {
+ @Override
+ public void writeTo(@NotNull OutputStream outputStream) throws IOException
+ {
+ OpenPGPV6KeyGenerator generator = new BcOpenPGPV6KeyGenerator(new Date());
+ try
+ {
+ PGPSecretKeyRing keyRing = generator.ed25519x25519Key(userId, passphrase);
+ OpenPGPKey key = new OpenPGPKey(keyRing);
+ if (armor)
+ {
+ outputStream.write(key.toAsciiArmoredString().getBytes(StandardCharsets.UTF_8));
+ }
+ else
+ {
+ keyRing.encode(outputStream);
+ }
+ }
+ catch (PGPException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @NotNull
+ @Override
+ public GenerateKey noArmor() {
+ this.armor = false;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public GenerateKey userId(@NotNull String s) {
+ this.userId = s;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public GenerateKey withKeyPassword(@NotNull String s) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
+ this.passphrase = s.toCharArray();
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public GenerateKey profile(@NotNull String s) {
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public GenerateKey signingOnly() {
+ this.signOnly = true;
+ return this;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java
new file mode 100644
index 0000000..a8f3892
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineSign.java
@@ -0,0 +1,63 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.api.OpenPGPKey;
+import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator;
+import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream;
+import org.bouncycastle.util.io.Streams;
+import org.jetbrains.annotations.NotNull;
+import sop.Ready;
+import sop.enums.InlineSignAs;
+import sop.exception.SOPGPException;
+import sop.operation.InlineSign;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BCInlineSign
+ extends AbstractBCOperation
+ implements InlineSign {
+
+ private final OpenPGPMessageGenerator mGen = new OpenPGPMessageGenerator();
+
+ @NotNull
+ @Override
+ public Ready data(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
+ return new Ready() {
+ @Override
+ public void writeTo(@NotNull OutputStream outputStream) throws IOException {
+ try {
+ OpenPGPMessageOutputStream mOut = mGen.open(outputStream);
+ Streams.pipeAll(inputStream, mOut);
+ mOut.close();
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @NotNull
+ @Override
+ public InlineSign mode(@NotNull InlineSignAs inlineSignAs) throws SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @Override
+ public InlineSign noArmor() {
+ mGen.setArmored(false);
+ return this;
+ }
+
+ @Override
+ public InlineSign key(@NotNull InputStream inputStream) throws SOPGPException.KeyCannotSign, SOPGPException.BadData, SOPGPException.UnsupportedAsymmetricAlgo, IOException {
+ mGen.addSigningKey(OpenPGPKey.fromInputStream(inputStream));
+ return this;
+ }
+
+ @Override
+ public InlineSign withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
+ return this;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java
new file mode 100644
index 0000000..a747bea
--- /dev/null
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCInlineVerify.java
@@ -0,0 +1,60 @@
+package org.pgpainless.bouncycastle.sop.operation;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.api.OpenPGPCertificate;
+import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream;
+import org.bouncycastle.openpgp.api.OpenPGPMessageProcessor;
+import org.bouncycastle.util.io.Streams;
+import org.jetbrains.annotations.NotNull;
+import sop.ReadyWithResult;
+import sop.Verification;
+import sop.exception.SOPGPException;
+import sop.operation.InlineVerify;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.List;
+
+public class BCInlineVerify
+ extends AbstractBCOperation
+ implements InlineVerify {
+
+ private final OpenPGPMessageProcessor processor = new OpenPGPMessageProcessor();
+
+ @NotNull
+ @Override
+ public ReadyWithResult> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
+ return new ReadyWithResult>() {
+ @Override
+ public List writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException {
+ try {
+ OpenPGPMessageInputStream mIn = processor.process(inputStream);
+ Streams.pipeAll(mIn, outputStream);
+ mIn.close();
+ OpenPGPMessageInputStream.Result result = mIn.getResult();
+ return getVerifications(result);
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @Override
+ public InlineVerify notBefore(@NotNull Date date) throws SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @Override
+ public InlineVerify notAfter(@NotNull Date date) throws SOPGPException.UnsupportedOption {
+ return this;
+ }
+
+ @Override
+ public InlineVerify cert(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
+ processor.addVerificationCertificate(OpenPGPCertificate.fromInputStream(inputStream));
+ return this;
+ }
+}
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java
index 6b31387..5d3317d 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java
@@ -6,7 +6,10 @@ import sop.operation.ListProfiles;
import java.util.List;
-public class BCListProfiles implements ListProfiles {
+public class BCListProfiles
+ extends AbstractBCOperation
+ implements ListProfiles {
+
@NotNull
@Override
public List subcommand(@NotNull String s) {
diff --git a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java
index cb7e1ce..02c53a6 100644
--- a/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java
+++ b/bcsop/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCVersion.java
@@ -6,7 +6,9 @@ import org.jetbrains.annotations.Nullable;
import sop.exception.SOPGPException;
import sop.operation.Version;
-public class BCVersion implements Version {
+public class BCVersion
+ extends AbstractBCOperation
+ implements Version {
@NotNull
@Override
diff --git a/pom.xml b/pom.xml
index 52c5d17..38a5952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,19 +17,33 @@
21
21
UTF-8
+ 3.3.0
+
+
+ local-maven-repo
+ file:///home/vanitas/.m2/
+
+
+
+
org.bouncycastle
bcprov-jdk18on
- 1.79
+ 1.80-SNAPSHOT
org.bouncycastle
bcpg-jdk18on
- 1.79
+ 1.80-SNAPSHOT
+
+
+ org.bouncycastle
+ bcutil-jdk18on
+ 1.80-SNAPSHOT
org.pgpainless
@@ -43,5 +57,37 @@
-
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven.assembly.plugin.version}
+
+
+
+ true
+ org.pgpainless.BcSopCLI
+
+
+
+
+
+
+
\ No newline at end of file