From 4844bf697cbf971518a8b6a24ea16a7e1aa4f78b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 4 Jun 2018 19:45:18 +0200 Subject: [PATCH] Start working on EncryptionBuilder API --- .../PublicKeyNotFoundException.java | 17 ++ .../SecretKeyNotFoundException.java | 17 ++ .../encryption_signing/EncryptionBuilder.java | 194 +++++++++++++++++- .../EncryptionBuilderInterface.java | 71 +++++++ .../encryption_signing/EncryptionStream.java | 75 +++++++ 5 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java create mode 100644 src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java create mode 100644 src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java create mode 100644 src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionStream.java diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java new file mode 100644 index 00000000..2292efbc --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java @@ -0,0 +1,17 @@ +package de.vanitasvitae.crypto.pgpainless; + +public class PublicKeyNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + private final long keyId; + + public PublicKeyNotFoundException(long keyId) { + super("No PGPPublicKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); + this.keyId = keyId; + } + + public long getKeyId() { + return keyId; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java new file mode 100644 index 00000000..49ea9b8a --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java @@ -0,0 +1,17 @@ +package de.vanitasvitae.crypto.pgpainless; + +public class SecretKeyNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + private final long keyId; + + public SecretKeyNotFoundException(long keyId) { + super("No PGPSecretKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); + this.keyId = keyId; + } + + public long getKeyId() { + return keyId; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilder.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilder.java index 0b0011a8..d072de0f 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilder.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilder.java @@ -1,5 +1,197 @@ package de.vanitasvitae.crypto.pgpainless.encryption_signing; -public class EncryptionBuilder { +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import de.vanitasvitae.crypto.pgpainless.PublicKeyNotFoundException; +import de.vanitasvitae.crypto.pgpainless.SecretKeyNotFoundException; +import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; + +public class EncryptionBuilder implements EncryptionBuilderInterface { + + private OutputStream outputStream; + private final Set encryptionKeys = new HashSet<>(); + private final Set signingKeys = new HashSet<>(); + private SymmetricKeyAlgorithm symmetricKeyAlgorithm; + private HashAlgorithm hashAlgorithm; + private CompressionAlgorithm compressionAlgorithm; + private boolean asciiArmor = false; + + @Override + public ToRecipients onOutputStream(OutputStream outputStream) { + this.outputStream = outputStream; + return new ToRecipientsImpl(); + } + + class ToRecipientsImpl implements ToRecipients { + + @Override + public WithAlgorithms toRecipient(PGPPublicKey key) { + EncryptionBuilder.this.encryptionKeys.add(key); + return new WithAlgorithmsImpl(); + } + + @Override + public WithAlgorithms toRecipients(Set keys) { + EncryptionBuilder.this.encryptionKeys.addAll(keys); + return new WithAlgorithmsImpl(); + } + + @Override + public WithAlgorithms toRecipients(Set keyIds, Set keyRings) + throws PublicKeyNotFoundException { + + Set keys = new HashSet<>(); + + for (Long id : keyIds) { + PGPPublicKey key = null; + + for (PGPPublicKeyRing ring : keyRings) { + key = ring.getPublicKey(id); + if (key != null) { + break; // Found key. Break inner loop + } + } + + if (key == null) { + throw new PublicKeyNotFoundException(id); + } + + keys.add(key); + } + return toRecipients(keys); + } + + @Override + public WithAlgorithms toRecipients(Set keyIds, PGPPublicKeyRingCollection keyRings) + throws PublicKeyNotFoundException { + + Set rings = new HashSet<>(); + + for (Iterator i = keyRings.getKeyRings(); i.hasNext();) { + rings.add(i.next()); + } + + return toRecipients(keyIds, rings); + } + + @Override + public SignWith doNotEncrypt() { + return new SignWithImpl(); + } + } + + class WithAlgorithmsImpl implements WithAlgorithms { + + @Override + public WithAlgorithms andToSelf(Set keys) { + EncryptionBuilder.this.encryptionKeys.addAll(keys); + return this; + } + + @Override + public SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm, + HashAlgorithm hashAlgorithm, + CompressionAlgorithm compressionAlgorithm) { + + EncryptionBuilder.this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; + EncryptionBuilder.this.hashAlgorithm = hashAlgorithm; + EncryptionBuilder.this.compressionAlgorithm = compressionAlgorithm; + + return new SignWithImpl(); + } + } + + class SignWithImpl implements SignWith { + + @Override + public Armor signWith(PGPSecretKey key) { + EncryptionBuilder.this.signingKeys.add(key); + return new ArmorImpl(); + } + + @Override + public Armor signWith(Set keys) { + EncryptionBuilder.this.signingKeys.addAll(keys); + return new ArmorImpl(); + } + + @Override + public Armor signWith(Set keyIds, Set keyRings) + throws SecretKeyNotFoundException { + Set keys = new HashSet<>(); + + for (Long id : keyIds) { + + PGPSecretKey key = null; + + for (PGPSecretKeyRing ring : keyRings) { + key = ring.getSecretKey(id); + if (key != null) { + break; // Found key. Break inner loop + } + } + + if (key == null) { + throw new SecretKeyNotFoundException(id); + } + + keys.add(key); + } + return signWith(keys); + } + + @Override + public Armor signWith(Set keyIds, PGPSecretKeyRingCollection keys) + throws SecretKeyNotFoundException { + + Set rings = new HashSet<>(); + + for (Iterator i = keys.getKeyRings(); i.hasNext();) { + rings.add(i.next()); + } + return signWith(keyIds, rings); + } + + @Override + public Armor doNotSign() { + return new ArmorImpl(); + } + } + + class ArmorImpl implements Armor { + + @Override + public OutputStream asciiArmor() { + EncryptionBuilder.this.asciiArmor = true; + return build(); + } + + @Override + public OutputStream noArmor() { + EncryptionBuilder.this.asciiArmor = false; + return build(); + } + + private OutputStream build() { + return EncryptionStream.create( + EncryptionBuilder.this.outputStream, + EncryptionBuilder.this.encryptionKeys, + EncryptionBuilder.this.signingKeys, + EncryptionBuilder.this.symmetricKeyAlgorithm, + EncryptionBuilder.this.hashAlgorithm, + EncryptionBuilder.this.compressionAlgorithm, + EncryptionBuilder.this.asciiArmor); + } + } } diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java new file mode 100644 index 00000000..208c022c --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java @@ -0,0 +1,71 @@ +package de.vanitasvitae.crypto.pgpainless.encryption_signing; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.PublicKeyNotFoundException; +import de.vanitasvitae.crypto.pgpainless.SecretKeyNotFoundException; +import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; + +public interface EncryptionBuilderInterface { + + ToRecipients onOutputStream(OutputStream outputStream); + + interface ToRecipients { + + WithAlgorithms toRecipient(PGPPublicKey key); + + WithAlgorithms toRecipients(Set keys); + + WithAlgorithms toRecipients(Set keyIds, Set keyRings) + throws PublicKeyNotFoundException; + + WithAlgorithms toRecipients(Set keyIds, PGPPublicKeyRingCollection keys) + throws PublicKeyNotFoundException; + + SignWith doNotEncrypt(); + + } + + interface WithAlgorithms { + + WithAlgorithms andToSelf(Set keys); + + SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm, + HashAlgorithm hashAlgorithm, + CompressionAlgorithm compressionAlgorithm); + + } + + interface SignWith { + + Armor signWith(PGPSecretKey key); + + Armor signWith(Set keys); + + Armor signWith(Set keyIds, Set keyRings) throws SecretKeyNotFoundException; + + Armor signWith(Set keyIds, PGPSecretKeyRingCollection keys) throws SecretKeyNotFoundException; + + Armor doNotSign(); + + } + + interface Armor { + + OutputStream asciiArmor(); + + OutputStream noArmor(); + + } + +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionStream.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionStream.java new file mode 100644 index 00000000..48fc43ca --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionStream.java @@ -0,0 +1,75 @@ +package de.vanitasvitae.crypto.pgpainless.encryption_signing; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; +import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; + +public class EncryptionStream extends OutputStream { + + private final OutputStream outputStream; + private final Set encryptionKeys; + private final Set signingKeys; + private final SymmetricKeyAlgorithm symmetricKeyAlgorithm; + private final HashAlgorithm hashAlgorithm; + private final CompressionAlgorithm compressionAlgorithm; + private final boolean asciiArmor; + + private EncryptionStream(OutputStream outputStream, + Set encryptionKeys, + Set signingKeys, + SymmetricKeyAlgorithm symmetricKeyAlgorithm, + HashAlgorithm hashAlgorithm, + CompressionAlgorithm compressionAlgorithm, + boolean asciiArmor) { + this.outputStream = outputStream; + this.encryptionKeys = encryptionKeys; + this.signingKeys = signingKeys; + this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + this.compressionAlgorithm = compressionAlgorithm; + this.asciiArmor = asciiArmor; + } + + public static EncryptionStream create(OutputStream outputStream, + Set encryptionKeys, + Set signingKeys, + SymmetricKeyAlgorithm symmetricKeyAlgorithm, + HashAlgorithm hashAlgorithm, + CompressionAlgorithm compressionAlgorithm, + boolean asciiArmor) { + + requireNonNull(outputStream, "outputStream"); + requireNonNull(encryptionKeys, "encryptionKeys"); + requireNonNull(signingKeys, "signingKeys"); + requireNonNull(symmetricKeyAlgorithm, "symmetricKeyAlgorithm"); + requireNonNull(hashAlgorithm, "hashAlgorithm"); + requireNonNull(compressionAlgorithm, "compressionAlgorithm"); + + + + return new EncryptionStream(outputStream, + encryptionKeys, + signingKeys, + symmetricKeyAlgorithm, + hashAlgorithm, + compressionAlgorithm, + asciiArmor); + } + + @Override + public void write(int i) throws IOException { + + } + + private static void requireNonNull(Object o, String name) { + if (o == null) { + throw new IllegalArgumentException("Argument '" + name + "' MUST NOT be null."); + } + } +}