diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java index 2b7b1b2d..45d872eb 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -18,6 +18,7 @@ package org.pgpainless.decryption_verification; import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,13 +29,41 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.protection.UnprotectedKeysProtector; public interface DecryptionBuilderInterface { - DecryptWith onInputStream(InputStream inputStream); + /** + * Create a {@link DecryptionStream} on an {@link InputStream} which contains the encrypted and/or signed data. + * + * @param inputStream encrypted and/or signed data. + * @return api handle + */ + DecryptWith onInputStream(@Nonnull InputStream inputStream); interface DecryptWith { + /** + * Decrypt the encrypted data using the secret keys found in the provided {@link PGPSecretKeyRingCollection}. + * Here it is assumed that the secret keys are not password protected. + * For password protected secret keys use {@link #decryptWith(SecretKeyRingProtector, PGPSecretKeyRingCollection)} + * and pass in a {@link org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector}. + * + * @param secretKeyRings secret keys + * @return api handle + */ + default Verify decryptWith(@Nonnull PGPSecretKeyRingCollection secretKeyRings) { + return decryptWith(new UnprotectedKeysProtector(), secretKeyRings); + } + + /** + * Decrypt the encrypted data using the secret keys found in the provided {@link PGPSecretKeyRingCollection}. + * The secret keys are being unlocked by the provided {@link SecretKeyRingProtector}. + * + * @param decryptor for unlocking locked secret keys + * @param secretKeyRings secret keys + * @return api handle + */ Verify decryptWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRingCollection secretKeyRings); Verify doNotDecrypt(); @@ -43,33 +72,123 @@ public interface DecryptionBuilderInterface { interface Verify extends VerifyWith { - VerifyWith verifyDetachedSignature(InputStream inputStream) throws IOException, PGPException; + /** + * Pass in one or more detached signatures to verify. + * + * @param inputStream detached signature (ascii armored or binary). + * @return api handle + * @throws IOException in case something is wrong with the input stream + * @throws PGPException if the detached signatures are malformed + */ + VerifyWith verifyDetachedSignature(@Nonnull InputStream inputStream) throws IOException, PGPException; - VerifyWith verifyDetachedSignatures(List signatures); + /** + * Pass in a detached signature to verify. + * + * @param signature detached signature + * @return api handle + */ + default VerifyWith verifyDetachedSignature(@Nonnull PGPSignature signature) { + return verifyDetachedSignatures(Collections.singletonList(signature)); + } + /** + * Pass in a list of detached signatures to verify. + * + * @param signatures detached signatures + * @return api handle + */ + VerifyWith verifyDetachedSignatures(@Nonnull List signatures); + + /** + * Instruct the {@link DecryptionStream} to not verify any signatures. + * + * @return api handle + */ Build doNotVerify(); } interface VerifyWith { + /** + * Pass in a collection of public keys to verify the signatures with. + * + * @param publicKeyRings public keys + * @return api handle + */ HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRingCollection publicKeyRings); - HandleMissingPublicKeys verifyWith(@Nonnull Set trustedFingerprints, @Nonnull PGPPublicKeyRingCollection publicKeyRings); + /** + * Pass in a collection of public keys along with the fingerprint of the key that shall be used to + * verify the signatures. + * + * @param trustedFingerprint {@link OpenPgpV4Fingerprint} of the public key that shall be used to verify the signatures. + * @param publicKeyRings public keys + * @return api handle + */ + default HandleMissingPublicKeys verifyWith(@Nonnull OpenPgpV4Fingerprint trustedFingerprint, + @Nonnull PGPPublicKeyRingCollection publicKeyRings) { + return verifyWith(Collections.singleton(trustedFingerprint), publicKeyRings); + } + /** + * Pass in a collection of public keys along with a set of fingerprints of those keys that shall be used to + * verify the signatures. + * + * @param trustedFingerprints set of trusted {@link OpenPgpV4Fingerprint OpenPgpV4Fingerprints}. + * @param publicKeyRings public keys + * @return api handle + */ + HandleMissingPublicKeys verifyWith(@Nonnull Set trustedFingerprints, + @Nonnull PGPPublicKeyRingCollection publicKeyRings); + + /** + * Pass in a trusted public key ring to verify the signature with. + * + * @param publicKeyRing public key + * @return api handle + */ + default HandleMissingPublicKeys verifyWith(@Nonnull PGPPublicKeyRing publicKeyRing) { + return verifyWith(Collections.singleton(publicKeyRing)); + } + + /** + * Pass in a set of trusted public keys to verify the signatures with. + * + * @param publicKeyRings public keys + * @return api handle + */ HandleMissingPublicKeys verifyWith(@Nonnull Set publicKeyRings); - } interface HandleMissingPublicKeys { + /** + * Pass in a callback that can is used to request missing public keys. + * + * @param callback callback + * @return api handle + */ Build handleMissingPublicKeysWith(@Nonnull MissingPublicKeyCallback callback); + /** + * Instruct the {@link DecryptionStream} to ignore any missing public keys. + * + * @return api handle + */ Build ignoreMissingPublicKeys(); } interface Build { + /** + * Build the configured {@link DecryptionStream}. + * + * @return the decryption stream + * @throws IOException in case of an I/O error + * @throws PGPException if something is malformed + */ DecryptionStream build() throws IOException, PGPException; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilder.java index d00cb804..dc498f8b 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilder.java @@ -257,12 +257,12 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { } @Override - public Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKey... keys) { + public Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKey... keys) { return new SignWithImpl().signWith(decryptor, keys); } @Override - public Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKeyRing... keyRings) { + public Armor signWith(@org.jetbrains.annotations.NotNull SecretKeyRingProtector decryptor, @org.jetbrains.annotations.NotNull PGPSecretKeyRing... keyRings) { return new SignWithImpl().signWith(decryptor, keyRings); } @@ -275,13 +275,13 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { class SignWithImpl implements SignWith { @Override - public Armor signWith(@Nonnull SecretKeyRingProtector decryptor, + public Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKey... keys) { if (keys.length == 0) { throw new IllegalArgumentException("Recipient list MUST NOT be empty."); } for (PGPSecretKey s : keys) { - if (EncryptionBuilder.this.signingKeySelector().accept(null, s)) { + if (EncryptionBuilder.this.signingKeySelector().accept(null, s)) { signingKeys.add(s); } else { throw new IllegalArgumentException("Key " + s.getKeyID() + " is not a valid signing key."); @@ -292,7 +292,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { } @Override - public Armor signWith(@Nonnull SecretKeyRingProtector decryptor, + public Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing... keys) { if (keys.length == 0) { throw new IllegalArgumentException("Recipient list MUST NOT be empty."); @@ -300,7 +300,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { for (PGPSecretKeyRing key : keys) { for (Iterator i = key.getSecretKeys(); i.hasNext(); ) { PGPSecretKey s = i.next(); - if (EncryptionBuilder.this.signingKeySelector().accept(null, s)) { + if (EncryptionBuilder.this.signingKeySelector().accept(null, s)) { EncryptionBuilder.this.signingKeys.add(s); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilderInterface.java index ddd35c60..fcec8046 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/EncryptionBuilderInterface.java @@ -29,63 +29,195 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import org.pgpainless.decryption_verification.OpenPgpMetadata; import org.pgpainless.exception.SecretKeyNotFoundException; import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy; import org.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy; import org.pgpainless.util.MultiMap; public interface EncryptionBuilderInterface { + /** + * Create a {@link EncryptionStream} on an {@link OutputStream} that contains the plain data that + * shall be encrypted and or signed. + * + * @param outputStream output stream of the plain data. + * @return api handle + */ ToRecipients onOutputStream(@Nonnull OutputStream outputStream); interface ToRecipients { + /** + * Pass in a list of trusted public keys of the recipients. + * + * @param keys recipient keys for which the message will be encrypted. + * @return api handle + */ WithAlgorithms toRecipients(@Nonnull PGPPublicKey... keys); + /** + * Pass in a list of trusted public key rings of the recipients. + * + * @param keys recipient keys for which the message will be encrypted. + * @return api handle + */ WithAlgorithms toRecipients(@Nonnull PGPPublicKeyRing... keys); + /** + * Pass in a list of trusted public key ring collections of the recipients. + * + * @param keys recipient keys for which the message will be encrypted. + * @return api handle + */ WithAlgorithms toRecipients(@Nonnull PGPPublicKeyRingCollection... keys); + /** + * Pass in a map of recipient key ring collections along with a strategy for key selection. + * + * @param selectionStrategy selection strategy that is used to select suitable encryption keys. + * @param keys public keys + * @param selection criteria type (eg. email address) on which the selection strategy is based + * @return api handle + */ WithAlgorithms toRecipients(@Nonnull PublicKeyRingSelectionStrategy selectionStrategy, @Nonnull MultiMap keys); + /** + * Instruct the {@link EncryptionStream} to not encrypt any data. + * + * @return api handle + */ DetachedSign doNotEncrypt(); } interface WithAlgorithms { + /** + * Add our own public key to the list of recipient keys. + * + * @param keys own public keys + * @return api handle + */ WithAlgorithms andToSelf(@Nonnull PGPPublicKey... keys); + /** + * Add our own public key to the list of recipient keys. + * + * @param keys own public keys + * @return api handle + */ WithAlgorithms andToSelf(@Nonnull PGPPublicKeyRing... keys); + /** + * Add our own public keys to the list of recipient keys. + * + * @param keys own public keys + * @return api handle + */ WithAlgorithms andToSelf(@Nonnull PGPPublicKeyRingCollection keys); + /** + * Add our own public keys to the list of recipient keys. + * + * @param selectionStrategy key selection strategy used to determine suitable keys for encryption. + * @param keys public keys + * @param selection criteria type (eg. email address) used by the selection strategy. + * @return api handle + */ WithAlgorithms andToSelf(@Nonnull PublicKeyRingSelectionStrategy selectionStrategy, @Nonnull MultiMap keys); + /** + * Specify which algorithms should be used for the encryption. + * + * @param symmetricKeyAlgorithm symmetric algorithm for the session key + * @param hashAlgorithm hash algorithm + * @param compressionAlgorithm compression algorithm + * @return api handle + */ DetachedSign usingAlgorithms(@Nonnull SymmetricKeyAlgorithm symmetricKeyAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, @Nonnull CompressionAlgorithm compressionAlgorithm); + /** + * Use a suite of algorithms that are considered secure. + * + * @return api handle + */ DetachedSign usingSecureAlgorithms(); } interface DetachedSign extends SignWith { + + /** + * Instruct the {@link EncryptionStream} to generate detached signatures instead of One-Pass-Signatures. + * Those can be retrieved later via {@link OpenPgpMetadata#getSignatures()}. + * + * @return api handle + */ SignWith createDetachedSignature(); + /** + * Do not sign the plain data at all. + * + * @return api handle + */ Armor doNotSign(); } interface SignWith { - Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKey... keys); + /** + * Pass in a list of secret keys used for signing. + * Those keys are considered unlocked (ie. not password protected). + * If you need to use password protected keys instead, use {@link #signWith(SecretKeyRingProtector, PGPSecretKey...)}. + * + * @param keys secret keys + * @return api handle + */ + default Armor signWith(@Nonnull PGPSecretKey... keys) { + return signWith(new UnprotectedKeysProtector(), keys); + } - Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing... keyRings); + /** + * Pass in a list of secret keys used for signing, along with a {@link SecretKeyRingProtector} used to unlock + * the secret keys. + * + * @param decryptor {@link SecretKeyRingProtector} used to unlock the secret keys + * @param keys secret keys used for signing + * @return api handle + */ + Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKey... keys); + /** + * Pass in a list of secret keys used for signing, along with a {@link SecretKeyRingProtector} used to unlock + * the secret keys. + * + * @param decryptor {@link SecretKeyRingProtector} used to unlock the secret keys + * @param keyRings secret keys used for signing + * @return api handle + */ + Armor signWith(@Nonnull SecretKeyRingProtector decryptor, @Nonnull PGPSecretKeyRing... keyRings); + + /** + * Pass in a map of secret keys for signing, as well as a {@link org.pgpainless.key.selection.key.SecretKeySelectionStrategy} + * that is used to determine suitable secret keys. + * If the keys are locked by a password, the provided {@link SecretKeyRingProtector} will be used to unlock the keys. + * + * @param selectionStrategy key selection strategy + * @param decryptor decryptor for unlocking secret keys + * @param keys secret keys + * @param selection criteria type (eg. email address) + * @return api handle + * + * @throws SecretKeyNotFoundException in case no suitable secret key can be found + */ Armor signWith(@Nonnull SecretKeyRingSelectionStrategy selectionStrategy, @Nonnull SecretKeyRingProtector decryptor, @Nonnull MultiMap keys) @@ -95,8 +227,23 @@ public interface EncryptionBuilderInterface { interface Armor { + /** + * Wrap the encrypted/signed output in an ASCII armor. + * This can come in handy for sending the encrypted message via eg. email. + * + * @return encryption stream + * @throws IOException in case some I/O error occurs + * @throws PGPException in case of some malformed pgp data + */ EncryptionStream asciiArmor() throws IOException, PGPException; + /** + * Do not wrap the output in an ASCII armor. + * + * @return encryption stream + * @throws IOException in case some I/O error occurs + * @throws PGPException in case of some malformed pgp data + */ EncryptionStream noArmor() throws IOException, PGPException; }