mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-25 12:27:58 +01:00
WIP: Implement custom PGPDecryptionStream
This commit is contained in:
parent
9fcb9782a3
commit
ad8bf4fcac
6 changed files with 654 additions and 0 deletions
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.PacketTags;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public enum OpenPgpPacket {
|
||||||
|
PKESK(PacketTags.PUBLIC_KEY_ENC_SESSION),
|
||||||
|
SIG(PacketTags.SIGNATURE),
|
||||||
|
SKESK(PacketTags.SYMMETRIC_KEY_ENC_SESSION),
|
||||||
|
OPS(PacketTags.ONE_PASS_SIGNATURE),
|
||||||
|
SK(PacketTags.SECRET_KEY),
|
||||||
|
PK(PacketTags.PUBLIC_KEY),
|
||||||
|
SSK(PacketTags.SECRET_SUBKEY),
|
||||||
|
COMP(PacketTags.COMPRESSED_DATA),
|
||||||
|
SED(PacketTags.SYMMETRIC_KEY_ENC),
|
||||||
|
MARKER(PacketTags.MARKER),
|
||||||
|
LIT(PacketTags.LITERAL_DATA),
|
||||||
|
TRUST(PacketTags.TRUST),
|
||||||
|
UID(PacketTags.USER_ID),
|
||||||
|
PSK(PacketTags.PUBLIC_SUBKEY),
|
||||||
|
UATTR(PacketTags.USER_ATTRIBUTE),
|
||||||
|
SEIPD(PacketTags.SYM_ENC_INTEGRITY_PRO),
|
||||||
|
MOD(PacketTags.MOD_DETECTION_CODE),
|
||||||
|
|
||||||
|
EXP_1(PacketTags.EXPERIMENTAL_1),
|
||||||
|
EXP_2(PacketTags.EXPERIMENTAL_2),
|
||||||
|
EXP_3(PacketTags.EXPERIMENTAL_3),
|
||||||
|
EXP_4(PacketTags.EXPERIMENTAL_4),
|
||||||
|
;
|
||||||
|
|
||||||
|
static final Map<Integer, OpenPgpPacket> MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (OpenPgpPacket p : OpenPgpPacket.values()) {
|
||||||
|
MAP.put(p.getTag(), p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int tag;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static OpenPgpPacket fromTag(int tag) {
|
||||||
|
return MAP.get(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static OpenPgpPacket requireFromTag(int tag) {
|
||||||
|
OpenPgpPacket p = fromTag(tag);
|
||||||
|
if (p == null) {
|
||||||
|
throw new NoSuchElementException("No OpenPGP packet known for tag " + tag);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenPgpPacket(int tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.BCPGInputStream;
|
||||||
|
import org.bouncycastle.bcpg.BCPGOutputStream;
|
||||||
|
import org.bouncycastle.bcpg.ModDetectionCodePacket;
|
||||||
|
import org.bouncycastle.bcpg.OnePassSignaturePacket;
|
||||||
|
import org.bouncycastle.bcpg.Packet;
|
||||||
|
import org.bouncycastle.bcpg.PacketTags;
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
|
||||||
|
import org.bouncycastle.bcpg.SignaturePacket;
|
||||||
|
import org.bouncycastle.bcpg.SymmetricEncDataPacket;
|
||||||
|
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
|
||||||
|
import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
|
||||||
|
import org.bouncycastle.bcpg.TrustPacket;
|
||||||
|
import org.bouncycastle.bcpg.UserAttributePacket;
|
||||||
|
import org.bouncycastle.bcpg.UserIDPacket;
|
||||||
|
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||||
|
import org.bouncycastle.openpgp.PGPEncryptedDataList;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
|
import org.pgpainless.algorithm.OpenPgpPacket;
|
||||||
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
||||||
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class PGPDecryptionStream extends InputStream {
|
||||||
|
|
||||||
|
PushdownAutomaton automaton = new PushdownAutomaton();
|
||||||
|
// nested streams, outermost at the bottom of the stack
|
||||||
|
Stack<Layer> packetLayers = new Stack<>();
|
||||||
|
|
||||||
|
public PGPDecryptionStream(InputStream inputStream) throws IOException, PGPException {
|
||||||
|
try {
|
||||||
|
packetLayers.push(Layer.initial(inputStream));
|
||||||
|
walkLayer();
|
||||||
|
} catch (MalformedOpenPgpMessageException e) {
|
||||||
|
throw e.toRuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void walkLayer() throws PGPException, IOException {
|
||||||
|
if (packetLayers.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layer layer = packetLayers.peek();
|
||||||
|
BCPGInputStream inputStream = (BCPGInputStream) layer.inputStream;
|
||||||
|
|
||||||
|
loop: while (true) {
|
||||||
|
if (inputStream.nextPacketTag() == -1) {
|
||||||
|
popLayer();
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
OpenPgpPacket tag = nextTagOrThrow(inputStream);
|
||||||
|
switch (tag) {
|
||||||
|
|
||||||
|
case PKESK:
|
||||||
|
PublicKeyEncSessionPacket pkeskPacket = (PublicKeyEncSessionPacket) inputStream.readPacket();
|
||||||
|
PGPEncryptedDataList encList = null;
|
||||||
|
break;
|
||||||
|
case SIG:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.Signatures);
|
||||||
|
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
BCPGOutputStream bcpgOut = new BCPGOutputStream(buf);
|
||||||
|
while (inputStream.nextPacketTag() == PacketTags.SIGNATURE || inputStream.nextPacketTag() == PacketTags.MARKER) {
|
||||||
|
Packet packet = inputStream.readPacket();
|
||||||
|
if (packet instanceof SignaturePacket) {
|
||||||
|
SignaturePacket sig = (SignaturePacket) packet;
|
||||||
|
sig.encode(bcpgOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PGPSignatureList signatures = (PGPSignatureList) ImplementationFactory.getInstance()
|
||||||
|
.getPGPObjectFactory(buf.toByteArray()).nextObject();
|
||||||
|
break;
|
||||||
|
case SKESK:
|
||||||
|
SymmetricKeyEncSessionPacket skeskPacket = (SymmetricKeyEncSessionPacket) inputStream.readPacket();
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPS:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.OnePassSignatures);
|
||||||
|
buf = new ByteArrayOutputStream();
|
||||||
|
bcpgOut = new BCPGOutputStream(buf);
|
||||||
|
while (inputStream.nextPacketTag() == PacketTags.ONE_PASS_SIGNATURE || inputStream.nextPacketTag() == PacketTags.MARKER) {
|
||||||
|
Packet packet = inputStream.readPacket();
|
||||||
|
if (packet instanceof OnePassSignaturePacket) {
|
||||||
|
OnePassSignaturePacket sig = (OnePassSignaturePacket) packet;
|
||||||
|
sig.encode(bcpgOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PGPOnePassSignatureList onePassSignatures = (PGPOnePassSignatureList) ImplementationFactory.getInstance()
|
||||||
|
.getPGPObjectFactory(buf.toByteArray()).nextObject();
|
||||||
|
break;
|
||||||
|
case SK:
|
||||||
|
break;
|
||||||
|
case PK:
|
||||||
|
break;
|
||||||
|
case SSK:
|
||||||
|
break;
|
||||||
|
case COMP:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.CompressedData);
|
||||||
|
PGPCompressedData compressedData = new PGPCompressedData(inputStream);
|
||||||
|
inputStream = new BCPGInputStream(compressedData.getDataStream());
|
||||||
|
packetLayers.push(Layer.CompressedData(inputStream));
|
||||||
|
break;
|
||||||
|
case SED:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.EncryptedData);
|
||||||
|
SymmetricEncDataPacket symmetricEncDataPacket = (SymmetricEncDataPacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case MARKER:
|
||||||
|
inputStream.readPacket(); // discard
|
||||||
|
break;
|
||||||
|
case LIT:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.LiteralData);
|
||||||
|
PGPLiteralData literalData = new PGPLiteralData(inputStream);
|
||||||
|
packetLayers.push(Layer.LiteralMessage(literalData.getDataStream()));
|
||||||
|
break loop;
|
||||||
|
case TRUST:
|
||||||
|
TrustPacket trustPacket = (TrustPacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case UID:
|
||||||
|
UserIDPacket userIDPacket = (UserIDPacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case PSK:
|
||||||
|
break;
|
||||||
|
case UATTR:
|
||||||
|
UserAttributePacket userAttributePacket = (UserAttributePacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case SEIPD:
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.EncryptedData);
|
||||||
|
SymmetricEncIntegrityPacket symmetricEncIntegrityPacket = (SymmetricEncIntegrityPacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case MOD:
|
||||||
|
ModDetectionCodePacket modDetectionCodePacket = (ModDetectionCodePacket) inputStream.readPacket();
|
||||||
|
break;
|
||||||
|
case EXP_1:
|
||||||
|
break;
|
||||||
|
case EXP_2:
|
||||||
|
break;
|
||||||
|
case EXP_3:
|
||||||
|
break;
|
||||||
|
case EXP_4:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OpenPgpPacket nextTagOrThrow(BCPGInputStream inputStream)
|
||||||
|
throws IOException, InvalidOpenPgpPacketException {
|
||||||
|
try {
|
||||||
|
return OpenPgpPacket.requireFromTag(inputStream.nextPacketTag());
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
throw new InvalidOpenPgpPacketException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void popLayer() throws MalformedOpenPgpMessageException {
|
||||||
|
if (packetLayers.pop().isNested)
|
||||||
|
automaton.next(PushdownAutomaton.InputAlphabet.EndOfSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (packetLayers.isEmpty()) {
|
||||||
|
try {
|
||||||
|
automaton.assertValid();
|
||||||
|
} catch (MalformedOpenPgpMessageException e) {
|
||||||
|
throw e.toRuntimeException();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = -1;
|
||||||
|
try {
|
||||||
|
r = packetLayers.peek().inputStream.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
if (r == -1) {
|
||||||
|
try {
|
||||||
|
popLayer();
|
||||||
|
walkLayer();
|
||||||
|
} catch (MalformedOpenPgpMessageException e) {
|
||||||
|
throw e.toRuntimeException();
|
||||||
|
}
|
||||||
|
catch (PGPException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return read();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvalidOpenPgpPacketException extends PGPException {
|
||||||
|
|
||||||
|
public InvalidOpenPgpPacketException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Layer {
|
||||||
|
InputStream inputStream;
|
||||||
|
boolean isNested;
|
||||||
|
|
||||||
|
private Layer(InputStream inputStream, boolean isNested) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.isNested = isNested;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Layer initial(InputStream inputStream) {
|
||||||
|
BCPGInputStream bcpgIn;
|
||||||
|
if (inputStream instanceof BCPGInputStream) {
|
||||||
|
bcpgIn = (BCPGInputStream) inputStream;
|
||||||
|
} else {
|
||||||
|
bcpgIn = new BCPGInputStream(inputStream);
|
||||||
|
}
|
||||||
|
return new Layer(bcpgIn, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Layer LiteralMessage(InputStream inputStream) {
|
||||||
|
return new Layer(inputStream, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Layer CompressedData(InputStream inputStream) {
|
||||||
|
return new Layer(inputStream, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -342,6 +342,12 @@ public class PushdownAutomaton {
|
||||||
return getState() == State.Valid && stack.isEmpty();
|
return getState() == State.Valid && stack.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertValid() throws MalformedOpenPgpMessageException {
|
||||||
|
if (!isValid()) {
|
||||||
|
throw new MalformedOpenPgpMessageException("Pushdown Automaton is not in an acceptable state: " + toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop an item from the stack.
|
* Pop an item from the stack.
|
||||||
*
|
*
|
||||||
|
|
|
@ -28,4 +28,15 @@ public class MalformedOpenPgpMessageException extends PGPException {
|
||||||
PushdownAutomaton.StackAlphabet stackItem) {
|
PushdownAutomaton.StackAlphabet stackItem) {
|
||||||
this("Invalid input: There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
this("Invalid input: There is no legal transition from state '" + state + "' for input '" + input + "' when '" + stackItem + "' is on top of the stack.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RTE toRuntimeException() {
|
||||||
|
return new RTE(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RTE extends RuntimeException {
|
||||||
|
|
||||||
|
public RTE(MalformedOpenPgpMessageException e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.PacketTags;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
public class OpenPgpPacketTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromInvalidTag() {
|
||||||
|
int tag = PacketTags.RESERVED;
|
||||||
|
assertNull(OpenPgpPacket.fromTag(tag));
|
||||||
|
assertThrows(NoSuchElementException.class,
|
||||||
|
() -> OpenPgpPacket.requireFromTag(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromExistingTags() {
|
||||||
|
for (OpenPgpPacket p : OpenPgpPacket.values()) {
|
||||||
|
assertEquals(p, OpenPgpPacket.fromTag(p.getTag()));
|
||||||
|
assertEquals(p, OpenPgpPacket.requireFromTag(p.getTag()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPKESKTagMatches() {
|
||||||
|
assertEquals(PacketTags.PUBLIC_KEY_ENC_SESSION, OpenPgpPacket.PKESK.getTag());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||||
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
|
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
|
||||||
|
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
|
import org.pgpainless.exception.MalformedOpenPgpMessageException;
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.util.ArmoredInputStreamFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
public class PGPDecryptionStreamTest {
|
||||||
|
|
||||||
|
private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: DA05 848F 37D4 68E6 F982 C889 7A70 1FC6 904D 3F4C\n" +
|
||||||
|
"Comment: Alice <alice@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEYxzSCBYJKwYBBAHaRw8BAQdAeJU8m4GOJb1eQgv/ryilFHRfNLTYFMNqL6zj\n" +
|
||||||
|
"r0vF7dsAAP42rAtngpJ6dZxoZlJX0Je65zk1VMPeTrXaWfPS2HSKBRGptBxBbGlj\n" +
|
||||||
|
"ZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+iI8EExYKAEEFAmMc0ggJEHpwH8aQTT9M\n" +
|
||||||
|
"FiEE2gWEjzfUaOb5gsiJenAfxpBNP0wCngECmwEFFgIDAQAECwkIBwUVCgkICwKZ\n" +
|
||||||
|
"AQAApZEBALUXHtvswPZG28YO+16Men6/fpk+scvqpNMnD4ty3IkAAPwK6TuXjNnZ\n" +
|
||||||
|
"0XuWdnilvLMV23Ai1d5g6em+lwLK5M2SApxdBGMc0ggSCisGAQQBl1UBBQEBB0D8\n" +
|
||||||
|
"mNUVX8y2MXFaSeFYqOTPFnGT7dgNVdn6yc0UtkkHOgMBCAcAAP9y9OtP4SX9voPb\n" +
|
||||||
|
"ID2u9PkJKgo4hTB8NK5LouGppdRtEBGriHUEGBYKAB0FAmMc0ggCngECmwwFFgID\n" +
|
||||||
|
"AQAECwkIBwUVCgkICwAKCRB6cB/GkE0/TAywAQDpZRJS/joFH4+xcwheqWfI7ay/\n" +
|
||||||
|
"WfojUoGQMYGnUjsgYwEAkceRUsgkqI0SVgYvuglfaQpZ9k2ns1mZGVLkXvu/yQyc\n" +
|
||||||
|
"WARjHNIIFgkrBgEEAdpHDwEBB0BGN9BybSOrj8B6gim1SjbB/IiqAshlzMDunVkQ\n" +
|
||||||
|
"X23npQABAJqvjOOY7qhBuTusC5/Q5+25iLrhMn4TI+LXlJHMVNOaE0OI1QQYFgoA\n" +
|
||||||
|
"fQUCYxzSCAKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmMc0ggACgkQ\n" +
|
||||||
|
"KALh4BJQXl6yTQD/dh0N5228Uwtu7XHy6dmpMRX62cac5tXQ9WaDzpy8STgBAMdn\n" +
|
||||||
|
"Mq948UOYEhdk/ZY2/hwux/4t+FHvqrXW8ziBe4cLAAoJEHpwH8aQTT9M1hQA/3Ms\n" +
|
||||||
|
"P3kzoed3VsWu1ZMr7dKEngbc6SoJ2XPayzN0QYJaAQCIY5NcT9mZF97HWV3Vgeum\n" +
|
||||||
|
"00sWMHXfkW3+nl5OpUZaDA==\n" +
|
||||||
|
"=THgv\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
|
||||||
|
private static final String PLAINTEXT = "Hello, World!\n";
|
||||||
|
|
||||||
|
private static final String LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"yxRiAAAAAABIZWxsbywgV29ybGQhCg==\n" +
|
||||||
|
"=WGju\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
private static final String LIT_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"yxRiAAAAAABIZWxsbywgV29ybGQhCssUYgAAAAAASGVsbG8sIFdvcmxkIQo=\n" +
|
||||||
|
"=A91Q\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
private static final String COMP_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owE7LZLEAAIeqTk5+ToK4flFOSmKXAA=\n" +
|
||||||
|
"=ZYDg\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
private static final String COMP = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owEDAA==\n" +
|
||||||
|
"=MDzg\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
private static final String COMP_COMP_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"owEBRwC4/6MDQlpoOTFBWSZTWVuW2KAAAAr3hGAQBABgBABAAIAWBJAAAAggADFM\n" +
|
||||||
|
"ABNBqBo00N6puqWR+TqInoXQ58XckU4UJBbltigA\n" +
|
||||||
|
"=K9Zl\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
private static final String SIG_LIT = "" +
|
||||||
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: BCPG v1.71\n" +
|
||||||
|
"\n" +
|
||||||
|
"iHUEABYKACcFAmMc1i0JECgC4eASUF5eFiEEjN3RiJxCf/TyYOQjKALh4BJQXl4A\n" +
|
||||||
|
"AHkrAP98uPpqrgIix7epgL7MM1cjXXGSxqbDfXHwgptk1YGQlgD/fw89VGcXwFaI\n" +
|
||||||
|
"2k7kpXQYy/1BqnovM/jZ3X3mXhhTaAOjATstksQAAh6pOTn5Ogrh+UU5KYpcAA==\n" +
|
||||||
|
"=WKPn\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genLIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processLIT() throws IOException, PGPException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(LIT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(decIn, out);
|
||||||
|
assertEquals(PLAINTEXT, out.toString());
|
||||||
|
armorIn.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getLIT_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
|
||||||
|
litOut = litGen.open(armorOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processLIT_LIT() throws IOException, PGPException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(LIT_LIT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
assertThrows(MalformedOpenPgpMessageException.RTE.class, () -> Streams.pipeAll(decIn, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genCOMP_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut = compGen.open(armorOut);
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(compOut, PGPLiteralDataGenerator.BINARY, "", PGPLiteralData.NOW, new byte[1 << 9]);
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
compOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processCOMP_LIT() throws IOException, PGPException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP_LIT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(decIn, out);
|
||||||
|
decIn.close();
|
||||||
|
armorIn.close();
|
||||||
|
|
||||||
|
assertEquals(PLAINTEXT, out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genCOMP() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut = compGen.open(armorOut);
|
||||||
|
compOut.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processCOMP() throws IOException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
assertThrows(MalformedOpenPgpMessageException.RTE.class, () -> {
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
Streams.drain(decIn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genCOMP_COMP_LIT() throws IOException {
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(System.out);
|
||||||
|
|
||||||
|
PGPCompressedDataGenerator compGen1 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
|
||||||
|
OutputStream compOut1 = compGen1.open(armorOut);
|
||||||
|
|
||||||
|
PGPCompressedDataGenerator compGen2 = new PGPCompressedDataGenerator(CompressionAlgorithmTags.BZIP2);
|
||||||
|
OutputStream compOut2 = compGen2.open(compOut1);
|
||||||
|
|
||||||
|
PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator();
|
||||||
|
OutputStream litOut = litGen.open(compOut2, PGPLiteralDataGenerator.BINARY, "", PGPLiteralDataGenerator.NOW, new byte[1 << 9]);
|
||||||
|
|
||||||
|
litOut.write(PLAINTEXT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
litOut.close();
|
||||||
|
compOut2.close();
|
||||||
|
compOut1.close();
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processCOMP_COMP_LIT() throws PGPException, IOException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(COMP_COMP_LIT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(decIn, out);
|
||||||
|
decIn.close();
|
||||||
|
|
||||||
|
assertEquals(PLAINTEXT, out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
|
System.out.println(PGPainless.asciiArmor(
|
||||||
|
PGPainless.generateKeyRing().modernKeyRing("Alice <alice@pgpainless.org>")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genSIG_LIT() throws PGPException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
|
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream signer = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(msgOut)
|
||||||
|
.withOptions(
|
||||||
|
ProducerOptions.sign(
|
||||||
|
SigningOptions.get()
|
||||||
|
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys)
|
||||||
|
).setAsciiArmor(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
Streams.pipeAll(new ByteArrayInputStream(PLAINTEXT.getBytes(StandardCharsets.UTF_8)), signer);
|
||||||
|
signer.close();
|
||||||
|
EncryptionResult result = signer.getResult();
|
||||||
|
PGPSignature detachedSignature = result.getDetachedSignatures().get(result.getDetachedSignatures().keySet().iterator().next()).iterator().next();
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(out);
|
||||||
|
armorOut.flush();
|
||||||
|
detachedSignature.encode(armorOut);
|
||||||
|
armorOut.write(msgOut.toByteArray());
|
||||||
|
armorOut.close();
|
||||||
|
|
||||||
|
String armored = out.toString();
|
||||||
|
System.out.println(armored
|
||||||
|
.replace("-----BEGIN PGP SIGNATURE-----\n", "-----BEGIN PGP MESSAGE-----\n")
|
||||||
|
.replace("-----END PGP SIGNATURE-----", "-----END PGP MESSAGE-----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void processSIG_LIT() throws IOException, PGPException {
|
||||||
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(SIG_LIT.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
|
||||||
|
PGPDecryptionStream decIn = new PGPDecryptionStream(armorIn);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(decIn, out);
|
||||||
|
decIn.close();
|
||||||
|
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue