mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-22 19:08:00 +01:00
Fix decryption, WiP
This commit is contained in:
parent
afce16e51e
commit
5d8effb0c5
10 changed files with 883 additions and 3 deletions
171
src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java
Normal file
171
src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.key.generation.type.length.RsaLength;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
|
||||||
|
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
||||||
|
import org.bouncycastle.util.encoders.Base64;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
|
||||||
|
IOException {
|
||||||
|
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
|
PGPSecretKeyRing a = PGPainless.generateKeyRing().simpleRsaKeyRing("a@b.c", RsaLength._2048);
|
||||||
|
PGPSecretKeyRing b = PGPainless.generateKeyRing().simpleRsaKeyRing("b@c.d", RsaLength._2048);
|
||||||
|
|
||||||
|
SecretKeyRingDecryptor secretKeyRingDecryptor = new SecretKeyRingDecryptor() {
|
||||||
|
@Override
|
||||||
|
public PBESecretKeyDecryptor getDecryptor(Long keyId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PBESecretKeyEncryptor getEncryptor(Long keyId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] m = "Dies ist ein verschlüsselter Text.".getBytes();
|
||||||
|
ByteArrayInputStream fromPlain = new ByteArrayInputStream(m);
|
||||||
|
ByteArrayOutputStream toEncrypted = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OutputStream encryptor = PGPainless.createEncryptor().onOutputStream(toEncrypted)
|
||||||
|
.toRecipient(b.getPublicKey())
|
||||||
|
.usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED)
|
||||||
|
.signWith(a.getSecretKey(), secretKeyRingDecryptor)
|
||||||
|
.asciiArmor();
|
||||||
|
|
||||||
|
Streams.pipeAll(fromPlain, encryptor);
|
||||||
|
fromPlain.close();
|
||||||
|
encryptor.close();
|
||||||
|
|
||||||
|
System.out.println(new String(toEncrypted.toByteArray()));
|
||||||
|
|
||||||
|
ByteArrayInputStream fromEncrypted = new ByteArrayInputStream(toEncrypted.toByteArray());
|
||||||
|
ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
PainlessResult.ResultAndInputStream resultAndInputStream = PGPainless.createDecryptor()
|
||||||
|
.onInputStream(fromEncrypted)
|
||||||
|
.decryptWith(new PGPSecretKeyRingCollection(Collections.singleton(b)), secretKeyRingDecryptor)
|
||||||
|
.verifyWith(Collections.singleton(a.getPublicKey().getKeyID()),
|
||||||
|
Collections.singleton(new PGPPublicKeyRing(a.getPublicKey().getEncoded(), new BcKeyFingerprintCalculator())))
|
||||||
|
.ignoreMissingPublicKeys()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
InputStream decryptor = resultAndInputStream.getInputStream();
|
||||||
|
|
||||||
|
Streams.pipeAll(decryptor, toPlain);
|
||||||
|
decryptor.close();
|
||||||
|
toPlain.close();
|
||||||
|
|
||||||
|
PainlessResult result = resultAndInputStream.getResult();
|
||||||
|
|
||||||
|
System.out.println(b.getPublicKey().getKeyID() + " " + result.getDecryptionKeyId());
|
||||||
|
System.out.println(new String(toPlain.toByteArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void gpg(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingDecryptor secretKeyRingDecryptor)
|
||||||
|
throws IOException, PGPException {
|
||||||
|
String gpg = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"\n" +
|
||||||
|
"hQGMAwAAAAAAAAAAAQv+JyovfiPxDiLe9XlgQAG6zD+YdRtZRuUJD+A+ZX4Sn0w0\n" +
|
||||||
|
"2Dl9Ehf7lKjIo0cIfVOUrgITnWIRWAyfrk5KiXdXcZ6dXxz/YJFnLSlgqUwq7GWi\n" +
|
||||||
|
"NYf3Uqg+/8f3Ucl0x6sr1oddwB9OI7zRJwDqEzTORjLBu1vtDlFPMPWwAeqDtZgz\n" +
|
||||||
|
"ikT6vSFfhVjVbgx4mztw7hatWNjXzNkl9+lojzo9IyiA+3SBsRe/2My3ZBjPx97f\n" +
|
||||||
|
"3YGMCvbggdX3C/MRV2iek2pFX7YTKasFeEy5Y1c09upqaEIpaJq8vi1Fu44dv0Rt\n" +
|
||||||
|
"gv4sdljaJXsFn9aoVFrp/xU4SyPiC1Z/KjqE3Zfyh+OMWKoWmtYH07/g8IGkkBCh\n" +
|
||||||
|
"xuDiyy813WS3xtCyX405Vd+rxYC3y1h1FtthdO/AIrYSWj6qI6hyK2tyYmwsg+oY\n" +
|
||||||
|
"1oaXhcTbWXsBO89v0YtmVK1bPVXq8ao/DQvrs84JsYsKzXA17gKyBLBUoTNn8h8A\n" +
|
||||||
|
"AXg455AN8iHi2u5pAKr7hQIMAwAAAAAAAAAAAQ/7Bg8SGEfMPmtDy/BazrYWaXvX\n" +
|
||||||
|
"1+WWRNM630ULPEq7LG4BKJJOOrhk0kNkjIsaXhgqn9bt3YxxuE1eOQksjn0sNOD7\n" +
|
||||||
|
"3NAOicHzQ7xOarvN9OUSGirc+EIn4ETRGKYF1TXHBSYnBeb+DLCbRZkBZhRrA5Lc\n" +
|
||||||
|
"z08kWrGRfq5Bz6eMatBTO1L8XTIxHPgc9/LNv7OqcIfT0udjOQMkA7oxCz5mLl2b\n" +
|
||||||
|
"dApsDEFNKNaGgRzSf2rDqw0SGkDxYsXI6IYrVSEm6uDt+ScybS0KkcEgg+I9l91n\n" +
|
||||||
|
"XgqQQaXpYnHgETqKYfcUOk5iEND5Lvik/XhHNViaL3CdOkxFLTa0wfy0y0IsV2Y3\n" +
|
||||||
|
"xGkMOWdDjXlY8UWRgoK61M91phgZ48zfSoVvXNDrjOJzm1jn8CFFFov4Gse7CtlM\n" +
|
||||||
|
"A+3ntVdjL94jkp+2mU3e9kzCOG+ChylLuqlGTvavbHt/rzuZooi/6g1VHy1r+v9I\n" +
|
||||||
|
"rWKX6q55H8JzZXZOrfED39QocK9b1BjtEca/Qnqw82+IVY/CufBmnmbOWUkHq1zP\n" +
|
||||||
|
"6nj840HxH1zV5vHf8vlXxV7/iBesAF94dLT/Hp0E7+Ilyp/pQaQjMS2RLycMUJQJ\n" +
|
||||||
|
"pQey81gpuOWD6YIbvgnrMBMrJLyJSk3r3sMdJ3DCPxHC+OyvHxddA5TdL2e4aP3L\n" +
|
||||||
|
"OzKql59v1w+9Doe3LEPSPgEVAdUUg0nEl5lg9LLqaepaYp8NfsEC1Rnk/MLxciJu\n" +
|
||||||
|
"9oNjqPqQxKTv4aQO/Qb8gHFb3O34OnNKz+CzrX5Q\n" +
|
||||||
|
"=sVVl\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(gpg.getBytes());
|
||||||
|
|
||||||
|
InputStream decryptor = PGPainless.createDecryptor().onInputStream(inputStream)
|
||||||
|
.decryptWith(new PGPSecretKeyRingCollection(Collections.singleton(b)), secretKeyRingDecryptor)
|
||||||
|
.doNotVerify()
|
||||||
|
.build()
|
||||||
|
.getInputStream();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void symm(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingDecryptor secretKeyRingDecryptor)
|
||||||
|
throws IOException, PGPException {
|
||||||
|
byte[] bytes = "Diese Nachricht ist streng geheim!!!".getBytes(Charset.forName("UTF-8"));
|
||||||
|
ByteArrayInputStream fromPlain = new ByteArrayInputStream(bytes);
|
||||||
|
ByteArrayOutputStream toEncrypted = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OutputStream encryptor = PGPainless.createEncryptor()
|
||||||
|
.onOutputStream(toEncrypted)
|
||||||
|
.toRecipient(b.getPublicKey())
|
||||||
|
.usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED)
|
||||||
|
.signWith(a.getSecretKey(), secretKeyRingDecryptor)
|
||||||
|
.noArmor();
|
||||||
|
|
||||||
|
Streams.pipeAll(fromPlain, encryptor);
|
||||||
|
encryptor.close();
|
||||||
|
|
||||||
|
System.out.println(new String(toEncrypted.toByteArray(), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
ByteArrayInputStream fromEncrypted = new ByteArrayInputStream(toEncrypted.toByteArray());
|
||||||
|
ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
PainlessResult.ResultAndInputStream resultAndInputStream = PGPainless.createDecryptor()
|
||||||
|
.onInputStream(fromEncrypted)
|
||||||
|
.decryptWith(new PGPSecretKeyRingCollection(Collections.singleton(b)), secretKeyRingDecryptor)
|
||||||
|
.verifyWith(Collections.singleton(a.getPublicKey().getKeyID()),
|
||||||
|
Collections.singleton(new PGPPublicKeyRing(a.getPublicKey().getEncoded(), new BcKeyFingerprintCalculator())))
|
||||||
|
.ignoreMissingPublicKeys()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
InputStream decryptor = resultAndInputStream.getInputStream();
|
||||||
|
|
||||||
|
Streams.pipeAll(decryptor, toPlain);
|
||||||
|
decryptor.close();
|
||||||
|
|
||||||
|
PainlessResult result = resultAndInputStream.getResult();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
|
|
||||||
|
public class PGPv4Fingerprint {
|
||||||
|
|
||||||
|
private final byte[] fingerprintBytes;
|
||||||
|
|
||||||
|
public PGPv4Fingerprint(PGPPublicKey publicKey) {
|
||||||
|
if (publicKey.getVersion() != 4) {
|
||||||
|
throw new IllegalArgumentException("PublicKey is not a OpenPGP v4 Public Key.");
|
||||||
|
}
|
||||||
|
this.fingerprintBytes = publicKey.getFingerprint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPv4Fingerprint(PGPSecretKey secretKey) {
|
||||||
|
this(secretKey.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(o instanceof PGPv4Fingerprint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.equals(fingerprintBytes, ((PGPv4Fingerprint) o).fingerprintBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(fingerprintBytes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,139 @@
|
||||||
package de.vanitasvitae.crypto.pgpainless;
|
package de.vanitasvitae.crypto.pgpainless;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
|
||||||
public class PainlessResult {
|
public class PainlessResult {
|
||||||
Set<Long> signingKeys;
|
|
||||||
Long decryptingKey;
|
private final Set<Long> recipientKeyIds;
|
||||||
|
private final Long decryptionKeyId;
|
||||||
|
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
||||||
|
private final CompressionAlgorithm compressionAlgorithm;
|
||||||
|
private final boolean integrityProtected;
|
||||||
|
private final Set<Long> signatureKeyIds;
|
||||||
|
private final Set<Long> verifiedSignatureKeyIds;
|
||||||
|
|
||||||
|
public PainlessResult(Set<Long> recipientKeyIds,
|
||||||
|
Long decryptionKeyId,
|
||||||
|
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
|
||||||
|
CompressionAlgorithm algorithm,
|
||||||
|
boolean integrityProtected,
|
||||||
|
Set<Long> signatureKeyIds,
|
||||||
|
Set<Long> verifiedSignatureKeyIds) {
|
||||||
|
|
||||||
|
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
||||||
|
this.decryptionKeyId = decryptionKeyId;
|
||||||
|
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||||
|
this.compressionAlgorithm = algorithm;
|
||||||
|
this.integrityProtected = integrityProtected;
|
||||||
|
this.signatureKeyIds = Collections.unmodifiableSet(signatureKeyIds);
|
||||||
|
this.verifiedSignatureKeyIds = Collections.unmodifiableSet(verifiedSignatureKeyIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getRecipientKeyIds() {
|
||||||
|
return recipientKeyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDecryptionKeyId() {
|
||||||
|
return decryptionKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() {
|
||||||
|
return symmetricKeyAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompressionAlgorithm getCompressionAlgorithm() {
|
||||||
|
return compressionAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIntegrityProtected() {
|
||||||
|
return integrityProtected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getSignatureKeyIds() {
|
||||||
|
return signatureKeyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getVerifiedSignatureKeyIds() {
|
||||||
|
return verifiedSignatureKeyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder getBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private final Set<Long> recipientKeyIds = new HashSet<>();
|
||||||
|
private Long decryptionKeyId;
|
||||||
|
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
||||||
|
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||||
|
private boolean integrityProtected = false;
|
||||||
|
private final Set<Long> signatureKeyIds = new HashSet<>();
|
||||||
|
private final Set<Long> verifiedSignatureKeyIds = new HashSet<>();
|
||||||
|
|
||||||
|
public Builder addRecipientKeyId(long id) {
|
||||||
|
this.recipientKeyIds.add(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDecryptionKeyId(long id) {
|
||||||
|
this.decryptionKeyId = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCompressionAlgorithm(CompressionAlgorithm algorithm) {
|
||||||
|
this.compressionAlgorithm = algorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addSignatureKeyId(long id) {
|
||||||
|
this.signatureKeyIds.add(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addVerifiedSignatureKeyId(long id) {
|
||||||
|
this.verifiedSignatureKeyIds.add(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm symmetricKeyAlgorithm) {
|
||||||
|
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setIntegrityProtected(boolean integrityProtected) {
|
||||||
|
this.integrityProtected = integrityProtected;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PainlessResult build() {
|
||||||
|
return new PainlessResult(recipientKeyIds, decryptionKeyId, symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected, signatureKeyIds, verifiedSignatureKeyIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResultAndInputStream {
|
||||||
|
private final PainlessResult.Builder resultBuilder;
|
||||||
|
private final PainlessStream.In inputStream;
|
||||||
|
|
||||||
|
public ResultAndInputStream(PainlessResult.Builder resultBuilder, PainlessStream.In inputStream) {
|
||||||
|
this.resultBuilder = resultBuilder;
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PainlessResult getResult() {
|
||||||
|
if (!inputStream.isClosed()) {
|
||||||
|
throw new IllegalStateException("InputStream must be closed before the PainlessResult can be accessed.");
|
||||||
|
}
|
||||||
|
return resultBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PainlessStream.In getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public interface PainlessStream {
|
||||||
|
|
||||||
|
boolean isClosed();
|
||||||
|
|
||||||
|
class In extends InputStream implements PainlessStream {
|
||||||
|
private final InputStream inputStream;
|
||||||
|
|
||||||
|
private boolean isClosed = false;
|
||||||
|
|
||||||
|
public In(InputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return inputStream.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
inputStream.close();
|
||||||
|
isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return isClosed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Out extends OutputStream implements PainlessStream {
|
||||||
|
|
||||||
|
private final OutputStream outputStream;
|
||||||
|
private boolean isClosed = false;
|
||||||
|
|
||||||
|
public Out(OutputStream outputStream) {
|
||||||
|
this.outputStream = outputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return isClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int i) throws IOException {
|
||||||
|
outputStream.write(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
outputStream.close();
|
||||||
|
this.isClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,97 @@
|
||||||
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
public class DecryptionBuilder {
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.PainlessResult;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
|
||||||
|
public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
|
|
||||||
|
private InputStream inputStream;
|
||||||
|
private PGPSecretKeyRingCollection decryptionKeys;
|
||||||
|
private SecretKeyRingDecryptor decryptionKeyDecryptor;
|
||||||
|
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
||||||
|
private Set<Long> trustedKeyIds = new HashSet<>();
|
||||||
|
private MissingPublicKeyCallback missingPublicKeyCallback = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DecryptWith onInputStream(InputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
return new DecryptWithImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DecryptWithImpl implements DecryptWith {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor) {
|
||||||
|
DecryptionBuilder.this.decryptionKeys = secretKeyRings;
|
||||||
|
DecryptionBuilder.this.decryptionKeyDecryptor = decryptor;
|
||||||
|
return new VerifyWithImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VerifyWith doNotDecrypt() {
|
||||||
|
DecryptionBuilder.this.decryptionKeys = null;
|
||||||
|
DecryptionBuilder.this.decryptionKeyDecryptor = null;
|
||||||
|
return new VerifyWithImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VerifyWithImpl implements VerifyWith {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MissingPublicKeyFeedback verifyWith(Set<Long> trustedKeyIds,
|
||||||
|
PGPPublicKeyRingCollection publicKeyRingCollection) {
|
||||||
|
Set<PGPPublicKeyRing> publicKeyRings = new HashSet<>();
|
||||||
|
for (Iterator<PGPPublicKeyRing> i = publicKeyRingCollection.getKeyRings(); i.hasNext(); ) {
|
||||||
|
publicKeyRings.add(i.next());
|
||||||
|
}
|
||||||
|
return verifyWith(trustedKeyIds, publicKeyRings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MissingPublicKeyFeedback verifyWith(Set<Long> trustedKeyIds, Set<PGPPublicKeyRing> publicKeyRings) {
|
||||||
|
DecryptionBuilder.this.verificationKeys = publicKeyRings;
|
||||||
|
DecryptionBuilder.this.trustedKeyIds = trustedKeyIds;
|
||||||
|
return new MissingPublicKeyFeedbackImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Build doNotVerify() {
|
||||||
|
DecryptionBuilder.this.verificationKeys = null;
|
||||||
|
DecryptionBuilder.this.trustedKeyIds = null;
|
||||||
|
return new BuildImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MissingPublicKeyFeedbackImpl implements MissingPublicKeyFeedback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Build handleMissingPublicKeysWith(MissingPublicKeyCallback callback) {
|
||||||
|
DecryptionBuilder.this.missingPublicKeyCallback = callback;
|
||||||
|
return new BuildImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Build ignoreMissingPublicKeys() {
|
||||||
|
return new BuildImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildImpl implements Build {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PainlessResult.ResultAndInputStream build() throws IOException, PGPException {
|
||||||
|
return InputStreamFactory.create(inputStream,
|
||||||
|
decryptionKeys, decryptionKeyDecryptor, verificationKeys, trustedKeyIds, missingPublicKeyCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.PainlessResult;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
|
||||||
|
public interface DecryptionBuilderInterface {
|
||||||
|
|
||||||
|
DecryptWith onInputStream(InputStream inputStream);
|
||||||
|
|
||||||
|
interface DecryptWith {
|
||||||
|
|
||||||
|
VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor);
|
||||||
|
|
||||||
|
VerifyWith doNotDecrypt();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VerifyWith {
|
||||||
|
|
||||||
|
MissingPublicKeyFeedback verifyWith(Set<Long> trustedFingerprints, PGPPublicKeyRingCollection publicKeyRings);
|
||||||
|
|
||||||
|
MissingPublicKeyFeedback verifyWith(Set<Long> trustedFingerprints, Set<PGPPublicKeyRing> publicKeyRings);
|
||||||
|
|
||||||
|
Build doNotVerify();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MissingPublicKeyFeedback {
|
||||||
|
|
||||||
|
Build handleMissingPublicKeysWith(MissingPublicKeyCallback callback);
|
||||||
|
|
||||||
|
Build ignoreMissingPublicKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Build {
|
||||||
|
|
||||||
|
PainlessResult.ResultAndInputStream build() throws IOException, PGPException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.PainlessResult;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.PainlessStream;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||||
|
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
|
import org.bouncycastle.openpgp.PGPOnePassSignature;
|
||||||
|
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
|
||||||
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
|
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
|
||||||
|
|
||||||
|
public class InputStreamFactory {
|
||||||
|
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
private final PGPSecretKeyRingCollection decryptionKeys;
|
||||||
|
private final SecretKeyRingDecryptor decryptionKeyDecryptor;
|
||||||
|
private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
|
||||||
|
private final Set<Long> trustedKeyIds = new HashSet<>();
|
||||||
|
private final MissingPublicKeyCallback missingPublicKeyCallback;
|
||||||
|
|
||||||
|
private final PainlessResult.Builder resultBuilder = PainlessResult.getBuilder();
|
||||||
|
private final PGPContentVerifierBuilderProvider verifierBuilderProvider = new BcPGPContentVerifierBuilderProvider();
|
||||||
|
private final Map<Long, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
|
||||||
|
|
||||||
|
private InputStreamFactory(PGPSecretKeyRingCollection decryptionKeys,
|
||||||
|
SecretKeyRingDecryptor decryptor,
|
||||||
|
Set<PGPPublicKeyRing> verificationKeys,
|
||||||
|
Set<Long> trustedKeyIds,
|
||||||
|
MissingPublicKeyCallback missingPublicKeyCallback)
|
||||||
|
throws IOException {
|
||||||
|
this.decryptionKeys = decryptionKeys;
|
||||||
|
this.decryptionKeyDecryptor = decryptor;
|
||||||
|
this.verificationKeys.addAll(verificationKeys != null ? verificationKeys : Collections.emptyList());
|
||||||
|
this.trustedKeyIds.addAll(trustedKeyIds != null ? trustedKeyIds : Collections.emptyList());
|
||||||
|
this.missingPublicKeyCallback = missingPublicKeyCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PainlessResult.ResultAndInputStream create(InputStream inputStream,
|
||||||
|
PGPSecretKeyRingCollection decryptionKeys,
|
||||||
|
SecretKeyRingDecryptor decryptor,
|
||||||
|
Set<PGPPublicKeyRing> verificationKeys,
|
||||||
|
Set<Long> trustedKeyIds,
|
||||||
|
MissingPublicKeyCallback missingPublicKeyCallback)
|
||||||
|
throws IOException, PGPException {
|
||||||
|
|
||||||
|
InputStreamFactory factory = new InputStreamFactory(decryptionKeys,
|
||||||
|
decryptor,
|
||||||
|
verificationKeys,
|
||||||
|
trustedKeyIds,
|
||||||
|
missingPublicKeyCallback);
|
||||||
|
|
||||||
|
PGPObjectFactory objectFactory = new PGPObjectFactory(
|
||||||
|
PGPUtil.getDecoderStream(inputStream), new BcKeyFingerprintCalculator());
|
||||||
|
|
||||||
|
return new PainlessResult.ResultAndInputStream(
|
||||||
|
factory.resultBuilder,
|
||||||
|
new PainlessStream.In(factory.wrap(objectFactory)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream wrap(PGPObjectFactory objectFactory) throws IOException, PGPException {
|
||||||
|
KeyFingerPrintCalculator fingerCalc = new BcKeyFingerprintCalculator();
|
||||||
|
|
||||||
|
Object pgpObj = null;
|
||||||
|
while ((pgpObj = objectFactory.nextObject()) != null) {
|
||||||
|
|
||||||
|
if (pgpObj instanceof PGPEncryptedDataList) {
|
||||||
|
PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpObj;
|
||||||
|
InputStream nextStream = decrypt(encDataList);
|
||||||
|
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
|
||||||
|
return wrap(objectFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgpObj instanceof PGPCompressedData) {
|
||||||
|
PGPCompressedData compressedData = (PGPCompressedData) pgpObj;
|
||||||
|
InputStream nextStream = compressedData.getDataStream();
|
||||||
|
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.fromId(compressedData.getAlgorithm()));
|
||||||
|
objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(nextStream), fingerCalc);
|
||||||
|
return wrap(objectFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgpObj instanceof PGPOnePassSignatureList) {
|
||||||
|
PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) pgpObj;
|
||||||
|
verify(onePassSignatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgpObj instanceof PGPLiteralData) {
|
||||||
|
PGPLiteralData literalData = (PGPLiteralData) pgpObj;
|
||||||
|
InputStream literalDataInputStream = literalData.getInputStream();
|
||||||
|
|
||||||
|
if (verifiableOnePassSignatures.isEmpty()) {
|
||||||
|
return literalDataInputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SignatureVerifyingInputStream(literalDataInputStream,
|
||||||
|
objectFactory, verifiableOnePassSignatures, resultBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PGPException("No Literal Data Packet found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream decrypt(PGPEncryptedDataList encryptedDataList)
|
||||||
|
throws PGPException {
|
||||||
|
Iterator<?> iterator = encryptedDataList.getEncryptedDataObjects();
|
||||||
|
if (!iterator.hasNext()) {
|
||||||
|
throw new PGPException("Decryption failed - No encrypted data found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPPrivateKey decryptionKey = null;
|
||||||
|
PGPPublicKeyEncryptedData encryptedSessionKey = null;
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
encryptedSessionKey = (PGPPublicKeyEncryptedData) iterator.next();
|
||||||
|
long keyId = encryptedSessionKey.getKeyID();
|
||||||
|
|
||||||
|
resultBuilder.addRecipientKeyId(keyId);
|
||||||
|
|
||||||
|
if (decryptionKey != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSecretKey secretKey = decryptionKeys.getSecretKey(keyId);
|
||||||
|
if (secretKey != null) {
|
||||||
|
decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId));
|
||||||
|
resultBuilder.setDecryptionKeyId(keyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decryptionKey == null) {
|
||||||
|
throw new PGPException("Decryption failed - No suitable decryption key found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicKeyDataDecryptorFactory keyDecryptor = new BcPublicKeyDataDecryptorFactory(decryptionKey);
|
||||||
|
resultBuilder.setSymmetricKeyAlgorithm(
|
||||||
|
SymmetricKeyAlgorithm.forId(encryptedSessionKey.getSymmetricAlgorithm(keyDecryptor)));
|
||||||
|
resultBuilder.setIntegrityProtected(encryptedSessionKey.isIntegrityProtected());
|
||||||
|
|
||||||
|
InputStream decryptionStream = encryptedSessionKey.getDataStream(keyDecryptor);
|
||||||
|
|
||||||
|
return decryptionStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verify(PGPOnePassSignatureList onePassSignatureList) throws PGPException {
|
||||||
|
Iterator<PGPOnePassSignature> iterator = onePassSignatureList.iterator();
|
||||||
|
if (!iterator.hasNext()) {
|
||||||
|
throw new PGPException("Verification failed - No OnePassSignatures found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
PGPOnePassSignature signature = iterator.next();
|
||||||
|
long keyId = signature.getKeyID();
|
||||||
|
resultBuilder.addSignatureKeyId(keyId);
|
||||||
|
|
||||||
|
// Find public key
|
||||||
|
PGPPublicKey verificationKey = null;
|
||||||
|
for (PGPPublicKeyRing publicKeyRing : verificationKeys) {
|
||||||
|
verificationKey = publicKeyRing.getPublicKey(signature.getKeyID());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verificationKey != null) {
|
||||||
|
signature.init(verifierBuilderProvider, verificationKey);
|
||||||
|
verifiableOnePassSignatures.put(signature.getKeyID(), signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
public interface MissingPublicKeyCallback {
|
||||||
|
|
||||||
|
void onMissingPublicKeyEncountered(Long keyId);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import de.vanitasvitae.crypto.pgpainless.PainlessResult;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class SignatureVerifyingInputStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private final PGPObjectFactory objectFactory;
|
||||||
|
private final Map<Long, PGPOnePassSignature> onePassSignatures;
|
||||||
|
private final PainlessResult.Builder resultBuilder;
|
||||||
|
|
||||||
|
protected SignatureVerifyingInputStream(InputStream inputStream,
|
||||||
|
PGPObjectFactory objectFactory,
|
||||||
|
Map<Long, PGPOnePassSignature> onePassSignatures,
|
||||||
|
PainlessResult.Builder resultBuilder) {
|
||||||
|
super(inputStream);
|
||||||
|
this.objectFactory = objectFactory;
|
||||||
|
this.resultBuilder = resultBuilder;
|
||||||
|
this.onePassSignatures = onePassSignatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOnePassSignatures(byte data) {
|
||||||
|
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
||||||
|
signature.update(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOnePassSignatures(byte[] b, int off, int len) {
|
||||||
|
for (PGPOnePassSignature signature : onePassSignatures.values()) {
|
||||||
|
signature.update(b, off, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOnePassSignatures() throws IOException {
|
||||||
|
if (onePassSignatures.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PGPSignatureList signatureList = null;
|
||||||
|
Iterator objectIterator = objectFactory.iterator();
|
||||||
|
while (objectIterator.hasNext() && signatureList == null) {
|
||||||
|
Object object = objectIterator.next();
|
||||||
|
if (object instanceof PGPSignatureList) {
|
||||||
|
signatureList = (PGPSignatureList) object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signatureList == null || signatureList.isEmpty()) {
|
||||||
|
throw new IOException("Verification failed - No Signatures found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPSignature signature : signatureList) {
|
||||||
|
PGPOnePassSignature onePassSignature = onePassSignatures.get(signature.getKeyID());
|
||||||
|
if (onePassSignature == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!onePassSignature.verify(signature)) {
|
||||||
|
throw new SignatureException("Bad Signature of key " + signature.getKeyID());
|
||||||
|
} else {
|
||||||
|
resultBuilder.addVerifiedSignatureKeyId(signature.getKeyID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PGPException | SignatureException e) {
|
||||||
|
throw new IOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
final int data = super.read();
|
||||||
|
final boolean endOfStream = data == -1;
|
||||||
|
if (endOfStream) {
|
||||||
|
validateOnePassSignatures();
|
||||||
|
} else {
|
||||||
|
updateOnePassSignatures((byte) data);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
int read = super.read(b, off, len);
|
||||||
|
|
||||||
|
final boolean endOfStream = read == -1;
|
||||||
|
if (endOfStream) {
|
||||||
|
validateOnePassSignatures();
|
||||||
|
} else {
|
||||||
|
updateOnePassSignatures(b, off, read);
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) {
|
||||||
|
throw new UnsupportedOperationException("skip() is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void mark(int readlimit) {
|
||||||
|
throw new UnsupportedOperationException("mark() not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() {
|
||||||
|
throw new UnsupportedOperationException("reset() is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package de.vanitasvitae.crypto.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
public class VerificationFeedbackCallback {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue