pgpainless/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilder.java

307 lines
12 KiB
Java
Raw Normal View History

/*
* Copyright 2018 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;
2018-06-05 01:30:58 +02:00
import java.io.IOException;
2018-06-04 19:45:18 +02:00
import java.io.OutputStream;
2020-08-24 16:26:29 +02:00
import javax.annotation.Nonnull;
2018-06-05 01:30:58 +02:00
import org.bouncycastle.openpgp.PGPException;
2018-06-04 19:45:18 +02:00
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
2021-04-26 13:38:12 +02:00
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
2021-05-06 00:04:03 +02:00
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.decryption_verification.OpenPgpMetadata;
2021-05-06 00:04:03 +02:00
import org.pgpainless.exception.KeyValidationException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
2021-05-06 00:04:03 +02:00
import org.pgpainless.policy.Policy;
2021-04-26 13:38:12 +02:00
import org.pgpainless.util.Passphrase;
2018-06-04 19:45:18 +02:00
public class EncryptionBuilder implements EncryptionBuilderInterface {
private OutputStream outputStream;
2021-05-06 00:04:03 +02:00
private EncryptionOptions encryptionOptions;
private SigningOptions signingOptions = new SigningOptions();
private ProducerOptions options;
private OpenPgpMetadata.FileInfo fileInfo;
2018-06-04 19:45:18 +02:00
public EncryptionBuilder() {
2021-05-06 00:04:03 +02:00
this.encryptionOptions = new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS);
}
public EncryptionBuilder(@Nonnull EncryptionStream.Purpose purpose) {
2021-05-06 00:04:03 +02:00
this.encryptionOptions = new EncryptionOptions(purpose);
}
2018-06-04 19:45:18 +02:00
@Override
2021-05-06 00:04:03 +02:00
public ToRecipientsOrNoEncryption onOutputStream(@Nonnull OutputStream outputStream, OpenPgpMetadata.FileInfo fileInfo) {
2018-06-04 19:45:18 +02:00
this.outputStream = outputStream;
this.fileInfo = fileInfo;
2021-05-06 00:04:03 +02:00
return new ToRecipientsOrNoEncryptionImpl();
2018-06-04 19:45:18 +02:00
}
class ToRecipientsImpl implements ToRecipients {
@Override
2021-05-06 00:04:03 +02:00
public AdditionalRecipients toRecipient(@Nonnull PGPPublicKeyRing key) {
encryptionOptions.addRecipient(key);
return new AdditionalRecipientsImpl();
2018-06-04 19:45:18 +02:00
}
2021-05-06 00:04:03 +02:00
@Override
public AdditionalRecipients toRecipient(@Nonnull PGPPublicKeyRing key, @Nonnull String userId) {
encryptionOptions.addRecipient(key, userId);
return new AdditionalRecipientsImpl();
}
@Override
2021-05-06 00:04:03 +02:00
public AdditionalRecipients toRecipient(@Nonnull PGPPublicKeyRingCollection keys, @Nonnull String userId) {
for (PGPPublicKeyRing ring : keys) {
encryptionOptions.addRecipient(ring, userId);
}
2021-05-06 00:04:03 +02:00
return new AdditionalRecipientsImpl();
2018-06-04 19:45:18 +02:00
}
@Override
2021-05-06 00:04:03 +02:00
public AdditionalRecipients toRecipients(@Nonnull PGPPublicKeyRingCollection keys) {
for (PGPPublicKeyRing ring : keys) {
encryptionOptions.addRecipient(ring);
}
2021-05-06 00:04:03 +02:00
return new AdditionalRecipientsImpl();
}
2018-06-04 19:45:18 +02:00
@Override
2021-05-06 00:04:03 +02:00
public AdditionalRecipients forPassphrase(Passphrase passphrase) {
encryptionOptions.addPassphrase(passphrase);
return new AdditionalRecipientsImpl();
2018-06-04 19:45:18 +02:00
}
}
2021-05-06 00:04:03 +02:00
class ToRecipientsOrNoEncryptionImpl extends ToRecipientsImpl implements ToRecipientsOrNoEncryption {
2018-06-04 19:45:18 +02:00
2018-06-07 18:12:13 +02:00
@Override
2021-05-06 00:04:03 +02:00
public EncryptionStream withOptions(ProducerOptions options) throws PGPException, IOException {
if (options == null) {
throw new NullPointerException("ProducerOptions cannot be null.");
2018-06-07 18:12:13 +02:00
}
2021-05-06 00:04:03 +02:00
return new EncryptionStream(outputStream, options, fileInfo);
2018-06-04 19:45:18 +02:00
}
2018-07-08 18:05:55 +02:00
@Override
2021-05-06 00:04:03 +02:00
public SignWithOrDontSign doNotEncrypt() {
EncryptionBuilder.this.encryptionOptions = null;
return new SignWithOrDontSignImpl();
2018-06-11 01:33:49 +02:00
}
2021-05-06 00:04:03 +02:00
}
2018-06-11 01:33:49 +02:00
2021-05-06 00:04:03 +02:00
class AdditionalRecipientsImpl implements AdditionalRecipients {
2018-06-04 19:45:18 +02:00
@Override
2021-05-06 00:04:03 +02:00
public ToRecipientsOrSign and() {
return new ToRecipientsOrSignImpl();
2018-06-04 19:45:18 +02:00
}
2021-05-06 00:04:03 +02:00
}
2018-06-04 19:45:18 +02:00
2021-05-06 00:04:03 +02:00
class ToRecipientsOrSignImpl extends ToRecipientsImpl implements ToRecipientsOrSign {
2018-06-07 18:12:13 +02:00
2021-05-06 00:04:03 +02:00
@Override
public Armor doNotSign() {
EncryptionBuilder.this.signingOptions = null;
return new ArmorImpl();
2020-08-24 14:55:06 +02:00
}
@Override
2021-05-20 13:42:52 +02:00
public AdditionalSignWith signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing... keyRings) throws KeyValidationException, PGPException {
2021-05-06 00:04:03 +02:00
return new SignWithImpl().signWith(decryptor, keyRings);
}
2020-08-24 14:55:06 +02:00
@Override
2021-05-20 13:42:52 +02:00
public AdditionalSignWith signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection keyRings) throws PGPException {
2021-05-06 00:04:03 +02:00
return new SignWithImpl().signWith(decryptor, keyRings);
2018-06-04 19:45:18 +02:00
}
2020-08-24 14:55:06 +02:00
@Override
2021-05-06 00:04:03 +02:00
public AdditionalSignWith signInlineWith(@Nonnull SecretKeyRingProtector secretKeyDecryptor, @Nonnull PGPSecretKeyRing signingKey, String userId, DocumentSignatureType signatureType) throws PGPException {
return new SignWithImpl().signInlineWith(secretKeyDecryptor, signingKey, userId, signatureType);
2020-08-24 14:55:06 +02:00
}
@Override
2021-05-06 00:04:03 +02:00
public AdditionalSignWith signDetachedWith(@Nonnull SecretKeyRingProtector secretKeyDecryptor, @Nonnull PGPSecretKeyRing signingKey, String userId, DocumentSignatureType signatureType) throws PGPException {
return new SignWithImpl().signDetachedWith(secretKeyDecryptor, signingKey, userId, signatureType);
2020-08-24 14:55:06 +02:00
}
2021-05-06 00:04:03 +02:00
}
class SignWithOrDontSignImpl extends SignWithImpl implements SignWithOrDontSign {
2020-08-24 14:55:06 +02:00
@Override
2021-05-06 00:04:03 +02:00
public Armor doNotSign() {
return new ArmorImpl();
}
2018-06-07 18:12:13 +02:00
}
class SignWithImpl implements SignWith {
2018-06-04 19:45:18 +02:00
@Override
2021-05-06 00:04:03 +02:00
public AdditionalSignWith signWith(@Nonnull SecretKeyRingProtector decryptor,
@Nonnull PGPSecretKeyRing... keyRings)
2021-05-20 13:42:52 +02:00
throws KeyValidationException, PGPException {
2021-05-06 00:04:03 +02:00
for (PGPSecretKeyRing secretKeyRing : keyRings) {
signingOptions.addInlineSignature(decryptor, secretKeyRing, DocumentSignatureType.BINARY_DOCUMENT);
2018-06-04 19:45:18 +02:00
}
2021-05-06 00:04:03 +02:00
return new AdditionalSignWithImpl();
2020-12-16 16:33:14 +01:00
}
@Override
2021-05-06 00:04:03 +02:00
public AdditionalSignWith signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection keyRings)
2021-05-20 13:42:52 +02:00
throws KeyValidationException, PGPException {
2021-05-06 00:04:03 +02:00
for (PGPSecretKeyRing key : keyRings) {
signingOptions.addInlineSignature(decryptor, key, DocumentSignatureType.BINARY_DOCUMENT);
}
2021-05-06 00:04:03 +02:00
return new AdditionalSignWithImpl();
}
2021-05-06 00:04:03 +02:00
@Override
public AdditionalSignWith signInlineWith(@Nonnull SecretKeyRingProtector secretKeyDecryptor,
@Nonnull PGPSecretKeyRing signingKey,
String userId,
DocumentSignatureType signatureType)
throws KeyValidationException, PGPException {
signingOptions.addInlineSignature(secretKeyDecryptor, signingKey, userId, signatureType);
return new AdditionalSignWithImpl();
}
@Override
public AdditionalSignWith signDetachedWith(@Nonnull SecretKeyRingProtector secretKeyDecryptor,
@Nonnull PGPSecretKeyRing signingKey,
String userId,
DocumentSignatureType signatureType)
throws PGPException, KeyValidationException {
2021-05-20 13:42:52 +02:00
signingOptions.addDetachedSignature(secretKeyDecryptor, signingKey, userId, signatureType);
2021-05-06 00:04:03 +02:00
return new AdditionalSignWithImpl();
}
2020-12-16 16:33:14 +01:00
}
2021-05-06 00:04:03 +02:00
class AdditionalSignWithImpl implements AdditionalSignWith {
2020-12-16 16:33:14 +01:00
@Override
2021-05-06 00:04:03 +02:00
public SignWith and() {
return new SignWithImpl();
2020-12-16 16:33:14 +01:00
}
@Override
2021-05-06 00:04:03 +02:00
public EncryptionStream asciiArmor() throws IOException, PGPException {
return new ArmorImpl().asciiArmor();
}
@Override
public EncryptionStream noArmor() throws IOException, PGPException {
return new ArmorImpl().noArmor();
2018-06-04 19:45:18 +02:00
}
}
class ArmorImpl implements Armor {
@Override
public EncryptionStream asciiArmor() throws IOException, PGPException {
2021-05-06 00:04:03 +02:00
assignProducerOptions();
options.setAsciiArmor(true);
2018-06-04 19:45:18 +02:00
return build();
}
@Override
public EncryptionStream noArmor() throws IOException, PGPException {
2021-05-06 00:04:03 +02:00
assignProducerOptions();
options.setAsciiArmor(false);
2018-06-04 19:45:18 +02:00
return build();
}
private EncryptionStream build() throws IOException, PGPException {
return new EncryptionStream(
2018-06-04 19:45:18 +02:00
EncryptionBuilder.this.outputStream,
2021-05-06 00:04:03 +02:00
EncryptionBuilder.this.options,
fileInfo);
2018-06-04 19:45:18 +02:00
}
2021-05-06 00:04:03 +02:00
private void assignProducerOptions() {
if (encryptionOptions != null && signingOptions != null) {
options = ProducerOptions.signAndEncrypt(encryptionOptions, signingOptions);
} else if (encryptionOptions != null) {
options = ProducerOptions.encrypt(encryptionOptions);
} else if (signingOptions != null) {
options = ProducerOptions.sign(signingOptions);
} else {
options = ProducerOptions.noEncryptionNoSigning();
}
}
2018-06-04 19:45:18 +02:00
}
2018-06-11 01:33:49 +02:00
2021-05-06 00:04:03 +02:00
/**
* Negotiate the {@link SymmetricKeyAlgorithm} used for message encryption.
* If the user chose to set an override ({@link EncryptionOptions#overrideEncryptionAlgorithm(SymmetricKeyAlgorithm)}, use that.
* Otherwise find an algorithm which is acceptable for all recipients.
* If no consensus can be reached, use {@link Policy.SymmetricKeyAlgorithmPolicy#getDefaultSymmetricKeyAlgorithm()}.
*
* @param encryptionOptions encryption options
* @return negotiated symmetric key algorithm
*/
public static SymmetricKeyAlgorithm negotiateSymmetricEncryptionAlgorithm(EncryptionOptions encryptionOptions) {
SymmetricKeyAlgorithm encryptionAlgorithmOverride = encryptionOptions.getEncryptionAlgorithmOverride();
if (encryptionAlgorithmOverride != null) {
return encryptionAlgorithmOverride;
}
// TODO: Negotiation
2021-05-20 13:42:52 +02:00
return PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy().getDefaultSymmetricKeyAlgorithm();
}
2021-05-06 00:04:03 +02:00
/**
* Negotiate the {@link HashAlgorithm} used for signatures.
*
* If we encrypt and sign, we look at the recipients keys to determine which algorithm to use.
* If we only sign, we look at the singing keys preferences instead.
*
* @param encryptionOptions encryption options (recipients keys)
* @param signingOptions signing options (signing keys)
* @return negotiated hash algorithm
*/
public static HashAlgorithm negotiateSignatureHashAlgorithm(EncryptionOptions encryptionOptions, SigningOptions signingOptions) {
HashAlgorithm hashAlgorithmOverride = signingOptions.getHashAlgorithmOverride();
if (hashAlgorithmOverride != null) {
return hashAlgorithmOverride;
}
// TODO: Negotiation
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
}
2021-05-06 00:04:03 +02:00
public static CompressionAlgorithm negotiateCompressionAlgorithm(ProducerOptions producerOptions) {
CompressionAlgorithm compressionAlgorithmOverride = producerOptions.getCompressionAlgorithmOverride();
if (compressionAlgorithmOverride != null) {
return compressionAlgorithmOverride;
}
2021-05-06 00:04:03 +02:00
// TODO: Negotiation
return PGPainless.getPolicy().getCompressionAlgorithmPolicy().defaultCompressionAlgorithm();
2018-06-11 01:33:49 +02:00
}
}