mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-27 06:42:05 +01:00
Introduce OpenPgpMetadata.FileInfo class for setting/getting file name, mod date, encoding...
This commit is contained in:
parent
3e29258ade
commit
2c4a3fca6a
7 changed files with 292 additions and 23 deletions
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoding of the stream.
|
||||||
|
*
|
||||||
|
* @see <a href="https://tools.ietf.org/html/rfc4880#section-5.9">RFC4880: Literal Data Packet</a>
|
||||||
|
*/
|
||||||
|
public enum StreamEncoding {
|
||||||
|
BINARY(PGPLiteralData.BINARY),
|
||||||
|
TEXT(PGPLiteralData.TEXT),
|
||||||
|
UTF8(PGPLiteralData.UTF8),
|
||||||
|
@Deprecated
|
||||||
|
LOCAL('l'),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final char code;
|
||||||
|
|
||||||
|
private static final Map<Character, StreamEncoding> MAP = new ConcurrentHashMap<>();
|
||||||
|
static {
|
||||||
|
for (StreamEncoding f : StreamEncoding.values()) {
|
||||||
|
MAP.put(f.code, f);
|
||||||
|
}
|
||||||
|
MAP.put('1', LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamEncoding(char code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StreamEncoding fromCode(int code) {
|
||||||
|
return MAP.get((char) code);
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,7 @@ import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
@ -173,6 +174,11 @@ public final class DecryptionStreamFactory {
|
||||||
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData) {
|
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData) {
|
||||||
LOGGER.log(LEVEL, "Found PGPLiteralData");
|
LOGGER.log(LEVEL, "Found PGPLiteralData");
|
||||||
InputStream literalDataInputStream = pgpLiteralData.getInputStream();
|
InputStream literalDataInputStream = pgpLiteralData.getInputStream();
|
||||||
|
OpenPgpMetadata.FileInfo fileInfo = new OpenPgpMetadata.FileInfo(
|
||||||
|
pgpLiteralData.getFileName(),
|
||||||
|
pgpLiteralData.getModificationTime(),
|
||||||
|
StreamEncoding.fromCode(pgpLiteralData.getFormat()));
|
||||||
|
resultBuilder.setFileInfo(fileInfo);
|
||||||
|
|
||||||
if (verifiableOnePassSignatures.isEmpty()) {
|
if (verifiableOnePassSignatures.isEmpty()) {
|
||||||
LOGGER.log(LEVEL, "No OnePassSignatures found -> We are done");
|
LOGGER.log(LEVEL, "No OnePassSignatures found -> We are done");
|
||||||
|
|
|
@ -17,16 +17,19 @@ package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
@ -39,6 +42,7 @@ public class OpenPgpMetadata {
|
||||||
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
|
||||||
private final CompressionAlgorithm compressionAlgorithm;
|
private final CompressionAlgorithm compressionAlgorithm;
|
||||||
private final boolean integrityProtected;
|
private final boolean integrityProtected;
|
||||||
|
private final FileInfo fileInfo;
|
||||||
|
|
||||||
public OpenPgpMetadata(Set<Long> recipientKeyIds,
|
public OpenPgpMetadata(Set<Long> recipientKeyIds,
|
||||||
OpenPgpV4Fingerprint decryptionFingerprint,
|
OpenPgpV4Fingerprint decryptionFingerprint,
|
||||||
|
@ -46,7 +50,8 @@ public class OpenPgpMetadata {
|
||||||
CompressionAlgorithm algorithm,
|
CompressionAlgorithm algorithm,
|
||||||
boolean integrityProtected,
|
boolean integrityProtected,
|
||||||
List<OnePassSignature> onePassSignatures,
|
List<OnePassSignature> onePassSignatures,
|
||||||
List<DetachedSignature> detachedSignatures) {
|
List<DetachedSignature> detachedSignatures,
|
||||||
|
FileInfo fileInfo) {
|
||||||
|
|
||||||
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
this.recipientKeyIds = Collections.unmodifiableSet(recipientKeyIds);
|
||||||
this.decryptionFingerprint = decryptionFingerprint;
|
this.decryptionFingerprint = decryptionFingerprint;
|
||||||
|
@ -55,6 +60,7 @@ public class OpenPgpMetadata {
|
||||||
this.integrityProtected = integrityProtected;
|
this.integrityProtected = integrityProtected;
|
||||||
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
|
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
|
||||||
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
|
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
|
||||||
|
this.fileInfo = fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Long> getRecipientKeyIds() {
|
public Set<Long> getRecipientKeyIds() {
|
||||||
|
@ -144,6 +150,89 @@ public class OpenPgpMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FileInfo getFileInfo() {
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileInfo {
|
||||||
|
public static final String FOR_YOUR_EYES_ONLY = PGPLiteralData.CONSOLE;
|
||||||
|
|
||||||
|
protected final String fileName;
|
||||||
|
protected final Date modicationDate;
|
||||||
|
protected final StreamEncoding streamEncoding;
|
||||||
|
|
||||||
|
public FileInfo(String fileName, Date modicationDate, StreamEncoding streamEncoding) {
|
||||||
|
this.fileName = fileName == null ? "" : fileName;
|
||||||
|
this.modicationDate = modicationDate == null ? PGPLiteralData.NOW : modicationDate;
|
||||||
|
this.streamEncoding = streamEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileInfo binaryStream() {
|
||||||
|
return new FileInfo("", null, StreamEncoding.BINARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileInfo forYourEyesOnly() {
|
||||||
|
return new FileInfo(FOR_YOUR_EYES_ONLY, null, StreamEncoding.BINARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForYourEyesOnly() {
|
||||||
|
return FOR_YOUR_EYES_ONLY.equals(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getModificationDate() {
|
||||||
|
return modicationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamEncoding getStreamFormat() {
|
||||||
|
return streamEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(other instanceof FileInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo o = (FileInfo) other;
|
||||||
|
|
||||||
|
if (getFileName() != null) {
|
||||||
|
if (!getFileName().equals(o.getFileName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (o.getFileName() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getModificationDate() != null) {
|
||||||
|
if (o.getModificationDate() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long diff = Math.abs(getModificationDate().getTime() - o.getModificationDate().getTime());
|
||||||
|
if (diff > 1000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (o.getModificationDate() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getStreamFormat() == o.getStreamFormat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Builder getBuilder() {
|
public static Builder getBuilder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
@ -157,6 +246,7 @@ public class OpenPgpMetadata {
|
||||||
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
|
||||||
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||||
private boolean integrityProtected = false;
|
private boolean integrityProtected = false;
|
||||||
|
private FileInfo fileInfo;
|
||||||
|
|
||||||
public Builder addRecipientKeyId(Long keyId) {
|
public Builder addRecipientKeyId(Long keyId) {
|
||||||
this.recipientFingerprints.add(keyId);
|
this.recipientFingerprints.add(keyId);
|
||||||
|
@ -195,10 +285,15 @@ public class OpenPgpMetadata {
|
||||||
this.onePassSignatures.add(onePassSignature);
|
this.onePassSignatures.add(onePassSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setFileInfo(FileInfo fileInfo) {
|
||||||
|
this.fileInfo = fileInfo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public OpenPgpMetadata build() {
|
public OpenPgpMetadata build() {
|
||||||
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
|
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
|
||||||
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
|
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
|
||||||
onePassSignatures, detachedSignatures);
|
onePassSignatures, detachedSignatures, fileInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
import org.pgpainless.exception.SecretKeyNotFoundException;
|
import org.pgpainless.exception.SecretKeyNotFoundException;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
@ -68,8 +69,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
|
private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
|
||||||
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
|
||||||
private boolean asciiArmor = false;
|
private boolean asciiArmor = false;
|
||||||
private String fileName;
|
private OpenPgpMetadata.FileInfo fileInfo;
|
||||||
private boolean forYourEyesOnly;
|
|
||||||
|
|
||||||
public EncryptionBuilder() {
|
public EncryptionBuilder() {
|
||||||
this.purpose = EncryptionStream.Purpose.COMMUNICATIONS;
|
this.purpose = EncryptionStream.Purpose.COMMUNICATIONS;
|
||||||
|
@ -80,10 +80,9 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ToRecipients onOutputStream(@Nonnull OutputStream outputStream, String fileName, boolean forYourEyesOnly) {
|
public ToRecipients onOutputStream(@Nonnull OutputStream outputStream, OpenPgpMetadata.FileInfo fileInfo) {
|
||||||
this.outputStream = outputStream;
|
this.outputStream = outputStream;
|
||||||
this.fileName = fileName == null ? "" : fileName;
|
this.fileInfo = fileInfo;
|
||||||
this.forYourEyesOnly = forYourEyesOnly;
|
|
||||||
return new ToRecipientsImpl();
|
return new ToRecipientsImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,8 +434,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
||||||
EncryptionBuilder.this.hashAlgorithm,
|
EncryptionBuilder.this.hashAlgorithm,
|
||||||
EncryptionBuilder.this.compressionAlgorithm,
|
EncryptionBuilder.this.compressionAlgorithm,
|
||||||
EncryptionBuilder.this.asciiArmor,
|
EncryptionBuilder.this.asciiArmor,
|
||||||
fileName,
|
fileInfo);
|
||||||
forYourEyesOnly);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.pgpainless.encryption_signing;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
@ -28,6 +29,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
import org.pgpainless.exception.SecretKeyNotFoundException;
|
import org.pgpainless.exception.SecretKeyNotFoundException;
|
||||||
|
@ -48,7 +50,7 @@ public interface EncryptionBuilderInterface {
|
||||||
* @return api handle
|
* @return api handle
|
||||||
*/
|
*/
|
||||||
default ToRecipients onOutputStream(@Nonnull OutputStream outputStream) {
|
default ToRecipients onOutputStream(@Nonnull OutputStream outputStream) {
|
||||||
return onOutputStream(outputStream,false);
|
return onOutputStream(outputStream, OpenPgpMetadata.FileInfo.binaryStream());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Create a {@link EncryptionStream} on an {@link OutputStream} that contains the plain data which shall
|
* Create a {@link EncryptionStream} on an {@link OutputStream} that contains the plain data which shall
|
||||||
|
@ -57,9 +59,11 @@ public interface EncryptionBuilderInterface {
|
||||||
* @param outputStream outputStream
|
* @param outputStream outputStream
|
||||||
* @param forYourEyesOnly flag indicating that the data is intended for the recipients eyes only
|
* @param forYourEyesOnly flag indicating that the data is intended for the recipients eyes only
|
||||||
* @return api handle
|
* @return api handle
|
||||||
|
*
|
||||||
|
* @deprecated use {@link #onOutputStream(OutputStream, OpenPgpMetadata.FileInfo)} instead.
|
||||||
*/
|
*/
|
||||||
default ToRecipients onOutputStream(@Nonnull OutputStream outputStream, boolean forYourEyesOnly) {
|
default ToRecipients onOutputStream(@Nonnull OutputStream outputStream, boolean forYourEyesOnly) {
|
||||||
return onOutputStream(outputStream, "", forYourEyesOnly);
|
return onOutputStream(outputStream, forYourEyesOnly ? OpenPgpMetadata.FileInfo.forYourEyesOnly() : OpenPgpMetadata.FileInfo.binaryStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,8 +74,22 @@ public interface EncryptionBuilderInterface {
|
||||||
* @param fileName name of the file (or "" if the encrypted data is not a file)
|
* @param fileName name of the file (or "" if the encrypted data is not a file)
|
||||||
* @param forYourEyesOnly flag indicating that the data is intended for the recipients eyes only
|
* @param forYourEyesOnly flag indicating that the data is intended for the recipients eyes only
|
||||||
* @return api handle
|
* @return api handle
|
||||||
|
*
|
||||||
|
* @deprecated use {@link #onOutputStream(OutputStream, OpenPgpMetadata.FileInfo)} instead.
|
||||||
*/
|
*/
|
||||||
ToRecipients onOutputStream(@Nonnull OutputStream outputStream, String fileName, boolean forYourEyesOnly);
|
default ToRecipients onOutputStream(@Nonnull OutputStream outputStream, String fileName, boolean forYourEyesOnly) {
|
||||||
|
return onOutputStream(outputStream, new OpenPgpMetadata.FileInfo(forYourEyesOnly ? "_CONSOLE" : fileName, new Date(), StreamEncoding.BINARY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link EncryptionStream} on an {@link OutputStream} that contains the plain data which shall
|
||||||
|
* be encrypted and/or signed.
|
||||||
|
*
|
||||||
|
* @param outputStream outputStream
|
||||||
|
* @param fileInfo file information
|
||||||
|
* @return api handle
|
||||||
|
*/
|
||||||
|
ToRecipients onOutputStream(@Nonnull OutputStream outputStream, OpenPgpMetadata.FileInfo fileInfo);
|
||||||
|
|
||||||
interface ToRecipients {
|
interface ToRecipients {
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.pgpainless.encryption_signing;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -31,7 +30,6 @@ import org.bouncycastle.bcpg.BCPGOutputStream;
|
||||||
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
|
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPLiteralData;
|
|
||||||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
@ -118,8 +116,7 @@ public final class EncryptionStream extends OutputStream {
|
||||||
@Nonnull HashAlgorithm hashAlgorithm,
|
@Nonnull HashAlgorithm hashAlgorithm,
|
||||||
@Nonnull CompressionAlgorithm compressionAlgorithm,
|
@Nonnull CompressionAlgorithm compressionAlgorithm,
|
||||||
boolean asciiArmor,
|
boolean asciiArmor,
|
||||||
@Nonnull String fileName,
|
@Nonnull OpenPgpMetadata.FileInfo fileInfo)
|
||||||
boolean forYourEyesOnly)
|
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
|
|
||||||
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
|
||||||
|
@ -138,8 +135,9 @@ public final class EncryptionStream extends OutputStream {
|
||||||
prepareSigning();
|
prepareSigning();
|
||||||
prepareCompression();
|
prepareCompression();
|
||||||
prepareOnePassSignatures();
|
prepareOnePassSignatures();
|
||||||
prepareLiteralDataProcessing(fileName, forYourEyesOnly);
|
prepareLiteralDataProcessing(fileInfo);
|
||||||
prepareResultBuilder();
|
prepareResultBuilder();
|
||||||
|
resultBuilder.setFileInfo(fileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareArmor() {
|
private void prepareArmor() {
|
||||||
|
@ -227,14 +225,13 @@ public final class EncryptionStream extends OutputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareLiteralDataProcessing(@Nonnull String fileName, boolean forYourEyesOnly) throws IOException {
|
private void prepareLiteralDataProcessing(@Nonnull OpenPgpMetadata.FileInfo fileInfo) throws IOException {
|
||||||
literalDataGenerator = new PGPLiteralDataGenerator();
|
literalDataGenerator = new PGPLiteralDataGenerator();
|
||||||
String name = fileName;
|
|
||||||
if (forYourEyesOnly) {
|
|
||||||
name = PGPLiteralData.CONSOLE;
|
|
||||||
}
|
|
||||||
literalDataStream = literalDataGenerator.open(outermostStream,
|
literalDataStream = literalDataGenerator.open(outermostStream,
|
||||||
PGPLiteralData.BINARY, name, new Date(), new byte[BUFFER_SIZE]);
|
fileInfo.getStreamFormat().getCode(),
|
||||||
|
fileInfo.getFileName(),
|
||||||
|
fileInfo.getModificationDate(),
|
||||||
|
new byte[BUFFER_SIZE]);
|
||||||
outermostStream = literalDataStream;
|
outermostStream = literalDataStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.encryption_signing;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.key.util.KeyRingUtils;
|
||||||
|
|
||||||
|
public class FileInfoTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void textFile() throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException {
|
||||||
|
OpenPgpMetadata.FileInfo fileInfo = new OpenPgpMetadata.FileInfo("message.txt", new Date(), StreamEncoding.TEXT);
|
||||||
|
executeWith(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void binaryStream() throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException {
|
||||||
|
OpenPgpMetadata.FileInfo fileInfo = OpenPgpMetadata.FileInfo.binaryStream();
|
||||||
|
executeWith(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forYourEyesOnly() throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException, IOException {
|
||||||
|
OpenPgpMetadata.FileInfo fileInfo = OpenPgpMetadata.FileInfo.forYourEyesOnly();
|
||||||
|
executeWith(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeWith(OpenPgpMetadata.FileInfo fileInfo) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit");
|
||||||
|
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||||
|
|
||||||
|
String data = "Hello, World!";
|
||||||
|
|
||||||
|
ByteArrayInputStream dataIn = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
ByteArrayOutputStream dataOut = new ByteArrayOutputStream();
|
||||||
|
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
|
||||||
|
.onOutputStream(dataOut, fileInfo)
|
||||||
|
.toRecipients(publicKeys)
|
||||||
|
.usingSecureAlgorithms()
|
||||||
|
.doNotSign()
|
||||||
|
.noArmor();
|
||||||
|
|
||||||
|
Streams.pipeAll(dataIn, encryptionStream);
|
||||||
|
encryptionStream.close();
|
||||||
|
|
||||||
|
OpenPgpMetadata.FileInfo cryptInfo = encryptionStream.getResult().getFileInfo();
|
||||||
|
assertEquals(fileInfo, cryptInfo);
|
||||||
|
|
||||||
|
ByteArrayInputStream cryptIn = new ByteArrayInputStream(dataOut.toByteArray());
|
||||||
|
ByteArrayOutputStream plainOut = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
|
||||||
|
.onInputStream(cryptIn)
|
||||||
|
.decryptWith(SecretKeyRingProtector.unprotectedKeys(), new PGPSecretKeyRingCollection(Collections.singleton(secretKeys)))
|
||||||
|
.doNotVerify()
|
||||||
|
.build();
|
||||||
|
Streams.pipeAll(decryptionStream, plainOut);
|
||||||
|
|
||||||
|
decryptionStream.close();
|
||||||
|
|
||||||
|
OpenPgpMetadata.FileInfo decryptInfo = decryptionStream.getResult().getFileInfo();
|
||||||
|
assertEquals(fileInfo, decryptInfo);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue