mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-22 19:08:00 +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 {
|
||||
implementation 'org.jetbrains:annotations:19.0.0'
|
||||
testImplementation group: 'junit', name: 'junit', version: '4.12'
|
||||
/*
|
||||
implementation "org.bouncycastle:bcprov-debug-jdk15on:$bouncyCastleVersion"
|
||||
|
|
|
@ -17,15 +17,24 @@ package org.pgpainless.decryption_verification;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
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.protection.SecretKeyRingProtector;
|
||||
|
||||
|
@ -34,9 +43,12 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
|||
private InputStream inputStream;
|
||||
private PGPSecretKeyRingCollection decryptionKeys;
|
||||
private SecretKeyRingProtector decryptionKeyDecryptor;
|
||||
private List<PGPSignature> detachedSignatures;
|
||||
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
||||
private MissingPublicKeyCallback missingPublicKeyCallback = null;
|
||||
|
||||
private final KeyFingerPrintCalculator keyFingerPrintCalculator = new BcKeyFingerprintCalculator();
|
||||
|
||||
@Override
|
||||
public DecryptWith onInputStream(InputStream inputStream) {
|
||||
this.inputStream = inputStream;
|
||||
|
@ -46,17 +58,76 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
|||
class DecryptWithImpl implements DecryptWith {
|
||||
|
||||
@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.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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerifyWith doNotDecrypt() {
|
||||
DecryptionBuilder.this.decryptionKeys = null;
|
||||
DecryptionBuilder.this.decryptionKeyDecryptor = null;
|
||||
return new VerifyWithImpl();
|
||||
public HandleMissingPublicKeys verifyWith(@org.jetbrains.annotations.NotNull PGPPublicKeyRingCollection publicKeyRings) {
|
||||
return new VerifyWithImpl().verifyWith(publicKeyRings);
|
||||
}
|
||||
|
||||
@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;
|
||||
return new HandleMissingPublicKeysImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Build doNotVerify() {
|
||||
DecryptionBuilder.this.verificationKeys = null;
|
||||
return new BuildImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class HandleMissingPublicKeysImpl implements HandleMissingPublicKeys {
|
||||
|
@ -128,7 +193,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
|||
@Override
|
||||
public DecryptionStream build() throws IOException, PGPException {
|
||||
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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
|
||||
|
@ -33,12 +35,21 @@ public interface DecryptionBuilderInterface {
|
|||
|
||||
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 {
|
||||
|
||||
HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings);
|
||||
|
@ -47,7 +58,6 @@ public interface DecryptionBuilderInterface {
|
|||
|
||||
HandleMissingPublicKeys verifyWith(@Nonnull Set<PGPPublicKeyRing> publicKeyRings);
|
||||
|
||||
Build doNotVerify();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import javax.annotation.Nonnull;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
|
||||
public class DecryptionStream extends InputStream {
|
||||
|
||||
private final InputStream inputStream;
|
||||
|
@ -32,15 +34,36 @@ public class DecryptionStream extends InputStream {
|
|||
|
||||
@Override
|
||||
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
|
||||
public void close() throws IOException {
|
||||
inputStream.close();
|
||||
maybeVerifyDetachedSignatures();
|
||||
this.isClosed = true;
|
||||
}
|
||||
|
||||
void maybeVerifyDetachedSignatures() {
|
||||
for (DetachedSignature s : resultBuilder.getDetachedSignatures()) {
|
||||
try {
|
||||
s.setVerified(s.getSignature().verify());
|
||||
} catch (PGPException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OpenPgpMetadata getResult() {
|
||||
if (!isClosed) {
|
||||
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.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
@ -41,6 +42,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
|||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPUtil;
|
||||
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||
|
@ -66,7 +68,7 @@ public final class DecryptionStreamFactory {
|
|||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
|
||||
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,
|
||||
@Nullable SecretKeyRingProtector decryptor,
|
||||
|
@ -81,21 +83,31 @@ public final class DecryptionStreamFactory {
|
|||
public static DecryptionStream create(@Nonnull InputStream inputStream,
|
||||
@Nullable PGPSecretKeyRingCollection decryptionKeys,
|
||||
@Nullable SecretKeyRingProtector decryptor,
|
||||
@Nullable List<PGPSignature> detachedSignatures,
|
||||
@Nullable Set<PGPPublicKeyRing> verificationKeys,
|
||||
@Nullable MissingPublicKeyCallback missingPublicKeyCallback)
|
||||
throws IOException, PGPException {
|
||||
|
||||
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, verificationKeys,
|
||||
InputStream pgpInputStream;
|
||||
DecryptionStreamFactory factory = new DecryptionStreamFactory(decryptionKeys, decryptor, verificationKeys,
|
||||
missingPublicKeyCallback);
|
||||
|
||||
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
||||
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
|
||||
|
||||
return new DecryptionStream(factory.processPGPPackets(objectFactory), factory.resultBuilder);
|
||||
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(
|
||||
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
|
||||
pgpInputStream = factory.processPGPPackets(objectFactory);
|
||||
}
|
||||
return new DecryptionStream(pgpInputStream, factory.resultBuilder);
|
||||
}
|
||||
|
||||
private InputStream processPGPPackets(@Nonnull PGPObjectFactory objectFactory) throws IOException, PGPException {
|
||||
|
||||
Object nextPgpObject;
|
||||
while ((nextPgpObject = objectFactory.nextObject()) != null) {
|
||||
if (nextPgpObject instanceof PGPEncryptedDataList) {
|
||||
|
@ -214,18 +226,20 @@ public final class DecryptionStreamFactory {
|
|||
|
||||
private void processOnePassSignature(PGPOnePassSignature signature) throws PGPException {
|
||||
final long keyId = signature.getKeyID();
|
||||
resultBuilder.addUnverifiedSignatureKeyId(keyId);
|
||||
|
||||
LOGGER.log(LEVEL, "Message contains OnePassSignature from " + Long.toHexString(keyId));
|
||||
|
||||
// Find public key
|
||||
PGPPublicKey verificationKey = findSignatureVerificationKey(keyId);
|
||||
if (verificationKey == null) {
|
||||
LOGGER.log(LEVEL, "Missing verification key from " + Long.toHexString(keyId));
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -269,4 +283,5 @@ public final class DecryptionStreamFactory {
|
|||
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -32,11 +34,8 @@ public class OpenPgpMetadata {
|
|||
|
||||
private final Set<Long> recipientKeyIds;
|
||||
private final OpenPgpV4Fingerprint decryptionFingerprint;
|
||||
private final Set<PGPSignature> signatures;
|
||||
private final Set<Long> signatureKeyIds;
|
||||
private final Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures;
|
||||
private final Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints;
|
||||
|
||||
private final List<OnePassSignature> onePassSignatures;
|
||||
private final List<DetachedSignature> detachedSignatures;
|
||||
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
||||
private final CompressionAlgorithm compressionAlgorithm;
|
||||
private final boolean integrityProtected;
|
||||
|
@ -46,20 +45,16 @@ public class OpenPgpMetadata {
|
|||
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
CompressionAlgorithm algorithm,
|
||||
boolean integrityProtected,
|
||||
Set<PGPSignature> signatures,
|
||||
Set<Long> signatureKeyIds,
|
||||
Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures,
|
||||
Set<OpenPgpV4Fingerprint> verifiedSignaturesFingerprints) {
|
||||
List<OnePassSignature> onePassSignatures,
|
||||
List<DetachedSignature> detachedSignatures) {
|
||||
|
||||
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
||||
this.decryptionFingerprint = decryptionFingerprint;
|
||||
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||
this.compressionAlgorithm = algorithm;
|
||||
this.integrityProtected = integrityProtected;
|
||||
this.signatures = Collections.unmodifiableSet(signatures);
|
||||
this.signatureKeyIds = Collections.unmodifiableSet(signatureKeyIds);
|
||||
this.verifiedSignatures = Collections.unmodifiableMap(verifiedSignatures);
|
||||
this.verifiedSignaturesFingerprints = Collections.unmodifiableSet(verifiedSignaturesFingerprints);
|
||||
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
|
||||
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
|
||||
}
|
||||
|
||||
public Set<Long> getRecipientKeyIds() {
|
||||
|
@ -87,27 +82,42 @@ public class OpenPgpMetadata {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Set<Long> getSignatureKeyIDs() {
|
||||
return signatureKeyIds;
|
||||
}
|
||||
|
||||
public boolean isSigned() {
|
||||
return !signatureKeyIds.isEmpty();
|
||||
return !getSignatures().isEmpty();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Set<OpenPgpV4Fingerprint> getVerifiedSignatureKeyFingerprints() {
|
||||
return verifiedSignaturesFingerprints;
|
||||
return getVerifiedSignatures().keySet();
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return !verifiedSignaturesFingerprints.isEmpty();
|
||||
return !getVerifiedSignatures().isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsVerifiedSignatureFrom(PGPPublicKeyRing publicKeys) {
|
||||
|
@ -121,7 +131,17 @@ public class OpenPgpMetadata {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -132,10 +152,8 @@ public class OpenPgpMetadata {
|
|||
|
||||
private final Set<Long> recipientFingerprints = new HashSet<>();
|
||||
private OpenPgpV4Fingerprint decryptionFingerprint;
|
||||
private final Set<PGPSignature> signatures = new HashSet<>();
|
||||
private final Set<Long> signatureKeyIds = new HashSet<>();
|
||||
private final Map<OpenPgpV4Fingerprint, PGPSignature> verifiedSignatures = new ConcurrentHashMap<>();
|
||||
private final Set<OpenPgpV4Fingerprint> verifiedSignatureKeyFingerprints = new HashSet<>();
|
||||
private final List<DetachedSignature> detachedSignatures = new ArrayList<>();
|
||||
private final List<OnePassSignature> onePassSignatures = new ArrayList<>();
|
||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
||||
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||
private boolean integrityProtected = false;
|
||||
|
@ -155,24 +173,8 @@ public class OpenPgpMetadata {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder addSignature(PGPSignature signature) {
|
||||
signatures.add(signature);
|
||||
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 List<DetachedSignature> getDetachedSignatures() {
|
||||
return detachedSignatures;
|
||||
}
|
||||
|
||||
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
|
||||
|
@ -185,11 +187,18 @@ public class OpenPgpMetadata {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void addDetachedSignature(DetachedSignature signature) {
|
||||
this.detachedSignatures.add(signature);
|
||||
}
|
||||
|
||||
public void addOnePassSignature(OnePassSignature onePassSignature) {
|
||||
this.onePassSignatures.add(onePassSignature);
|
||||
}
|
||||
|
||||
public OpenPgpMetadata build() {
|
||||
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
|
||||
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
|
||||
signatures, signatureKeyIds,
|
||||
verifiedSignatures, verifiedSignatureKeyFingerprints);
|
||||
onePassSignatures, detachedSignatures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.util.logging.Logger;
|
|||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
@ -37,14 +36,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
|||
private static final Level LEVEL = Level.FINE;
|
||||
|
||||
private final PGPObjectFactory objectFactory;
|
||||
private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures;
|
||||
private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures;
|
||||
private final OpenPgpMetadata.Builder resultBuilder;
|
||||
|
||||
private boolean validated = false;
|
||||
|
||||
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
|
||||
@Nonnull PGPObjectFactory objectFactory,
|
||||
@Nonnull Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures,
|
||||
@Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures,
|
||||
@Nonnull OpenPgpMetadata.Builder resultBuilder) {
|
||||
super(inputStream);
|
||||
this.objectFactory = objectFactory;
|
||||
|
@ -55,14 +54,14 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
|||
}
|
||||
|
||||
private void updateOnePassSignatures(byte data) {
|
||||
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.update(data);
|
||||
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.getOnePassSignature().update(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateOnePassSignatures(byte[] b, int off, int len) {
|
||||
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.update(b, off, len);
|
||||
for (OnePassSignature signature : onePassSignatures.values()) {
|
||||
signature.getOnePassSignature().update(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,10 +86,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
|||
|
||||
try {
|
||||
for (PGPSignature signature : signatureList) {
|
||||
resultBuilder.addSignature(signature);
|
||||
|
||||
OpenPgpV4Fingerprint fingerprint = findFingerprintForSignature(signature);
|
||||
PGPOnePassSignature onePassSignature = findOnePassSignature(fingerprint);
|
||||
OnePassSignature onePassSignature = findOnePassSignature(fingerprint);
|
||||
if (onePassSignature == null) {
|
||||
LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip");
|
||||
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)) {
|
||||
LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID()));
|
||||
resultBuilder.putVerifiedSignature(fingerprint, signature);
|
||||
resultBuilder.addVerifiedSignatureFingerprint(fingerprint);
|
||||
} else {
|
||||
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
|
||||
}
|
||||
}
|
||||
|
||||
private PGPOnePassSignature findOnePassSignature(OpenPgpV4Fingerprint fingerprint) {
|
||||
private OnePassSignature findOnePassSignature(OpenPgpV4Fingerprint fingerprint) {
|
||||
if (fingerprint != null) {
|
||||
return onePassSignatures.get(fingerprint);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ import java.io.IOException;
|
|||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
|
@ -30,9 +32,12 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
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.selection.key.PublicKeySelectionStrategy;
|
||||
import org.pgpainless.key.selection.key.SecretKeySelectionStrategy;
|
||||
|
@ -48,6 +53,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
|
||||
private OutputStream outputStream;
|
||||
private final Set<PGPPublicKey> encryptionKeys = new HashSet<>();
|
||||
private boolean detachedSignature = false;
|
||||
private final Set<PGPSecretKey> signingKeys = new HashSet<>();
|
||||
private SecretKeyRingProtector signingKeysDecryptor;
|
||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128;
|
||||
|
@ -142,8 +148,8 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SignWith doNotEncrypt() {
|
||||
return new SignWithImpl();
|
||||
public DetachedSign doNotEncrypt() {
|
||||
return new DetachedSignImpl();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,25 +222,54 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SignWith usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
@Nonnull HashAlgorithm hashAlgorithm,
|
||||
@Nonnull CompressionAlgorithm compressionAlgorithm) {
|
||||
public DetachedSign usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
@Nonnull HashAlgorithm hashAlgorithm,
|
||||
@Nonnull CompressionAlgorithm compressionAlgorithm) {
|
||||
|
||||
EncryptionBuilder.this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||
EncryptionBuilder.this.hashAlgorithm = hashAlgorithm;
|
||||
EncryptionBuilder.this.compressionAlgorithm = compressionAlgorithm;
|
||||
|
||||
return new SignWithImpl();
|
||||
return new DetachedSignImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignWith usingSecureAlgorithms() {
|
||||
public DetachedSign usingSecureAlgorithms() {
|
||||
EncryptionBuilder.this.symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256;
|
||||
EncryptionBuilder.this.hashAlgorithm = HashAlgorithm.SHA512;
|
||||
EncryptionBuilder.this.compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||
|
||||
return new DetachedSignImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class DetachedSignImpl implements DetachedSign {
|
||||
|
||||
@Override
|
||||
public SignWith createDetachedSignature() {
|
||||
EncryptionBuilder.this.detachedSignature = true;
|
||||
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 {
|
||||
|
@ -296,11 +331,6 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
}
|
||||
return new ArmorImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Armor doNotSign() {
|
||||
return new ArmorImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class ArmorImpl implements Armor {
|
||||
|
@ -319,14 +349,16 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
|
||||
private EncryptionStream build() throws IOException, PGPException {
|
||||
|
||||
Set<PGPPrivateKey> privateKeys = new HashSet<>();
|
||||
Map<OpenPgpV4Fingerprint, PGPPrivateKey> privateKeys = new ConcurrentHashMap<>();
|
||||
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(
|
||||
EncryptionBuilder.this.outputStream,
|
||||
EncryptionBuilder.this.encryptionKeys,
|
||||
EncryptionBuilder.this.detachedSignature,
|
||||
privateKeys,
|
||||
EncryptionBuilder.this.symmetricKeyAlgorithm,
|
||||
EncryptionBuilder.this.hashAlgorithm,
|
||||
|
|
|
@ -50,7 +50,7 @@ public interface EncryptionBuilderInterface {
|
|||
<O> WithAlgorithms toRecipients(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
||||
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
||||
|
||||
SignWith doNotEncrypt();
|
||||
DetachedSign doNotEncrypt();
|
||||
|
||||
}
|
||||
|
||||
|
@ -65,11 +65,18 @@ public interface EncryptionBuilderInterface {
|
|||
<O> WithAlgorithms andToSelf(@Nonnull PublicKeyRingSelectionStrategy<O> selectionStrategy,
|
||||
@Nonnull MultiMap<O, PGPPublicKeyRingCollection> keys);
|
||||
|
||||
SignWith usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
DetachedSign usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
@Nonnull HashAlgorithm hashAlgorithm,
|
||||
@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)
|
||||
throws SecretKeyNotFoundException;
|
||||
|
||||
Armor doNotSign();
|
||||
|
||||
}
|
||||
|
||||
interface Armor {
|
||||
|
|
|
@ -21,8 +21,11 @@ import java.io.OutputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -35,15 +38,19 @@ import org.bouncycastle.openpgp.PGPLiteralData;
|
|||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
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.BcPGPDataEncryptorBuilder;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.decryption_verification.DetachedSignature;
|
||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
/**
|
||||
* 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 CompressionAlgorithm compressionAlgorithm;
|
||||
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 OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||
|
||||
private List<PGPSignatureGenerator> signatureGenerators = new ArrayList<>();
|
||||
private Map<OpenPgpV4Fingerprint, PGPSignatureGenerator> signatureGenerators = new ConcurrentHashMap<>();
|
||||
private boolean closed = false;
|
||||
|
||||
OutputStream outermostStream = null;
|
||||
|
@ -81,7 +89,8 @@ public final class EncryptionStream extends OutputStream {
|
|||
|
||||
EncryptionStream(@Nonnull OutputStream targetOutputStream,
|
||||
@Nonnull Set<PGPPublicKey> encryptionKeys,
|
||||
@Nonnull Set<PGPPrivateKey> signingKeys,
|
||||
boolean detachedSignature,
|
||||
@Nonnull Map<OpenPgpV4Fingerprint, PGPPrivateKey> signingKeys,
|
||||
@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||
@Nonnull HashAlgorithm hashAlgorithm,
|
||||
@Nonnull CompressionAlgorithm compressionAlgorithm,
|
||||
|
@ -92,7 +101,8 @@ public final class EncryptionStream extends OutputStream {
|
|||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.compressionAlgorithm = compressionAlgorithm;
|
||||
this.encryptionKeys = Collections.unmodifiableSet(encryptionKeys);
|
||||
this.signingKeys = Collections.unmodifiableSet(signingKeys);
|
||||
this.detachedSignature = detachedSignature;
|
||||
this.signingKeys = Collections.unmodifiableMap(signingKeys);
|
||||
this.asciiArmor = asciiArmor;
|
||||
|
||||
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");
|
||||
for (PGPPrivateKey privateKey : signingKeys) {
|
||||
LOGGER.log(LEVEL, "Sign using key " + Long.toHexString(privateKey.getKeyID()));
|
||||
for (OpenPgpV4Fingerprint fingerprint : signingKeys.keySet()) {
|
||||
PGPPrivateKey privateKey = signingKeys.get(fingerprint);
|
||||
LOGGER.log(LEVEL, "Sign using key " + fingerprint);
|
||||
BcPGPContentSignerBuilder contentSignerBuilder = new BcPGPContentSignerBuilder(
|
||||
privateKey.getPublicKeyPacket().getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||
|
||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||
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 {
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||
signatureGenerator.generateOnePassVersion(false).encode(basicCompressionStream);
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +197,7 @@ public final class EncryptionStream extends OutputStream {
|
|||
public void write(int data) throws IOException {
|
||||
literalDataStream.write(data);
|
||||
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||
byte asByte = (byte) (data & 0xff);
|
||||
signatureGenerator.update(asByte);
|
||||
}
|
||||
|
@ -201,7 +212,7 @@ public final class EncryptionStream extends OutputStream {
|
|||
@Override
|
||||
public void write(byte[] buffer, int off, int len) throws IOException {
|
||||
literalDataStream.write(buffer, 0, len);
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators.values()) {
|
||||
signatureGenerator.update(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
|
@ -242,12 +253,14 @@ public final class EncryptionStream extends OutputStream {
|
|||
}
|
||||
|
||||
private void writeSignatures() throws IOException {
|
||||
for (PGPSignatureGenerator signatureGenerator : signatureGenerators) {
|
||||
for (OpenPgpV4Fingerprint fingerprint : signatureGenerators.keySet()) {
|
||||
PGPSignatureGenerator signatureGenerator = signatureGenerators.get(fingerprint);
|
||||
try {
|
||||
PGPSignature signature = signatureGenerator.generate();
|
||||
signature.encode(basicCompressionStream);
|
||||
resultBuilder.addSignature(signature);
|
||||
resultBuilder.addUnverifiedSignatureKeyId(signature.getKeyID());
|
||||
if (!detachedSignature) {
|
||||
signature.encode(basicCompressionStream);
|
||||
}
|
||||
resultBuilder.addDetachedSignature(new DetachedSignature(signature, fingerprint));
|
||||
} catch (PGPException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ public class DecryptAndVerifyMessageTest {
|
|||
assertTrue(metadata.isVerified());
|
||||
assertEquals(CompressionAlgorithm.ZLIB, metadata.getCompressionAlgorithm());
|
||||
assertEquals(SymmetricKeyAlgorithm.AES_256, metadata.getSymmetricKeyAlgorithm());
|
||||
assertEquals(1, metadata.getSignatureKeyIDs().size());
|
||||
//assertEquals(1, metadata.getSignatureKeyIDs().size());
|
||||
assertEquals(1, metadata.getVerifiedSignatureKeyFingerprints().size());
|
||||
assertTrue(metadata.containsVerifiedSignatureFrom(TestKeys.JULIET_FINGERPRINT));
|
||||
assertEquals(TestKeys.JULIET_FINGERPRINT, metadata.getDecryptionFingerprint());
|
||||
|
|
|
@ -26,11 +26,15 @@ import java.io.IOException;
|
|||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
|
@ -38,6 +42,7 @@ import org.pgpainless.algorithm.KeyFlag;
|
|||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.TestKeys;
|
||||
import org.pgpainless.key.collection.PGPKeyRing;
|
||||
import org.pgpainless.key.generation.KeySpec;
|
||||
|
@ -145,9 +150,9 @@ public class EncryptDecryptTest {
|
|||
|
||||
OpenPgpMetadata encryptionResult = encryptor.getResult();
|
||||
|
||||
assertFalse(encryptionResult.getSignatureKeyIDs().isEmpty());
|
||||
for (long keyId : encryptionResult.getSignatureKeyIDs()) {
|
||||
assertTrue(BCUtil.keyRingContainsKeyWithId(senderPub, keyId));
|
||||
assertFalse(encryptionResult.getSignatures().isEmpty());
|
||||
for (OpenPgpV4Fingerprint fingerprint : encryptionResult.getVerifiedSignatures().keySet()) {
|
||||
assertTrue(BCUtil.keyRingContainsKeyWithId(senderPub, fingerprint.getKeyId()));
|
||||
}
|
||||
|
||||
assertFalse(encryptionResult.getRecipientKeyIds().isEmpty());
|
||||
|
@ -180,4 +185,44 @@ public class EncryptDecryptTest {
|
|||
assertTrue(result.isEncrypted());
|
||||
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