mirror of
https://codeberg.org/PGPainless/bc-sop.git
synced 2025-09-09 19:29:41 +02:00
Compare commits
3 commits
ea8b1b6459
...
43fdac4ac5
Author | SHA1 | Date | |
---|---|---|---|
43fdac4ac5 | |||
b54ba8ac1e | |||
80de3585d7 |
15 changed files with 536 additions and 24 deletions
|
@ -4,6 +4,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi;
|
import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCArmor;
|
import org.pgpainless.bouncycastle.sop.operation.BCArmor;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCDearmor;
|
import org.pgpainless.bouncycastle.sop.operation.BCDearmor;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCDecrypt;
|
import org.pgpainless.bouncycastle.sop.operation.BCDecrypt;
|
||||||
|
@ -15,10 +16,12 @@ import org.pgpainless.bouncycastle.sop.operation.BCGenerateKey;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCInlineSign;
|
import org.pgpainless.bouncycastle.sop.operation.BCInlineSign;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCInlineVerify;
|
import org.pgpainless.bouncycastle.sop.operation.BCInlineVerify;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCListProfiles;
|
import org.pgpainless.bouncycastle.sop.operation.BCListProfiles;
|
||||||
|
import org.pgpainless.bouncycastle.sop.operation.BCMergeCerts;
|
||||||
import org.pgpainless.bouncycastle.sop.operation.BCVersion;
|
import org.pgpainless.bouncycastle.sop.operation.BCVersion;
|
||||||
import sop.SOP;
|
import sop.SOP;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.Armor;
|
import sop.operation.Armor;
|
||||||
|
import sop.operation.CertifyUserId;
|
||||||
import sop.operation.ChangeKeyPassword;
|
import sop.operation.ChangeKeyPassword;
|
||||||
import sop.operation.Dearmor;
|
import sop.operation.Dearmor;
|
||||||
import sop.operation.Decrypt;
|
import sop.operation.Decrypt;
|
||||||
|
@ -31,7 +34,10 @@ import sop.operation.InlineDetach;
|
||||||
import sop.operation.InlineSign;
|
import sop.operation.InlineSign;
|
||||||
import sop.operation.InlineVerify;
|
import sop.operation.InlineVerify;
|
||||||
import sop.operation.ListProfiles;
|
import sop.operation.ListProfiles;
|
||||||
|
import sop.operation.MergeCerts;
|
||||||
import sop.operation.RevokeKey;
|
import sop.operation.RevokeKey;
|
||||||
|
import sop.operation.UpdateKey;
|
||||||
|
import sop.operation.ValidateUserId;
|
||||||
import sop.operation.Version;
|
import sop.operation.Version;
|
||||||
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
@ -136,4 +142,28 @@ public class BouncyCastleSOP implements SOP {
|
||||||
public InlineVerify inlineVerify() {
|
public InlineVerify inlineVerify() {
|
||||||
return new BCInlineVerify(api);
|
return new BCInlineVerify(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public CertifyUserId certifyUserId() {
|
||||||
|
throw new SOPGPException.UnsupportedSubcommand("certify-userid is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public UpdateKey updateKey() {
|
||||||
|
throw new SOPGPException.UnsupportedSubcommand("update-key is not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public MergeCerts mergeCerts() {
|
||||||
|
return new BCMergeCerts(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ValidateUserId validateUserId() {
|
||||||
|
throw new SOPGPException.UnsupportedSubcommand("validate-userid is not implemented.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.api.KeyPassphraseProvider;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.CertifyUserId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BCCertifyUserId
|
||||||
|
extends AbstractBCOperation
|
||||||
|
implements CertifyUserId {
|
||||||
|
private final KeyPassphraseProvider.DefaultKeyPassphraseProvider passphraseProvider =
|
||||||
|
new KeyPassphraseProvider.DefaultKeyPassphraseProvider();
|
||||||
|
private boolean armor = true;
|
||||||
|
private boolean requireSelfSig = true;
|
||||||
|
private final Set<String> userIds = new HashSet<>();
|
||||||
|
|
||||||
|
public BCCertifyUserId(OpenPGPApi api) {
|
||||||
|
super(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Ready certs(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException, SOPGPException.CertUserIdNoMatch {
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
||||||
|
Streams.pipeAll(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CertifyUserId noArmor() throws SOPGPException.UnsupportedOption {
|
||||||
|
this.armor = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CertifyUserId userId(@NotNull String s) throws SOPGPException.UnsupportedOption {
|
||||||
|
this.userIds.add(s.trim());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CertifyUserId withKeyPassword(@NotNull byte[] bytes) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
|
passphraseProvider.addPassphrase(new String(bytes).toCharArray());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CertifyUserId noRequireSelfSig() throws SOPGPException.UnsupportedOption {
|
||||||
|
this.requireSelfSig = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CertifyUserId keys(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException, SOPGPException.KeyIsProtected {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ public class BCDetachedVerify
|
||||||
{
|
{
|
||||||
|
|
||||||
private final OpenPGPDetachedSignatureProcessor processor;
|
private final OpenPGPDetachedSignatureProcessor processor;
|
||||||
|
private boolean hasCerts = false;
|
||||||
|
|
||||||
public BCDetachedVerify(OpenPGPApi api)
|
public BCDetachedVerify(OpenPGPApi api)
|
||||||
{
|
{
|
||||||
|
@ -50,12 +51,16 @@ public class BCDetachedVerify
|
||||||
@Override
|
@Override
|
||||||
public DetachedVerify cert(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
|
public DetachedVerify cert(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
|
||||||
processor.addVerificationCertificate(parseCertificate(inputStream));
|
processor.addVerificationCertificate(parseCertificate(inputStream));
|
||||||
|
hasCerts = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<Verification> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
public List<Verification> data(@NotNull InputStream inputStream) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||||
|
if (!hasCerts) {
|
||||||
|
throw new SOPGPException.MissingArg("No certificates provided for signature verification.");
|
||||||
|
}
|
||||||
List<OpenPGPSignature.OpenPGPDocumentSignature> signatures = processor.process(inputStream);
|
List<OpenPGPSignature.OpenPGPDocumentSignature> signatures = processor.process(inputStream);
|
||||||
|
|
||||||
List<Verification> verifications = getVerifications(signatures);
|
List<Verification> verifications = getVerifications(signatures);
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class BCEncrypt
|
||||||
private final OpenPGPMessageGenerator mGen;
|
private final OpenPGPMessageGenerator mGen;
|
||||||
private final List<OpenPGPKey> signingKeys = new ArrayList<>();
|
private final List<OpenPGPKey> signingKeys = new ArrayList<>();
|
||||||
private int signatureMode = PGPSignature.BINARY_DOCUMENT;
|
private int signatureMode = PGPSignature.BINARY_DOCUMENT;
|
||||||
|
private boolean hasEncryptionMethod = false;
|
||||||
|
|
||||||
public BCEncrypt(OpenPGPApi api) {
|
public BCEncrypt(OpenPGPApi api) {
|
||||||
super(api);
|
super(api);
|
||||||
|
@ -83,6 +84,7 @@ public class BCEncrypt
|
||||||
public Encrypt withPassword(@NotNull String s)
|
public Encrypt withPassword(@NotNull String s)
|
||||||
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
mGen.addEncryptionPassphrase(s.toCharArray());
|
mGen.addEncryptionPassphrase(s.toCharArray());
|
||||||
|
hasEncryptionMethod = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +94,7 @@ public class BCEncrypt
|
||||||
throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
throws SOPGPException.CertCannotEncrypt, SOPGPException.UnsupportedAsymmetricAlgo, SOPGPException.BadData {
|
||||||
try {
|
try {
|
||||||
mGen.addEncryptionCertificate(parseCertificate(inputStream));
|
mGen.addEncryptionCertificate(parseCertificate(inputStream));
|
||||||
|
hasEncryptionMethod = true;
|
||||||
} catch (InvalidEncryptionKeyException e) {
|
} catch (InvalidEncryptionKeyException e) {
|
||||||
throw new SOPGPException.CertCannotEncrypt("Certificate cannot encrypt", e);
|
throw new SOPGPException.CertCannotEncrypt("Certificate cannot encrypt", e);
|
||||||
}
|
}
|
||||||
|
@ -109,6 +112,10 @@ public class BCEncrypt
|
||||||
@Override
|
@Override
|
||||||
public ReadyWithResult<EncryptionResult> plaintext(@NotNull InputStream inputStream)
|
public ReadyWithResult<EncryptionResult> plaintext(@NotNull InputStream inputStream)
|
||||||
throws SOPGPException.KeyIsProtected {
|
throws SOPGPException.KeyIsProtected {
|
||||||
|
if (!hasEncryptionMethod) {
|
||||||
|
throw new SOPGPException.MissingArg("No encryption method provided.");
|
||||||
|
}
|
||||||
|
|
||||||
for (OpenPGPKey key : signingKeys) {
|
for (OpenPGPKey key : signingKeys) {
|
||||||
try {
|
try {
|
||||||
mGen.addSigningKey(key, new SignatureParameters.Callback() {
|
mGen.addSigningKey(key, new SignatureParameters.Callback() {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPUtil;
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPKey;
|
||||||
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
|
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import sop.Ready;
|
import sop.Ready;
|
||||||
|
@ -16,6 +18,7 @@ import sop.operation.ExtractCert;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -33,29 +36,32 @@ public class BCExtractCert
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Ready key(@NotNull InputStream inputStream) throws IOException, SOPGPException.BadData {
|
public Ready key(@NotNull InputStream inputStream) throws IOException, SOPGPException.BadData {
|
||||||
InputStream decodeIn = PGPUtil.getDecoderStream(inputStream);
|
List<OpenPGPKey> keys = api.readKeyOrCertificate().parseKeys(inputStream);
|
||||||
PGPObjectFactory objFac = new BcPGPObjectFactory(decodeIn);
|
List<OpenPGPCertificate> certs = new ArrayList<>();
|
||||||
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject();
|
for (OpenPGPKey key : keys) {
|
||||||
|
certs.add(key.toCertificate());
|
||||||
List<PGPPublicKey> list = new ArrayList<>();
|
|
||||||
Iterator<PGPPublicKey> iterator = secretKeys.getPublicKeys();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
list.add(iterator.next());
|
|
||||||
}
|
}
|
||||||
PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(list);
|
|
||||||
|
|
||||||
return new Ready() {
|
return new Ready() {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
||||||
if (armor) {
|
if (armor) {
|
||||||
ArmoredOutputStream aOut = ArmoredOutputStream.builder()
|
if (certs.size() == 1) {
|
||||||
.clearHeaders()
|
outputStream.write(certs.get(0).toAsciiArmoredString().getBytes(StandardCharsets.UTF_8));
|
||||||
.enableCRC(true)
|
} else {
|
||||||
.build(outputStream);
|
ArmoredOutputStream aOut = ArmoredOutputStream.builder()
|
||||||
publicKeys.encode(aOut);
|
.clearHeaders()
|
||||||
aOut.close();
|
.enableCRC(true)
|
||||||
|
.build(outputStream);
|
||||||
|
for (OpenPGPCertificate cert : certs) {
|
||||||
|
aOut.write(cert.getEncoded());
|
||||||
|
}
|
||||||
|
aOut.close();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
publicKeys.encode(outputStream);
|
for (OpenPGPCertificate cert : certs) {
|
||||||
|
outputStream.write(cert.getEncoded());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.pgpainless.bouncycastle.sop.operation;
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import sop.Profile;
|
import sop.Profile;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
import sop.operation.ListProfiles;
|
import sop.operation.ListProfiles;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -22,7 +23,9 @@ public class BCListProfiles
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case "generate-key":
|
case "generate-key":
|
||||||
return BCGenerateKey.PROFILES;
|
return BCGenerateKey.PROFILES;
|
||||||
|
case "encrypt":
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
throw new SOPGPException.UnsupportedProfile(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
|
import org.bouncycastle.bcpg.KeyIdentifier;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.MergeCerts;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BCMergeCerts
|
||||||
|
extends AbstractBCOperation
|
||||||
|
implements MergeCerts {
|
||||||
|
|
||||||
|
private List<OpenPGPCertificate> updates = new ArrayList<>();
|
||||||
|
private boolean armor = true;
|
||||||
|
|
||||||
|
public BCMergeCerts(OpenPGPApi api) {
|
||||||
|
super(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Ready baseCertificates(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
||||||
|
Map<KeyIdentifier, OpenPGPCertificate> merged = new HashMap<>();
|
||||||
|
List<OpenPGPCertificate> baseCerts = api.readKeyOrCertificate().parseCertificates(inputStream);
|
||||||
|
|
||||||
|
// Merge base certs
|
||||||
|
for (OpenPGPCertificate base : baseCerts) {
|
||||||
|
OpenPGPCertificate existing = merged.get(base.getKeyIdentifier());
|
||||||
|
if (existing != null) {
|
||||||
|
try {
|
||||||
|
existing = OpenPGPCertificate.join(existing, base);
|
||||||
|
merged.put(existing.getKeyIdentifier(), existing);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
merged.put(base.getKeyIdentifier(), base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge updates
|
||||||
|
for (OpenPGPCertificate update : updates) {
|
||||||
|
OpenPGPCertificate existing = merged.get(update.getKeyIdentifier());
|
||||||
|
if (existing != null) {
|
||||||
|
try {
|
||||||
|
existing = OpenPGPCertificate.join(existing, update);
|
||||||
|
merged.put(existing.getKeyIdentifier(), existing);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// output
|
||||||
|
if (armor) {
|
||||||
|
if (merged.size() == 1) {
|
||||||
|
outputStream.write(merged.values().iterator().next().toAsciiArmoredString().getBytes(StandardCharsets.UTF_8));
|
||||||
|
} else {
|
||||||
|
ArmoredOutputStream aOut = ArmoredOutputStream.builder()
|
||||||
|
.clearHeaders()
|
||||||
|
.build(outputStream);
|
||||||
|
for (OpenPGPCertificate cert : merged.values()) {
|
||||||
|
aOut.write(cert.getEncoded());
|
||||||
|
}
|
||||||
|
aOut.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (OpenPGPCertificate cert : merged.values()) {
|
||||||
|
outputStream.write(cert.getEncoded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public MergeCerts noArmor() throws SOPGPException.UnsupportedOption {
|
||||||
|
armor = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public MergeCerts updates(@NotNull InputStream inputStream) throws SOPGPException.BadData, IOException {
|
||||||
|
this.updates.addAll(api.readKeyOrCertificate().parseCertificates(inputStream));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.api.KeyPassphraseProvider;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPKey;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPKeyEditor;
|
||||||
|
import org.bouncycastle.openpgp.api.exception.KeyPassphraseException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.RevokeKey;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BCRevokeKey extends AbstractBCOperation implements RevokeKey {
|
||||||
|
|
||||||
|
private boolean armor = true;
|
||||||
|
private final KeyPassphraseProvider.DefaultKeyPassphraseProvider passphraseProvider =
|
||||||
|
new KeyPassphraseProvider.DefaultKeyPassphraseProvider();
|
||||||
|
|
||||||
|
|
||||||
|
public BCRevokeKey(OpenPGPApi api) {
|
||||||
|
super(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Ready keys(@NotNull InputStream inputStream) {
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
||||||
|
List<OpenPGPKey> keys;
|
||||||
|
try {
|
||||||
|
keys = api.readKeyOrCertificate().parseKeys(inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SOPGPException.BadData(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OpenPGPCertificate> revoked = new ArrayList<>();
|
||||||
|
for (OpenPGPKey key : keys) {
|
||||||
|
try {
|
||||||
|
OpenPGPKeyEditor editor = api.editKey(key, passphraseProvider);
|
||||||
|
editor.revokeKey();
|
||||||
|
revoked.add(editor.done().toCertificate());
|
||||||
|
} catch (KeyPassphraseException e) {
|
||||||
|
throw new SOPGPException.KeyIsProtected("Cannot unlock secret key", e);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (armor) {
|
||||||
|
if (revoked.size() == 1) {
|
||||||
|
outputStream.write(revoked.get(0).toAsciiArmoredString().getBytes(StandardCharsets.UTF_8));
|
||||||
|
} else {
|
||||||
|
ArmoredOutputStream aOut = ArmoredOutputStream.builder()
|
||||||
|
.clearHeaders()
|
||||||
|
.build(outputStream);
|
||||||
|
for (OpenPGPCertificate cert : revoked) {
|
||||||
|
aOut.write(cert.getEncoded());
|
||||||
|
}
|
||||||
|
aOut.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (OpenPGPCertificate cert : revoked) {
|
||||||
|
outputStream.write(cert.getEncoded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public RevokeKey noArmor() {
|
||||||
|
armor = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public RevokeKey withKeyPassword(@NotNull byte[] bytes)
|
||||||
|
throws SOPGPException.UnsupportedOption, SOPGPException.PasswordNotHumanReadable {
|
||||||
|
passphraseProvider.addPassphrase(new String(bytes).toCharArray());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.api.KeyPassphraseProvider;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sop.Ready;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.UpdateKey;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class BCUpdateKey
|
||||||
|
extends AbstractBCOperation
|
||||||
|
implements UpdateKey {
|
||||||
|
private final KeyPassphraseProvider.DefaultKeyPassphraseProvider passphraseProvider =
|
||||||
|
new KeyPassphraseProvider.DefaultKeyPassphraseProvider();
|
||||||
|
private boolean armor = true;
|
||||||
|
private boolean signingOnly = false;
|
||||||
|
private boolean addCapabilities = true;
|
||||||
|
|
||||||
|
public BCUpdateKey(OpenPGPApi api) {
|
||||||
|
super(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Ready key(@NotNull InputStream inputStream)
|
||||||
|
throws SOPGPException.BadData, IOException, SOPGPException.KeyIsProtected, SOPGPException.PrimaryKeyBad {
|
||||||
|
return new Ready() {
|
||||||
|
@Override
|
||||||
|
public void writeTo(@NotNull OutputStream outputStream) throws IOException {
|
||||||
|
Streams.pipeAll(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UpdateKey noArmor() {
|
||||||
|
armor = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UpdateKey signingOnly()
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
signingOnly = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UpdateKey noAddedCapabilities()
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
addCapabilities = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UpdateKey withKeyPassword(@NotNull byte[] bytes)
|
||||||
|
throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
|
||||||
|
passphraseProvider.addPassphrase(new String(bytes).toCharArray());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public UpdateKey mergeCerts(@NotNull InputStream inputStream)
|
||||||
|
throws SOPGPException.UnsupportedOption, SOPGPException.BadData, IOException {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPSignatureException;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPApi;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.operation.ValidateUserId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BCValidateUserId
|
||||||
|
extends AbstractBCOperation
|
||||||
|
implements ValidateUserId {
|
||||||
|
|
||||||
|
private boolean validateSpecOnly = false;
|
||||||
|
private Date validateAt = new Date();
|
||||||
|
private final List<String> userIds = new ArrayList<>();
|
||||||
|
private final List<OpenPGPCertificate> authorities = new ArrayList<>();
|
||||||
|
|
||||||
|
public BCValidateUserId(OpenPGPApi api) {
|
||||||
|
super(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ValidateUserId addrSpecOnly()
|
||||||
|
throws SOPGPException.UnsupportedOption {
|
||||||
|
validateSpecOnly = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ValidateUserId userId(@NotNull String s) {
|
||||||
|
userIds.add(s.trim());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ValidateUserId authorities(@NotNull InputStream inputStream)
|
||||||
|
throws SOPGPException.BadData, IOException {
|
||||||
|
authorities.addAll(api.readKeyOrCertificate().parseCertificates(inputStream));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean subjects(@NotNull InputStream inputStream)
|
||||||
|
throws SOPGPException.BadData, IOException, SOPGPException.CertUserIdNoMatch {
|
||||||
|
List<OpenPGPCertificate> certificates = api.readKeyOrCertificate().parseCertificates(inputStream);
|
||||||
|
for (OpenPGPCertificate certificate : certificates) {
|
||||||
|
for (String userId : userIds) {
|
||||||
|
OpenPGPCertificate.OpenPGPUserId uid = certificate.getUserId(userId);
|
||||||
|
if (!uid.isBoundAt(validateAt)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (OpenPGPCertificate authority : authorities) {
|
||||||
|
OpenPGPCertificate.OpenPGPSignatureChain certification = uid.getCertificationBy(authority);
|
||||||
|
if (certification == null) {
|
||||||
|
throw new SOPGPException.CertUserIdNoMatch("Could not find certification for UserID '" + userId + "'");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!certification.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (PGPSignatureException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ValidateUserId validateAt(@NotNull Date date) {
|
||||||
|
validateAt = date;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,9 @@ public class BCVersion
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String getExtendedVersion() {
|
public String getExtendedVersion() {
|
||||||
return "";
|
return getName() + " " + getVersion() + "\n"
|
||||||
|
+ getBackendVersion() + "\n"
|
||||||
|
+ "sop-java " + getSopJavaVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import sop.testsuite.operation.CertifyValidateUserIdTest;
|
||||||
|
|
||||||
|
public class BCSopCertifyValidateUserIdTest extends CertifyValidateUserIdTest {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import sop.testsuite.operation.ListProfilesTest;
|
||||||
|
|
||||||
|
public class BCSopListProfilesTest extends ListProfilesTest {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.pgpainless.bouncycastle.sop.operation;
|
||||||
|
|
||||||
|
import sop.testsuite.operation.MergeCertsTest;
|
||||||
|
|
||||||
|
public class BCSopMergeCertsTest extends MergeCertsTest {
|
||||||
|
|
||||||
|
}
|
12
pom.xml
12
pom.xml
|
@ -33,32 +33,32 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk18on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
<version>1.80-SNAPSHOT</version>
|
<version>1.82-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcpg-jdk18on</artifactId>
|
<artifactId>bcpg-jdk18on</artifactId>
|
||||||
<version>1.80-SNAPSHOT</version>
|
<version>1.82-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcutil-jdk18on</artifactId>
|
<artifactId>bcutil-jdk18on</artifactId>
|
||||||
<version>1.80-SNAPSHOT</version>
|
<version>1.82-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.pgpainless</groupId>
|
<groupId>org.pgpainless</groupId>
|
||||||
<artifactId>sop-java</artifactId>
|
<artifactId>sop-java</artifactId>
|
||||||
<version>10.1.1-SNAPSHOT</version>
|
<version>14.0.1-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.pgpainless</groupId>
|
<groupId>org.pgpainless</groupId>
|
||||||
<artifactId>sop-java-picocli</artifactId>
|
<artifactId>sop-java-picocli</artifactId>
|
||||||
<version>10.1.1-SNAPSHOT</version>
|
<version>14.0.1-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.pgpainless</groupId>
|
<groupId>org.pgpainless</groupId>
|
||||||
<artifactId>sop-java-testfixtures</artifactId>
|
<artifactId>sop-java-testfixtures</artifactId>
|
||||||
<version>10.1.1-SNAPSHOT</version>
|
<version>14.0.1-SNAPSHOT</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue