Implement some operations

This commit is contained in:
Paul Schaub 2024-12-12 13:33:17 +01:00
parent 1af2a3c121
commit cc4870219a
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
18 changed files with 802 additions and 58 deletions

View file

@ -31,6 +31,47 @@
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk18on</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven.assembly.plugin.version}</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.pgpainless.BcSopCLI</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -10,6 +10,7 @@
</parent>
<artifactId>bcsop</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
@ -30,6 +31,23 @@
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk18on</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View file

@ -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();
}
}

View file

@ -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<Verification> verif = verify.data(new ByteArrayInputStream(MSG.getBytes(StandardCharsets.UTF_8)));
}
}

View file

@ -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<Verification> getVerifications(OpenPGPMessageInputStream.Result result)
{
List<Verification> 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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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<PGPSessionKey> sessionKeys = new ArrayList<>();
private final List<String> messagePassphrases = new ArrayList<>();
private final List<PGPPublicKeyRing> verificationCerts = new ArrayList<>();
private final List<PGPSecretKeyRing> encryptionKeys = new ArrayList<>();
private final List<String> encryptionKeyPassphrases = new ArrayList<>();
private final OpenPGPMessageProcessor processor = new OpenPGPMessageProcessor();
@NotNull
@Override
@ -36,32 +38,17 @@ public class BCDecrypt implements Decrypt {
return new ReadyWithResult<DecryptionResult>() {
@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;
}

View file

@ -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<SigningResult> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected, SOPGPException.ExpectedText {
return new ReadyWithResult<SigningResult>() {
@Override
public SigningResult writeTo(@NotNull OutputStream outputStream) throws IOException, SOPGPException {
try
{
List<OpenPGPSignature.OpenPGPDocumentSignature> 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;
}
}

View file

@ -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<Verification> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
List<OpenPGPSignature.OpenPGPDocumentSignature> signatures = processor.verify(inputStream);
List<Verification> 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;
}
}

View file

@ -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<EncryptionResult> plaintext(@NotNull InputStream inputStream) throws IOException, SOPGPException.KeyIsProtected {
return new ReadyWithResult<EncryptionResult>() {
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);
}
}
};
}
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<List<Verification>> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
return new ReadyWithResult<List<Verification>>() {
@Override
public List<Verification> 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;
}
}

View file

@ -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<Profile> subcommand(@NotNull String s) {

View file

@ -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

52
pom.xml
View file

@ -17,19 +17,33 @@
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.assembly.plugin.version>3.3.0</maven.assembly.plugin.version>
</properties>
<repositories>
<repository>
<id>local-maven-repo</id>
<url>file:///home/vanitas/.m2/</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.79</version>
<version>1.80-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
<version>1.79</version>
<version>1.80-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk18on</artifactId>
<version>1.80-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.pgpainless</groupId>
@ -43,5 +57,37 @@
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!-- Plugins -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
<!-- Shared plugin configuration -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven.assembly.plugin.version}</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.pgpainless.BcSopCLI</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>