2018-06-13 17:26:48 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-07-18 18:23:06 +02:00
|
|
|
package org.pgpainless.encryption_signing;
|
2018-06-04 14:50:09 +02:00
|
|
|
|
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-04 14:50:09 +02:00
|
|
|
|
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;
|
2021-04-30 10:23:12 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
2021-04-26 13:38:12 +02:00
|
|
|
import org.pgpainless.PGPainless;
|
2018-07-18 18:23:06 +02:00
|
|
|
import org.pgpainless.algorithm.CompressionAlgorithm;
|
2021-05-06 00:04:03 +02:00
|
|
|
import org.pgpainless.algorithm.DocumentSignatureType;
|
2018-07-18 18:23:06 +02:00
|
|
|
import org.pgpainless.algorithm.HashAlgorithm;
|
|
|
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
2021-04-25 00:28:48 +02:00
|
|
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
2021-05-06 00:04:03 +02:00
|
|
|
import org.pgpainless.exception.KeyValidationException;
|
2018-07-18 18:23:06 +02:00
|
|
|
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;
|
2021-04-25 00:28:48 +02:00
|
|
|
private OpenPgpMetadata.FileInfo fileInfo;
|
2018-06-04 19:45:18 +02:00
|
|
|
|
2021-01-09 20:30:34 +01:00
|
|
|
public EncryptionBuilder() {
|
2021-05-06 00:04:03 +02:00
|
|
|
this.encryptionOptions = new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS);
|
2021-01-09 20:30:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public EncryptionBuilder(@Nonnull EncryptionStream.Purpose purpose) {
|
2021-05-06 00:04:03 +02:00
|
|
|
this.encryptionOptions = new EncryptionOptions(purpose);
|
2021-01-09 20:30:34 +01:00
|
|
|
}
|
|
|
|
|
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;
|
2021-04-25 00:28:48 +02:00
|
|
|
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();
|
2018-07-08 17:22:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@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);
|
2018-07-08 17:22:29 +02:00
|
|
|
}
|
2021-05-06 00:04:03 +02:00
|
|
|
return new AdditionalRecipientsImpl();
|
2018-06-04 19:45:18 +02:00
|
|
|
}
|
|
|
|
|
2020-12-26 19:04:27 +01:00
|
|
|
@Override
|
2021-05-06 00:04:03 +02:00
|
|
|
public AdditionalRecipients toRecipients(@Nonnull PGPPublicKeyRingCollection keys) {
|
|
|
|
for (PGPPublicKeyRing ring : keys) {
|
|
|
|
encryptionOptions.addRecipient(ring);
|
2020-12-26 19:04:27 +01:00
|
|
|
}
|
2021-05-06 00:04:03 +02:00
|
|
|
return new AdditionalRecipientsImpl();
|
2020-12-26 19:04:27 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2020-12-26 19:04:27 +01: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-12-26 19:04:27 +01:00
|
|
|
}
|
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
|
|
|
|
2021-04-30 10:23:12 +02:00
|
|
|
@Override
|
2021-05-06 00:04:03 +02:00
|
|
|
public Armor doNotSign() {
|
|
|
|
return new ArmorImpl();
|
2021-04-30 10:23:12 +02:00
|
|
|
}
|
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
|
|
|
}
|
2021-04-30 10:23:12 +02: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-04-30 10:23:12 +02:00
|
|
|
}
|
2021-05-06 00:04:03 +02:00
|
|
|
return new AdditionalSignWithImpl();
|
|
|
|
}
|
2021-04-30 10:23:12 +02:00
|
|
|
|
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();
|
2021-04-30 10:23:12 +02:00
|
|
|
}
|
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
|
2018-06-27 15:09:39 +02:00
|
|
|
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
|
2018-06-27 15:09:39 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-06-27 15:09:39 +02:00
|
|
|
private EncryptionStream build() throws IOException, PGPException {
|
2018-07-31 20:09:16 +02:00
|
|
|
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,
|
2021-04-25 00:28:48 +02:00
|
|
|
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-01-09 20:30:34 +01:00
|
|
|
}
|
|
|
|
|
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-01-09 20:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-05-06 00:04:03 +02:00
|
|
|
public static CompressionAlgorithm negotiateCompressionAlgorithm(ProducerOptions producerOptions) {
|
|
|
|
CompressionAlgorithm compressionAlgorithmOverride = producerOptions.getCompressionAlgorithmOverride();
|
|
|
|
if (compressionAlgorithmOverride != null) {
|
|
|
|
return compressionAlgorithmOverride;
|
2021-01-09 20:30:34 +01:00
|
|
|
}
|
2021-05-06 00:04:03 +02:00
|
|
|
|
|
|
|
// TODO: Negotiation
|
|
|
|
|
|
|
|
return PGPainless.getPolicy().getCompressionAlgorithmPolicy().defaultCompressionAlgorithm();
|
2018-06-11 01:33:49 +02:00
|
|
|
}
|
2018-06-04 14:50:09 +02:00
|
|
|
}
|