mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 11:27:57 +01:00
Add support for detached signatures
This commit is contained in:
parent
e8ccf78455
commit
65b670740e
14 changed files with 410 additions and 122 deletions
|
@ -3,6 +3,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'org.jetbrains:annotations:19.0.0'
|
||||||
testImplementation group: 'junit', name: 'junit', version: '4.12'
|
testImplementation group: 'junit', name: 'junit', version: '4.12'
|
||||||
/*
|
/*
|
||||||
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
|
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
|
||||||
|
|
|
@ -17,15 +17,24 @@ package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
|
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
|
||||||
|
@ -34,9 +43,12 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
private InputStream inputStream;
|
private InputStream inputStream;
|
||||||
private PGPSecretKeyRingCollection decryptionKeys;
|
private PGPSecretKeyRingCollection decryptionKeys;
|
||||||
private SecretKeyRingProtector decryptionKeyDecryptor;
|
private SecretKeyRingProtector decryptionKeyDecryptor;
|
||||||
|
private List<PGPSignature> detachedSignatures;
|
||||||
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
||||||
private MissingPublicKeyCallback missingPublicKeyCallback = null;
|
private MissingPublicKeyCallback missingPublicKeyCallback = null;
|
||||||
|
|
||||||
|
private final KeyFingerPrintCalculator keyFingerPrintCalculator = new BcKeyFingerprintCalculator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptWith onInputStream(InputStream inputStream) {
|
public DecryptWith onInputStream(InputStream inputStream) {
|
||||||
this.inputStream = inputStream;
|
this.inputStream = inputStream;
|
||||||
|
@ -46,17 +58,76 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
class DecryptWithImpl implements DecryptWith {
|
class DecryptWithImpl implements DecryptWith {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VerifyWith decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings) {
|
public Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings) {
|
||||||
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
|
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
|
||||||
DecryptionBuilder.this.decryptionKeyDecryptor = decryptor;
|
DecryptionBuilder.this.decryptionKeyDecryptor = decryptor;
|
||||||
|
return new VerifyImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Verify doNotDecrypt() {
|
||||||
|
DecryptionBuilder.this.decryptionKeys = null;
|
||||||
|
DecryptionBuilder.this.decryptionKeyDecryptor = null;
|
||||||
|
return new VerifyImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VerifyImpl implements Verify {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifyWith verifyDetachedSignature(InputStream inputStream) throws IOException, PGPException {
|
||||||
|
List<PGPSignature> signatures = new ArrayList<>();
|
||||||
|
InputStream pgpIn = PGPUtil.getDecoderStream(inputStream);
|
||||||
|
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
||||||
|
pgpIn, keyFingerPrintCalculator);
|
||||||
|
Object nextObject = objectFactory.nextObject();
|
||||||
|
while (nextObject != null) {
|
||||||
|
if (nextObject instanceof PGPCompressedData) {
|
||||||
|
PGPCompressedData compressedData = (PGPCompressedData) nextObject;
|
||||||
|
objectFactory = new PGPObjectFactory(compressedData.getDataStream(), keyFingerPrintCalculator);
|
||||||
|
nextObject = objectFactory.nextObject();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nextObject instanceof PGPSignatureList) {
|
||||||
|
PGPSignatureList signatureList = (PGPSignatureList) nextObject;
|
||||||
|
for (PGPSignature s : signatureList) {
|
||||||
|
signatures.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextObject instanceof PGPSignature) {
|
||||||
|
signatures.add((PGPSignature) nextObject);
|
||||||
|
}
|
||||||
|
nextObject = objectFactory.nextObject();
|
||||||
|
}
|
||||||
|
pgpIn.close();
|
||||||
|
return verifyDetachedSignatures(signatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifyWith verifyDetachedSignatures(List<PGPSignature> signatures) {
|
||||||
|
DecryptionBuilder.this.detachedSignatures = signatures;
|
||||||
return new VerifyWithImpl();
|
return new VerifyWithImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VerifyWith doNotDecrypt() {
|
public HandleMissingPublicKeys verifyWith(@org.jetbrains.annotations.NotNull PGPPublicKeyRingCollection publicKeyRings) {
|
||||||
DecryptionBuilder.this.decryptionKeys = null;
|
return new VerifyWithImpl().verifyWith(publicKeyRings);
|
||||||
DecryptionBuilder.this.decryptionKeyDecryptor = null;
|
}
|
||||||
return new VerifyWithImpl();
|
|
||||||
|
@Override
|
||||||
|
public HandleMissingPublicKeys verifyWith(@org.jetbrains.annotations.NotNull Set<OpenPgpV4Fingerprint> trustedFingerprints, @org.jetbrains.annotations.NotNull PGPPublicKeyRingCollection publicKeyRings) {
|
||||||
|
return new VerifyWithImpl().verifyWith(trustedFingerprints, publicKeyRings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandleMissingPublicKeys verifyWith(@org.jetbrains.annotations.NotNull Set<PGPPublicKeyRing> publicKeyRings) {
|
||||||
|
return new VerifyWithImpl().verifyWith(publicKeyRings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Build doNotVerify() {
|
||||||
|
DecryptionBuilder.this.verificationKeys = null;
|
||||||
|
return new BuildImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,12 +171,6 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
DecryptionBuilder.this.verificationKeys = publicKeyRings;
|
DecryptionBuilder.this.verificationKeys = publicKeyRings;
|
||||||
return new HandleMissingPublicKeysImpl();
|
return new HandleMissingPublicKeysImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Build doNotVerify() {
|
|
||||||
DecryptionBuilder.this.verificationKeys = null;
|
|
||||||
return new BuildImpl();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class HandleMissingPublicKeysImpl implements HandleMissingPublicKeys {
|
class HandleMissingPublicKeysImpl implements HandleMissingPublicKeys {
|
||||||
|
@ -128,7 +193,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
@Override
|
@Override
|
||||||
public DecryptionStream build() throws IOException, PGPException {
|
public DecryptionStream build() throws IOException, PGPException {
|
||||||
return DecryptionStreamFactory.create(inputStream,
|
return DecryptionStreamFactory.create(inputStream,
|
||||||
decryptionKeys, decryptionKeyDecryptor, verificationKeys, missingPublicKeyCallback);
|
decryptionKeys, decryptionKeyDecryptor, detachedSignatures, verificationKeys, missingPublicKeyCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,14 @@ package org.pgpainless.decryption_verification;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
|
||||||
|
@ -33,12 +35,21 @@ public interface DecryptionBuilderInterface {
|
||||||
|
|
||||||
interface DecryptWith {
|
interface DecryptWith {
|
||||||
|
|
||||||
VerifyWith decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings);
|
Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings);
|
||||||
|
|
||||||
VerifyWith doNotDecrypt();
|
Verify doNotDecrypt();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Verify extends VerifyWith {
|
||||||
|
|
||||||
|
VerifyWith verifyDetachedSignature(InputStream inputStream) throws IOException, PGPException;
|
||||||
|
|
||||||
|
VerifyWith verifyDetachedSignatures(List<PGPSignature> signatures);
|
||||||
|
|
||||||
|
Build doNotVerify();
|
||||||
|
}
|
||||||
|
|
||||||
interface VerifyWith {
|
interface VerifyWith {
|
||||||
|
|
||||||
HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings);
|
HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings);
|
||||||
|
@ -47,7 +58,6 @@ public interface DecryptionBuilderInterface {
|
||||||
|
|
||||||
HandleMissingPublicKeys verifyWith(@Nonnull Set<PGPPublicKeyRing> publicKeyRings);
|
HandleMissingPublicKeys verifyWith(@Nonnull Set<PGPPublicKeyRing> publicKeyRings);
|
||||||
|
|
||||||
Build doNotVerify();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
|
||||||
public class DecryptionStream extends InputStream {
|
public class DecryptionStream extends InputStream {
|
||||||
|
|
||||||
private final InputStream inputStream;
|
private final InputStream inputStream;
|
||||||
|
@ -32,15 +34,36 @@ public class DecryptionStream extends InputStream {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
return inputStream.read();
|
int r = inputStream.read();
|
||||||
|
maybeUpdateDetachedSignatures(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeUpdateDetachedSignatures(int rByte) {
|
||||||
|
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||||
|
if (rByte != -1) {
|
||||||
|
s.getSignature().update((byte) rByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
maybeVerifyDetachedSignatures();
|
||||||
this.isClosed = true;
|
this.isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void maybeVerifyDetachedSignatures() {
|
||||||
|
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||||
|
try {
|
||||||
|
s.setVerified(s.getSignature().verify());
|
||||||
|
} catch (PGPException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public OpenPgpMetadata getResult() {
|
public OpenPgpMetadata getResult() {
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
throw new IllegalStateException("DecryptionStream MUST be closed before the result can be accessed.");
|
throw new IllegalStateException("DecryptionStream MUST be closed before the result can be accessed.");
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
@ -41,6 +42,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPUtil;
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
|
@ -66,7 +68,7 @@ public final class DecryptionStreamFactory {
|
||||||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||||
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
|
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
|
||||||
private final KeyFingerPrintCalculator keyFingerprintCalculator = new BcKeyFingerprintCalculator();
|
private final KeyFingerPrintCalculator keyFingerprintCalculator = new BcKeyFingerprintCalculator();
|
||||||
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
private final Map<OpenPgpV4Fingerprint, OnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
||||||
|
|
||||||
private DecryptionStreamFactory(@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
private DecryptionStreamFactory(@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
||||||
@Nullable SecretKeyRingProtector decryptor,
|
@Nullable SecretKeyRingProtector decryptor,
|
||||||
|
@ -81,21 +83,31 @@ public final class DecryptionStreamFactory {
|
||||||
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
||||||
@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
||||||
@Nullable SecretKeyRingProtector decryptor,
|
@Nullable SecretKeyRingProtector decryptor,
|
||||||
|
@Nullable List<PGPSignature> detachedSignatures,
|
||||||
@Nullable Set<PGPPublicKeyRing> verificationKeys,
|
@Nullable Set<PGPPublicKeyRing> verificationKeys,
|
||||||
@Nullable MissingPublicKeyCallback missingPublicKeyCallback)
|
@Nullable MissingPublicKeyCallback missingPublicKeyCallback)
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
|
InputStream pgpInputStream;
|
||||||
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, verificationKeys,
|
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, verificationKeys,
|
||||||
missingPublicKeyCallback);
|
missingPublicKeyCallback);
|
||||||
|
|
||||||
|
if (detachedSignatures != null) {
|
||||||
|
pgpInputStream = inputStream;
|
||||||
|
for (PGPSignature signature : detachedSignatures) {
|
||||||
|
PGPPublicKey signingKey = factory.findSignatureVerificationKey(signature.getKeyID());
|
||||||
|
signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey);
|
||||||
|
factory.resultBuilder.addDetachedSignature(
|
||||||
|
new DetachedSignature(signature, new OpenPgpV4Fingerprint(signingKey)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
||||||
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
|
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
|
||||||
|
pgpInputStream = factory.processPGPPackets(objectFactory);
|
||||||
return new DecryptionStream(factory.processPGPPackets(objectFactory), factory.resultBuilder);
|
}
|
||||||
|
return new DecryptionStream(pgpInputStream, factory.resultBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory) throws IOException, PGPException {
|
private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory) throws IOException, PGPException {
|
||||||
|
|
||||||
Object nextPgpObject;
|
Object nextPgpObject;
|
||||||
while ((nextPgpObject = objectFactory.nextObject()) != null) {
|
while ((nextPgpObject = objectFactory.nextObject()) != null) {
|
||||||
if (nextPgpObject instanceof PGPEncryptedDataList) {
|
if (nextPgpObject instanceof PGPEncryptedDataList) {
|
||||||
|
@ -214,18 +226,20 @@ public final class DecryptionStreamFactory {
|
||||||
|
|
||||||
private void processOnePassSignature(PGPOnePassSignature signature) throws PGPException {
|
private void processOnePassSignature(PGPOnePassSignature signature) throws PGPException {
|
||||||
final long keyId = signature.getKeyID();
|
final long keyId = signature.getKeyID();
|
||||||
resultBuilder.addUnverifiedSignatureKeyId(keyId);
|
|
||||||
|
|
||||||
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
|
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
|
||||||
|
|
||||||
// Find public key
|
// Find public key
|
||||||
PGPPublicKey verificationKey = findSignatureVerificationKey(keyId);
|
PGPPublicKey verificationKey = findSignatureVerificationKey(keyId);
|
||||||
if (verificationKey == null) {
|
if (verificationKey == null) {
|
||||||
|
LOGGER.log(LEVEL, "Missing verification key from " + Long.toHexString(keyId));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
signature.init(verifierBuilderProvider, verificationKey);
|
signature.init(verifierBuilderProvider, verificationKey);
|
||||||
verifiableOnePassSignatures.put(new OpenPgpV4Fingerprint(verificationKey), signature);
|
OnePassSignature onePassSignature = new OnePassSignature(signature, new OpenPgpV4Fingerprint(verificationKey));
|
||||||
|
resultBuilder.addOnePassSignature(onePassSignature);
|
||||||
|
verifiableOnePassSignatures.put(new OpenPgpV4Fingerprint(verificationKey), onePassSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPPublicKey findSignatureVerificationKey(long keyId) {
|
private PGPPublicKey findSignatureVerificationKey(long keyId) {
|
||||||
|
@ -269,4 +283,5 @@ public final class DecryptionStreamFactory {
|
||||||
|
|
||||||
return missingPublicKey;
|
return missingPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
public class DetachedSignature {
|
||||||
|
private final PGPSignature signature;
|
||||||
|
private final OpenPgpV4Fingerprint fingerprint;
|
||||||
|
private boolean verified;
|
||||||
|
|
||||||
|
public DetachedSignature(PGPSignature signature, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
this.signature = signature;
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVerified(boolean verified) {
|
||||||
|
this.verified = verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSignature getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpV4Fingerprint getFingerprint() {
|
||||||
|
return fingerprint;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
public class OnePassSignature {
|
||||||
|
private final PGPOnePassSignature onePassSignature;
|
||||||
|
private final OpenPgpV4Fingerprint fingerprint;
|
||||||
|
private PGPSignature signature;
|
||||||
|
private boolean verified;
|
||||||
|
|
||||||
|
public OnePassSignature(PGPOnePassSignature onePassSignature, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
this.onePassSignature = onePassSignature;
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPOnePassSignature getOnePassSignature() {
|
||||||
|
return onePassSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpV4Fingerprint getFingerprint() {
|
||||||
|
return fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(PGPSignature signature) throws PGPException {
|
||||||
|
this.verified = getOnePassSignature().verify(signature);
|
||||||
|
if (verified) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSignature getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.pgpainless.decryption_verification;
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -32,11 +34,8 @@ public class OpenPgpMetadata {
|
||||||
|
|
||||||
private final Set<Long> recipientKeyIds;
|
private final Set<Long> recipientKeyIds;
|
||||||
private final OpenPgpV4Fingerprint decryptionFingerprint;
|
private final OpenPgpV4Fingerprint decryptionFingerprint;
|
||||||
private final Set<PGPSignature> signatures;
|
private final List<OnePassSignature> onePassSignatures;
|
||||||
private final Set<Long> signatureKeyIds;
|
private final List<DetachedSignature> detachedSignatures;
|
||||||
private final Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures;
|
|
||||||
private final Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints;
|
|
||||||
|
|
||||||
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
||||||
private final CompressionAlgorithm compressionAlgorithm;
|
private final CompressionAlgorithm compressionAlgorithm;
|
||||||
private final boolean integrityProtected;
|
private final boolean integrityProtected;
|
||||||
|
@ -46,20 +45,16 @@ public class OpenPgpMetadata {
|
||||||
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||||
CompressionAlgorithm algorithm,
|
CompressionAlgorithm algorithm,
|
||||||
boolean integrityProtected,
|
boolean integrityProtected,
|
||||||
Set<PGPSignature> signatures,
|
List<OnePassSignature> onePassSignatures,
|
||||||
Set<Long> signatureKeyIds,
|
List<DetachedSignature> detachedSignatures) {
|
||||||
Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures,
|
|
||||||
Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints) {
|
|
||||||
|
|
||||||
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
||||||
this.decryptionFingerprint = decryptionFingerprint;
|
this.decryptionFingerprint = decryptionFingerprint;
|
||||||
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||||
this.compressionAlgorithm = algorithm;
|
this.compressionAlgorithm = algorithm;
|
||||||
this.integrityProtected = integrityProtected;
|
this.integrityProtected = integrityProtected;
|
||||||
this.signatures = Collections.unmodifiableSet(signatures);
|
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
|
||||||
this.signatureKeyIds = Collections.unmodifiableSet(signatureKeyIds);
|
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
|
||||||
this.verifiedSignatures = Collections.unmodifiableMap(verifiedSignatures);
|
|
||||||
this.verifiedSignaturesFingerprints = Collections.unmodifiableSet(verifiedSignaturesFingerprints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Long> getRecipientKeyIds() {
|
public Set<Long> getRecipientKeyIds() {
|
||||||
|
@ -87,27 +82,42 @@ public class OpenPgpMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<PGPSignature> getSignatures() {
|
public Set<PGPSignature> getSignatures() {
|
||||||
|
Set<PGPSignature> signatures = new HashSet<>();
|
||||||
|
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||||
|
signatures.add(detachedSignature.getSignature());
|
||||||
|
}
|
||||||
|
for (OnePassSignature onePassSignature : onePassSignatures) {
|
||||||
|
signatures.add(onePassSignature.getSignature());
|
||||||
|
}
|
||||||
return signatures;
|
return signatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Long> getSignatureKeyIDs() {
|
|
||||||
return signatureKeyIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return !signatureKeyIds.isEmpty();
|
return !getSignatures().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<OpenPgpV4Fingerprint, PGPSignature> getVerifiedSignatures() {
|
public Map<OpenPgpV4Fingerprint, PGPSignature> getVerifiedSignatures() {
|
||||||
|
Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures = new ConcurrentHashMap<>();
|
||||||
|
for (DetachedSignature detachedSignature : detachedSignatures) {
|
||||||
|
if (detachedSignature.isVerified()) {
|
||||||
|
verifiedSignatures.put(detachedSignature.getFingerprint(), detachedSignature.getSignature());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (OnePassSignature onePassSignature : onePassSignatures) {
|
||||||
|
if (onePassSignature.isVerified()) {
|
||||||
|
verifiedSignatures.put(onePassSignature.getFingerprint(), onePassSignature.getSignature());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return verifiedSignatures;
|
return verifiedSignatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<OpenPgpV4Fingerprint> getVerifiedSignatureKeyFingerprints() {
|
public Set<OpenPgpV4Fingerprint> getVerifiedSignatureKeyFingerprints() {
|
||||||
return verifiedSignaturesFingerprints;
|
return getVerifiedSignatures().keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return !verifiedSignaturesFingerprints.isEmpty();
|
return !getVerifiedSignatures().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) {
|
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) {
|
||||||
|
@ -121,7 +131,17 @@ public class OpenPgpMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsVerifiedSignatureFrom(OpenPgpV4Fingerprint fingerprint) {
|
public boolean containsVerifiedSignatureFrom(OpenPgpV4Fingerprint fingerprint) {
|
||||||
return verifiedSignaturesFingerprints.contains(fingerprint);
|
return getVerifiedSignatureKeyFingerprints().contains(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Signature {
|
||||||
|
protected final PGPSignature signature;
|
||||||
|
protected final OpenPgpV4Fingerprint fingerprint;
|
||||||
|
|
||||||
|
public Signature(PGPSignature signature, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
this.signature = signature;
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder getBuilder() {
|
public static Builder getBuilder() {
|
||||||
|
@ -132,10 +152,8 @@ public class OpenPgpMetadata {
|
||||||
|
|
||||||
private final Set<Long> recipientFingerprints = new HashSet<>();
|
private final Set<Long> recipientFingerprints = new HashSet<>();
|
||||||
private OpenPgpV4Fingerprint decryptionFingerprint;
|
private OpenPgpV4Fingerprint decryptionFingerprint;
|
||||||
private final Set<PGPSignature> signatures = new HashSet<>();
|
private final List<DetachedSignature> detachedSignatures = new ArrayList<>();
|
||||||
private final Set<Long> signatureKeyIds = new HashSet<>();
|
private final List<OnePassSignature> onePassSignatures = new ArrayList<>();
|
||||||
private final Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures = new ConcurrentHashMap<>();
|
|
||||||
private final Set<OpenPgpV4Fingerprint> verifiedSignatureKeyFingerprints = new HashSet<>();
|
|
||||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
||||||
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||||
private boolean integrityProtected = false;
|
private boolean integrityProtected = false;
|
||||||
|
@ -155,24 +173,8 @@ public class OpenPgpMetadata {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addSignature(PGPSignature signature) {
|
public List<DetachedSignature> getDetachedSignatures() {
|
||||||
signatures.add(signature);
|
return detachedSignatures;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addUnverifiedSignatureKeyId(Long keyId) {
|
|
||||||
this.signatureKeyIds.add(keyId);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder putVerifiedSignature(OpenPgpV4Fingerprint fingerprint, PGPSignature verifiedSignature) {
|
|
||||||
verifiedSignatures.put(fingerprint, verifiedSignature);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addVerifiedSignatureFingerprint(OpenPgpV4Fingerprint fingerprint) {
|
|
||||||
this.verifiedSignatureKeyFingerprints.add(fingerprint);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
|
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
|
||||||
|
@ -185,11 +187,18 @@ public class OpenPgpMetadata {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addDetachedSignature(DetachedSignature signature) {
|
||||||
|
this.detachedSignatures.add(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOnePassSignature(OnePassSignature onePassSignature) {
|
||||||
|
this.onePassSignatures.add(onePassSignature);
|
||||||
|
}
|
||||||
|
|
||||||
public OpenPgpMetadata build() {
|
public OpenPgpMetadata build() {
|
||||||
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
|
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
|
||||||
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
|
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
|
||||||
signatures, signatureKeyIds,
|
onePassSignatures, detachedSignatures);
|
||||||
verifiedSignatures, verifiedSignatureKeyFingerprints);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
@ -37,14 +36,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||||
private static final Level LEVEL = Level.FINE;
|
private static final Level LEVEL = Level.FINE;
|
||||||
|
|
||||||
private final PGPObjectFactory objectFactory;
|
private final PGPObjectFactory objectFactory;
|
||||||
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures;
|
private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures;
|
||||||
private final OpenPgpMetadata.Builder resultBuilder;
|
private final OpenPgpMetadata.Builder resultBuilder;
|
||||||
|
|
||||||
private boolean validated = false;
|
private boolean validated = false;
|
||||||
|
|
||||||
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
|
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
|
||||||
@Nonnull PGPObjectFactory objectFactory,
|
@Nonnull PGPObjectFactory objectFactory,
|
||||||
@Nonnull Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures,
|
@Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures,
|
||||||
@Nonnull OpenPgpMetadata.Builder resultBuilder) {
|
@Nonnull OpenPgpMetadata.Builder resultBuilder) {
|
||||||
super(inputStream);
|
super(inputStream);
|
||||||
this.objectFactory = objectFactory;
|
this.objectFactory = objectFactory;
|
||||||
|
@ -55,14 +54,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOnePassSignatures(byte data) {
|
private void updateOnePassSignatures(byte data) {
|
||||||
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||||
signature.update(data);
|
signature.getOnePassSignature().update(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOnePassSignatures(byte[] b, int off, int len) {
|
private void updateOnePassSignatures(byte[] b, int off, int len) {
|
||||||
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||||
signature.update(b, off, len);
|
signature.getOnePassSignature().update(b, off, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +86,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (PGPSignature signature : signatureList) {
|
for (PGPSignature signature : signatureList) {
|
||||||
resultBuilder.addSignature(signature);
|
|
||||||
|
|
||||||
OpenPgpV4Fingerprint fingerprint = findFingerprintForSignature(signature);
|
OpenPgpV4Fingerprint fingerprint = findFingerprintForSignature(signature);
|
||||||
PGPOnePassSignature onePassSignature = findOnePassSignature(fingerprint);
|
OnePassSignature onePassSignature = findOnePassSignature(fingerprint);
|
||||||
if (onePassSignature == null) {
|
if (onePassSignature == null) {
|
||||||
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
|
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
|
||||||
continue;
|
continue;
|
||||||
|
@ -103,17 +100,17 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySignatureOrThrowSignatureException(PGPSignature signature, OpenPgpV4Fingerprint fingerprint, PGPOnePassSignature onePassSignature) throws PGPException, SignatureException {
|
private void verifySignatureOrThrowSignatureException(PGPSignature signature, OpenPgpV4Fingerprint fingerprint,
|
||||||
|
OnePassSignature onePassSignature)
|
||||||
|
throws PGPException, SignatureException {
|
||||||
if (onePassSignature.verify(signature)) {
|
if (onePassSignature.verify(signature)) {
|
||||||
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
|
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
|
||||||
resultBuilder.putVerifiedSignature(fingerprint, signature);
|
|
||||||
resultBuilder.addVerifiedSignatureFingerprint(fingerprint);
|
|
||||||
} else {
|
} else {
|
||||||
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
|
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPOnePassSignature findOnePassSignature(OpenPgpV4Fingerprint fingerprint) {
|
private OnePassSignature findOnePassSignature(OpenPgpV4Fingerprint fingerprint) {
|
||||||
if (fingerprint != null) {
|
if (fingerprint != null) {
|
||||||
return onePassSignatures.get(fingerprint);
|
return onePassSignatures.get(fingerprint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
|
@ -30,9 +32,12 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.exception.SecretKeyNotFoundException;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
|
import org.pgpainless.key.selection.key.PublicKeySelectionStrategy;
|
||||||
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
|
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
|
||||||
|
@ -48,6 +53,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
|
|
||||||
private OutputStream outputStream;
|
private OutputStream outputStream;
|
||||||
private final Set<PGPPublicKey> encryptionKeys = new HashSet<>();
|
private final Set<PGPPublicKey> encryptionKeys = new HashSet<>();
|
||||||
|
private boolean detachedSignature = false;
|
||||||
private final Set<PGPSecretKey> signingKeys = new HashSet<>();
|
private final Set<PGPSecretKey> signingKeys = new HashSet<>();
|
||||||
private SecretKeyRingProtector signingKeysDecryptor;
|
private SecretKeyRingProtector signingKeysDecryptor;
|
||||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128;
|
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128;
|
||||||
|
@ -142,8 +148,8 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignWith doNotEncrypt() {
|
public DetachedSign doNotEncrypt() {
|
||||||
return new SignWithImpl();
|
return new DetachedSignImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +222,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignWith usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
public DetachedSign usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||||
@Nonnull HashAlgorithm hashAlgorithm,
|
@Nonnull HashAlgorithm hashAlgorithm,
|
||||||
@Nonnull CompressionAlgorithm compressionAlgorithm) {
|
@Nonnull CompressionAlgorithm compressionAlgorithm) {
|
||||||
|
|
||||||
|
@ -224,17 +230,46 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
EncryptionBuilder.this.hashAlgorithm = hashAlgorithm;
|
EncryptionBuilder.this.hashAlgorithm = hashAlgorithm;
|
||||||
EncryptionBuilder.this.compressionAlgorithm = compressionAlgorithm;
|
EncryptionBuilder.this.compressionAlgorithm = compressionAlgorithm;
|
||||||
|
|
||||||
return new SignWithImpl();
|
return new DetachedSignImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignWith usingSecureAlgorithms() {
|
public DetachedSign usingSecureAlgorithms() {
|
||||||
EncryptionBuilder.this.symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256;
|
EncryptionBuilder.this.symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256;
|
||||||
EncryptionBuilder.this.hashAlgorithm = HashAlgorithm.SHA512;
|
EncryptionBuilder.this.hashAlgorithm = HashAlgorithm.SHA512;
|
||||||
EncryptionBuilder.this.compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
EncryptionBuilder.this.compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||||
|
|
||||||
|
return new DetachedSignImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DetachedSignImpl implements DetachedSign {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignWith createDetachedSignature() {
|
||||||
|
EncryptionBuilder.this.detachedSignature = true;
|
||||||
return new SignWithImpl();
|
return new SignWithImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Armor doNotSign() {
|
||||||
|
return new ArmorImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <O> Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKey... keys) {
|
||||||
|
return new SignWithImpl().signWith(decryptor, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <O> Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKeyRing... keyRings) {
|
||||||
|
return new SignWithImpl().signWith(decryptor, keyRings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <O> Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingSelectionStrategy<O> selectionStrategy, @org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull MultiMap<O, PGPSecretKeyRingCollection> keys) throws SecretKeyNotFoundException {
|
||||||
|
return new SignWithImpl().signWith(selectionStrategy, decryptor, keys);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignWithImpl implements SignWith {
|
class SignWithImpl implements SignWith {
|
||||||
|
@ -296,11 +331,6 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
}
|
}
|
||||||
return new ArmorImpl();
|
return new ArmorImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Armor doNotSign() {
|
|
||||||
return new ArmorImpl();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArmorImpl implements Armor {
|
class ArmorImpl implements Armor {
|
||||||
|
@ -319,14 +349,16 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
|
|
||||||
private EncryptionStream build() throws IOException, PGPException {
|
private EncryptionStream build() throws IOException, PGPException {
|
||||||
|
|
||||||
Set<PGPPrivateKey> privateKeys = new HashSet<>();
|
Map<OpenPgpV4Fingerprint, PGPPrivateKey> privateKeys = new ConcurrentHashMap<>();
|
||||||
for (PGPSecretKey secretKey : signingKeys) {
|
for (PGPSecretKey secretKey : signingKeys) {
|
||||||
privateKeys.add(secretKey.extractPrivateKey(signingKeysDecryptor.getDecryptor(secretKey.getKeyID())));
|
privateKeys.put(new OpenPgpV4Fingerprint(secretKey),
|
||||||
|
secretKey.extractPrivateKey(signingKeysDecryptor.getDecryptor(secretKey.getKeyID())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EncryptionStream(
|
return new EncryptionStream(
|
||||||
EncryptionBuilder.this.outputStream,
|
EncryptionBuilder.this.outputStream,
|
||||||
EncryptionBuilder.this.encryptionKeys,
|
EncryptionBuilder.this.encryptionKeys,
|
||||||
|
EncryptionBuilder.this.detachedSignature,
|
||||||
privateKeys,
|
privateKeys,
|
||||||
EncryptionBuilder.this.symmetricKeyAlgorithm,
|
EncryptionBuilder.this.symmetricKeyAlgorithm,
|
||||||
EncryptionBuilder.this.hashAlgorithm,
|
EncryptionBuilder.this.hashAlgorithm,
|
||||||
|
|
|
@ -50,7 +50,7 @@ public interface EncryptionBuilderInterface {
|
||||||
<O> WithAlgorithms toRecipients(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
<O> WithAlgorithms toRecipients(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
||||||
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
||||||
|
|
||||||
SignWith doNotEncrypt();
|
DetachedSign doNotEncrypt();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,11 +65,18 @@ public interface EncryptionBuilderInterface {
|
||||||
<O> WithAlgorithms andToSelf(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
<O> WithAlgorithms andToSelf(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
||||||
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
||||||
|
|
||||||
SignWith usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
DetachedSign usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||||
@Nonnull HashAlgorithm hashAlgorithm,
|
@Nonnull HashAlgorithm hashAlgorithm,
|
||||||
@Nonnull CompressionAlgorithm compressionAlgorithm);
|
@Nonnull CompressionAlgorithm compressionAlgorithm);
|
||||||
|
|
||||||
SignWith usingSecureAlgorithms();
|
DetachedSign usingSecureAlgorithms();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DetachedSign extends SignWith {
|
||||||
|
SignWith createDetachedSignature();
|
||||||
|
|
||||||
|
Armor doNotSign();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +91,6 @@ public interface EncryptionBuilderInterface {
|
||||||
@Nonnull MultiMap<O, PGPSecretKeyRingCollection> keys)
|
@Nonnull MultiMap<O, PGPSecretKeyRingCollection> keys)
|
||||||
throws SecretKeyNotFoundException;
|
throws SecretKeyNotFoundException;
|
||||||
|
|
||||||
Armor doNotSign();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Armor {
|
interface Armor {
|
||||||
|
|
|
@ -21,8 +21,11 @@ import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -35,15 +38,19 @@ import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
|
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
|
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.decryption_verification.DetachedSignature;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is based upon Jens Neuhalfen's Bouncy-GPG PGPEncryptingStream.
|
* This class is based upon Jens Neuhalfen's Bouncy-GPG PGPEncryptingStream.
|
||||||
|
@ -60,12 +67,13 @@ public final class EncryptionStream extends OutputStream {
|
||||||
private final HashAlgorithm hashAlgorithm;
|
private final HashAlgorithm hashAlgorithm;
|
||||||
private final CompressionAlgorithm compressionAlgorithm;
|
private final CompressionAlgorithm compressionAlgorithm;
|
||||||
private final Set<PGPPublicKey> encryptionKeys;
|
private final Set<PGPPublicKey> encryptionKeys;
|
||||||
private final Set<PGPPrivateKey> signingKeys;
|
private final boolean detachedSignature;
|
||||||
|
private final Map<OpenPgpV4Fingerprint, PGPPrivateKey> signingKeys;
|
||||||
private final boolean asciiArmor;
|
private final boolean asciiArmor;
|
||||||
|
|
||||||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||||
|
|
||||||
private List<PGPSignatureGenerator> signatureGenerators = new ArrayList<>();
|
private Map<OpenPgpV4Fingerprint, PGPSignatureGenerator> signatureGenerators = new ConcurrentHashMap<>();
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
OutputStream outermostStream = null;
|
OutputStream outermostStream = null;
|
||||||
|
@ -81,7 +89,8 @@ public final class EncryptionStream extends OutputStream {
|
||||||
|
|
||||||
EncryptionStream(@Nonnull OutputStream targetOutputStream,
|
EncryptionStream(@Nonnull OutputStream targetOutputStream,
|
||||||
@Nonnull Set<PGPPublicKey> encryptionKeys,
|
@Nonnull Set<PGPPublicKey> encryptionKeys,
|
||||||
@Nonnull Set<PGPPrivateKey> signingKeys,
|
boolean detachedSignature,
|
||||||
|
@Nonnull Map<OpenPgpV4Fingerprint, PGPPrivateKey> signingKeys,
|
||||||
@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||||
@Nonnull HashAlgorithm hashAlgorithm,
|
@Nonnull HashAlgorithm hashAlgorithm,
|
||||||
@Nonnull CompressionAlgorithm compressionAlgorithm,
|
@Nonnull CompressionAlgorithm compressionAlgorithm,
|
||||||
|
@ -92,7 +101,8 @@ public final class EncryptionStream extends OutputStream {
|
||||||
this.hashAlgorithm = hashAlgorithm;
|
this.hashAlgorithm = hashAlgorithm;
|
||||||
this.compressionAlgorithm = compressionAlgorithm;
|
this.compressionAlgorithm = compressionAlgorithm;
|
||||||
this.encryptionKeys = Collections.unmodifiableSet(encryptionKeys);
|
this.encryptionKeys = Collections.unmodifiableSet(encryptionKeys);
|
||||||
this.signingKeys = Collections.unmodifiableSet(signingKeys);
|
this.detachedSignature = detachedSignature;
|
||||||
|
this.signingKeys = Collections.unmodifiableMap(signingKeys);
|
||||||
this.asciiArmor = asciiArmor;
|
this.asciiArmor = asciiArmor;
|
||||||
|
|
||||||
outermostStream = targetOutputStream;
|
outermostStream = targetOutputStream;
|
||||||
|
@ -144,14 +154,15 @@ public final class EncryptionStream extends OutputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.log(LEVEL, "At least one signing key is available -> sign " + hashAlgorithm + " hash of message");
|
LOGGER.log(LEVEL, "At least one signing key is available -> sign " + hashAlgorithm + " hash of message");
|
||||||
for (PGPPrivateKey privateKey : signingKeys) {
|
for (OpenPgpV4Fingerprint fingerprint : signingKeys.keySet()) {
|
||||||
LOGGER.log(LEVEL, "Sign using key " + Long.toHexString(privateKey.getKeyID()));
|
PGPPrivateKey privateKey = signingKeys.get(fingerprint);
|
||||||
|
LOGGER.log(LEVEL, "Sign using key " + fingerprint);
|
||||||
BcPGPContentSignerBuilder contentSignerBuilder = new BcPGPContentSignerBuilder(
|
BcPGPContentSignerBuilder contentSignerBuilder = new BcPGPContentSignerBuilder(
|
||||||
privateKey.getPublicKeyPacket().getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
privateKey.getPublicKeyPacket().getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||||
|
|
||||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
|
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
|
||||||
signatureGenerators.add(signatureGenerator);
|
signatureGenerators.put(fingerprint, signatureGenerator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +174,7 @@ public final class EncryptionStream extends OutputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareOnePassSignatures() throws IOException, PGPException {
|
private void prepareOnePassSignatures() throws IOException, PGPException {
|
||||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||||
signatureGenerator.generateOnePassVersion(false).encode(basicCompressionStream);
|
signatureGenerator.generateOnePassVersion(false).encode(basicCompressionStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +197,7 @@ public final class EncryptionStream extends OutputStream {
|
||||||
public void write(int data) throws IOException {
|
public void write(int data) throws IOException {
|
||||||
literalDataStream.write(data);
|
literalDataStream.write(data);
|
||||||
|
|
||||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||||
byte asByte = (byte) (data & 0xff);
|
byte asByte = (byte) (data & 0xff);
|
||||||
signatureGenerator.update(asByte);
|
signatureGenerator.update(asByte);
|
||||||
}
|
}
|
||||||
|
@ -201,7 +212,7 @@ public final class EncryptionStream extends OutputStream {
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buffer, int off, int len) throws IOException {
|
public void write(byte[] buffer, int off, int len) throws IOException {
|
||||||
literalDataStream.write(buffer, 0, len);
|
literalDataStream.write(buffer, 0, len);
|
||||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||||
signatureGenerator.update(buffer, 0, len);
|
signatureGenerator.update(buffer, 0, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,12 +253,14 @@ public final class EncryptionStream extends OutputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeSignatures() throws IOException {
|
private void writeSignatures() throws IOException {
|
||||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
for (OpenPgpV4Fingerprint fingerprint : signatureGenerators.keySet()) {
|
||||||
|
PGPSignatureGenerator signatureGenerator = signatureGenerators.get(fingerprint);
|
||||||
try {
|
try {
|
||||||
PGPSignature signature = signatureGenerator.generate();
|
PGPSignature signature = signatureGenerator.generate();
|
||||||
|
if (!detachedSignature) {
|
||||||
signature.encode(basicCompressionStream);
|
signature.encode(basicCompressionStream);
|
||||||
resultBuilder.addSignature(signature);
|
}
|
||||||
resultBuilder.addUnverifiedSignatureKeyId(signature.getKeyID());
|
resultBuilder.addDetachedSignature(new DetachedSignature(signature, fingerprint));
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class DecryptAndVerifyMessageTest {
|
||||||
assertTrue(metadata.isVerified());
|
assertTrue(metadata.isVerified());
|
||||||
assertEquals(CompressionAlgorithm.ZLIB, metadata.getCompressionAlgorithm());
|
assertEquals(CompressionAlgorithm.ZLIB, metadata.getCompressionAlgorithm());
|
||||||
assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getSymmetricKeyAlgorithm());
|
assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getSymmetricKeyAlgorithm());
|
||||||
assertEquals(1, metadata.getSignatureKeyIDs().size());
|
//assertEquals(1, metadata.getSignatureKeyIDs().size());
|
||||||
assertEquals(1, metadata.getVerifiedSignatureKeyFingerprints().size());
|
assertEquals(1, metadata.getVerifiedSignatureKeyFingerprints().size());
|
||||||
assertTrue(metadata.containsVerifiedSignatureFrom(TestKeys.JULIET_FINGERPRINT));
|
assertTrue(metadata.containsVerifiedSignatureFrom(TestKeys.JULIET_FINGERPRINT));
|
||||||
assertEquals(TestKeys.JULIET_FINGERPRINT, metadata.getDecryptionFingerprint());
|
assertEquals(TestKeys.JULIET_FINGERPRINT, metadata.getDecryptionFingerprint());
|
||||||
|
|
|
@ -26,11 +26,15 @@ import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
@ -38,6 +42,7 @@ import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.key.collection.PGPKeyRing;
|
import org.pgpainless.key.collection.PGPKeyRing;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
|
@ -145,9 +150,9 @@ public class EncryptDecryptTest {
|
||||||
|
|
||||||
OpenPgpMetadata encryptionResult = encryptor.getResult();
|
OpenPgpMetadata encryptionResult = encryptor.getResult();
|
||||||
|
|
||||||
assertFalse(encryptionResult.getSignatureKeyIDs().isEmpty());
|
assertFalse(encryptionResult.getSignatures().isEmpty());
|
||||||
for (long keyId : encryptionResult.getSignatureKeyIDs()) {
|
for (OpenPgpV4Fingerprint fingerprint : encryptionResult.getVerifiedSignatures().keySet()) {
|
||||||
assertTrue(BCUtil.keyRingContainsKeyWithId(senderPub, keyId));
|
assertTrue(BCUtil.keyRingContainsKeyWithId(senderPub, fingerprint.getKeyId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(encryptionResult.getRecipientKeyIds().isEmpty());
|
assertFalse(encryptionResult.getRecipientKeyIds().isEmpty());
|
||||||
|
@ -180,4 +185,44 @@ public class EncryptDecryptTest {
|
||||||
assertTrue(result.isEncrypted());
|
assertTrue(result.isEncrypted());
|
||||||
assertTrue(result.isVerified());
|
assertTrue(result.isVerified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDetachedSignatureCreationAndVerification() throws IOException, PGPException {
|
||||||
|
PGPKeyRing signingKeys = new PGPKeyRing(TestKeys.getJulietPublicKeyRing(), TestKeys.getJulietSecretKeyRing());
|
||||||
|
SecretKeyRingProtector keyRingProtector = new UnprotectedKeysProtector();
|
||||||
|
byte[] data = testMessage.getBytes();
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
|
||||||
|
ByteArrayOutputStream dummyOut = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream signer = PGPainless.createEncryptor().onOutputStream(dummyOut)
|
||||||
|
.doNotEncrypt()
|
||||||
|
.createDetachedSignature()
|
||||||
|
.signWith(keyRingProtector, signingKeys.getSecretKeys())
|
||||||
|
.noArmor();
|
||||||
|
Streams.pipeAll(inputStream, signer);
|
||||||
|
signer.close();
|
||||||
|
OpenPgpMetadata metadata = signer.getResult();
|
||||||
|
|
||||||
|
Set<PGPSignature> signatureSet = metadata.getSignatures();
|
||||||
|
ByteArrayOutputStream sigOut = new ByteArrayOutputStream();
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(sigOut);
|
||||||
|
signatureSet.iterator().next().encode(armorOut);
|
||||||
|
armorOut.close();
|
||||||
|
String armorSig = sigOut.toString();
|
||||||
|
|
||||||
|
System.out.println(armorSig);
|
||||||
|
|
||||||
|
inputStream = new ByteArrayInputStream(testMessage.getBytes());
|
||||||
|
DecryptionStream verifier = PGPainless.createDecryptor().onInputStream(inputStream)
|
||||||
|
.doNotDecrypt()
|
||||||
|
.verifyDetachedSignature(new ByteArrayInputStream(armorSig.getBytes()))
|
||||||
|
.verifyWith(Collections.singleton(signingKeys.getPublicKeys()))
|
||||||
|
.ignoreMissingPublicKeys()
|
||||||
|
.build();
|
||||||
|
dummyOut = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(verifier, dummyOut);
|
||||||
|
verifier.close();
|
||||||
|
|
||||||
|
metadata = verifier.getResult();
|
||||||
|
assertFalse(metadata.getVerifiedSignatures().isEmpty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue