diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java index a51b52db..ef4b8e2c 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/Main.java @@ -15,21 +15,16 @@ import java.util.Collections; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; -import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor; +import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import de.vanitasvitae.crypto.pgpainless.key.generation.type.length.RsaLength; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPObjectFactory; -import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; -import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.io.Streams; public class Main { @@ -43,7 +38,7 @@ public class Main { PGPSecretKeyRing a = PGPainless.generateKeyRing().simpleRsaKeyRing("a@b.c", RsaLength._2048); PGPSecretKeyRing b = PGPainless.generateKeyRing().simpleRsaKeyRing("b@c.d", RsaLength._2048); - SecretKeyRingDecryptor secretKeyRingDecryptor = new SecretKeyRingDecryptor() { + SecretKeyRingProtector secretKeyRingDecryptor = new SecretKeyRingProtector() { @Override public PBESecretKeyDecryptor getDecryptor(Long keyId) { return null; @@ -62,7 +57,7 @@ public class Main { OutputStream encryptor = PGPainless.createEncryptor().onOutputStream(toEncrypted) .toRecipient(b.getPublicKey()) .usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED) - .signWith(a.getSecretKey(), secretKeyRingDecryptor) + .signWith(a, secretKeyRingDecryptor) .asciiArmor(); Streams.pipeAll(fromPlain, encryptor); @@ -94,7 +89,7 @@ public class Main { System.out.println(new String(toPlain.toByteArray())); } - private static void gpg(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingDecryptor secretKeyRingDecryptor) + private static void gpg(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingProtector secretKeyRingDecryptor) throws IOException, PGPException { String gpg = "-----BEGIN PGP MESSAGE-----\n" + "\n" + @@ -132,7 +127,7 @@ public class Main { } - public static void symm(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingDecryptor secretKeyRingDecryptor) + public static void symm(PGPSecretKeyRing a, PGPSecretKeyRing b, SecretKeyRingProtector secretKeyRingDecryptor) throws IOException, PGPException { byte[] bytes = "Diese Nachricht ist streng geheim!!!".getBytes(Charset.forName("UTF-8")); ByteArrayInputStream fromPlain = new ByteArrayInputStream(bytes); @@ -142,7 +137,7 @@ public class Main { .onOutputStream(toEncrypted) .toRecipient(b.getPublicKey()) .usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED) - .signWith(a.getSecretKey(), secretKeyRingDecryptor) + .signWith(a, secretKeyRingDecryptor) .noArmor(); Streams.pipeAll(fromPlain, encryptor); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java index 2292efbc..c6e74650 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/PublicKeyNotFoundException.java @@ -1,16 +1,22 @@ package de.vanitasvitae.crypto.pgpainless; +import org.bouncycastle.openpgp.PGPException; + public class PublicKeyNotFoundException extends Exception { private static final long serialVersionUID = 1L; - private final long keyId; + private long keyId; public PublicKeyNotFoundException(long keyId) { super("No PGPPublicKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); this.keyId = keyId; } + public PublicKeyNotFoundException(PGPException e) { + + } + 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 index 49ea9b8a..cee61f5a 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/SecretKeyNotFoundException.java @@ -4,7 +4,7 @@ public class SecretKeyNotFoundException extends Exception { private static final long serialVersionUID = 1L; - private final long keyId; + private long keyId; public SecretKeyNotFoundException(long keyId) { super("No PGPSecretKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java index d2a64875..72280036 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilder.java @@ -7,7 +7,7 @@ import java.util.Iterator; import java.util.Set; import de.vanitasvitae.crypto.pgpainless.PainlessResult; -import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor; +import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -17,7 +17,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { private InputStream inputStream; private PGPSecretKeyRingCollection decryptionKeys; - private SecretKeyRingDecryptor decryptionKeyDecryptor; + private SecretKeyRingProtector decryptionKeyDecryptor; private Set verificationKeys = new HashSet<>(); private Set trustedKeyIds = new HashSet<>(); private MissingPublicKeyCallback missingPublicKeyCallback = null; @@ -31,7 +31,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface { class DecryptWithImpl implements DecryptWith { @Override - public VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor) { + public VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingProtector decryptor) { DecryptionBuilder.this.decryptionKeys = secretKeyRings; DecryptionBuilder.this.decryptionKeyDecryptor = decryptor; return new VerifyWithImpl(); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java index b2b152c2..b9ef0939 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -5,7 +5,7 @@ import java.io.InputStream; import java.util.Set; import de.vanitasvitae.crypto.pgpainless.PainlessResult; -import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor; +import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -17,7 +17,7 @@ public interface DecryptionBuilderInterface { interface DecryptWith { - VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor); + VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingProtector decryptor); VerifyWith doNotDecrypt(); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/InputStreamFactory.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/InputStreamFactory.java index 59352fbb..72e6f541 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/InputStreamFactory.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/decryption_verification/InputStreamFactory.java @@ -13,7 +13,7 @@ import de.vanitasvitae.crypto.pgpainless.PainlessResult; import de.vanitasvitae.crypto.pgpainless.PainlessStream; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; -import de.vanitasvitae.crypto.pgpainless.encryption_signing.SecretKeyRingDecryptor; +import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPException; @@ -40,7 +40,7 @@ public class InputStreamFactory { private InputStream inputStream; private final PGPSecretKeyRingCollection decryptionKeys; - private final SecretKeyRingDecryptor decryptionKeyDecryptor; + private final SecretKeyRingProtector decryptionKeyDecryptor; private final Set verificationKeys = new HashSet<>(); private final Set trustedKeyIds = new HashSet<>(); private final MissingPublicKeyCallback missingPublicKeyCallback; @@ -50,7 +50,7 @@ public class InputStreamFactory { private final Map verifiableOnePassSignatures = new HashMap<>(); private InputStreamFactory(PGPSecretKeyRingCollection decryptionKeys, - SecretKeyRingDecryptor decryptor, + SecretKeyRingProtector decryptor, Set verificationKeys, Set trustedKeyIds, MissingPublicKeyCallback missingPublicKeyCallback) @@ -64,7 +64,7 @@ public class InputStreamFactory { public static PainlessResult.ResultAndInputStream create(InputStream inputStream, PGPSecretKeyRingCollection decryptionKeys, - SecretKeyRingDecryptor decryptor, + SecretKeyRingProtector decryptor, Set verificationKeys, Set trustedKeyIds, MissingPublicKeyCallback missingPublicKeyCallback) 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 fda45e05..7d5549c0 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 @@ -2,6 +2,7 @@ package de.vanitasvitae.crypto.pgpainless.encryption_signing; import java.io.IOException; import java.io.OutputStream; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -11,6 +12,7 @@ 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 de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; @@ -25,7 +27,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { private OutputStream outputStream; private final Set encryptionKeys = new HashSet<>(); private final Set signingKeys = new HashSet<>(); - private SecretKeyRingDecryptor signingKeysDecryptor; + private SecretKeyRingProtector signingKeysDecryptor; private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128; private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256; private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED; @@ -41,52 +43,45 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { @Override public WithAlgorithms toRecipient(PGPPublicKey key) { + if (!key.isEncryptionKey()) { + throw new IllegalStateException("Public Key " + Long.toHexString(key.getKeyID()) + " is not capable of encryption."); + } EncryptionBuilder.this.encryptionKeys.add(key); return new WithAlgorithmsImpl(); } @Override - public WithAlgorithms toRecipients(Set keys) { - EncryptionBuilder.this.encryptionKeys.addAll(keys); + public WithAlgorithms toRecipients(Set keys) { + for (PGPPublicKeyRing ring : keys) { + for (PGPPublicKey k : ring) { + if (k.isEncryptionKey()) { + EncryptionBuilder.this.encryptionKeys.add(k); + } + } + } return new WithAlgorithmsImpl(); } @Override - public WithAlgorithms toRecipients(Set keyIds, Set keyRings) + public WithAlgorithms toRecipients(Set keyIds, Set keys) 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()); + for (PGPPublicKeyRingCollection collection : keys) { + for (long keyId : keyIds) { + try { + PGPPublicKeyRing ring = collection.getPublicKeyRing(keyId); + if (ring != null) { + rings.add(ring); + keyIds.remove(keyId); + } + } catch (PGPException e) { + throw new PublicKeyNotFoundException(e); + } + } } - return toRecipients(keyIds, rings); + return toRecipients(rings); } @Override @@ -98,8 +93,21 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { class WithAlgorithmsImpl implements WithAlgorithms { @Override - public WithAlgorithms andToSelf(Set keys) { - EncryptionBuilder.this.encryptionKeys.addAll(keys); + public WithAlgorithms andToSelf(PGPPublicKey key) { + EncryptionBuilder.this.encryptionKeys.add(key); + return this; + } + + @Override + public WithAlgorithms andToSelf(Set keyRings) { + for (PGPPublicKeyRing ring : keyRings) { + for (Iterator i = ring.getPublicKeys(); i.hasNext(); ) { + PGPPublicKey key = i.next(); + if (key.isEncryptionKey()) { + EncryptionBuilder.this.encryptionKeys.add(key); + } + } + } return this; } @@ -114,59 +122,56 @@ public class EncryptionBuilder implements EncryptionBuilderInterface { return new SignWithImpl(); } + + @Override + public SignWith usingSecureAlgorithms() { + EncryptionBuilder.this.symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_256; + EncryptionBuilder.this.hashAlgorithm = HashAlgorithm.SHA512; + EncryptionBuilder.this.compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED; + + return new SignWithImpl(); + } } class SignWithImpl implements SignWith { @Override - public Armor signWith(PGPSecretKey key, SecretKeyRingDecryptor decryptor) { - EncryptionBuilder.this.signingKeys.add(key); - EncryptionBuilder.this.signingKeysDecryptor = decryptor; - return new ArmorImpl(); + public Armor signWith(PGPSecretKeyRing key, SecretKeyRingProtector decryptor) { + return signWith(Collections.singleton(key), decryptor); } @Override - public Armor signWith(Set keys, SecretKeyRingDecryptor decryptor) { - EncryptionBuilder.this.signingKeys.addAll(keys); - EncryptionBuilder.this.signingKeysDecryptor = decryptor; - return new ArmorImpl(); - } - - @Override - public Armor signWith(Set keyIds, Set keyRings, SecretKeyRingDecryptor decryptor) - 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 + public Armor signWith(Set keys, SecretKeyRingProtector decryptor) { + for (PGPSecretKeyRing key : keys) { + for (Iterator i = key.getSecretKeys(); i.hasNext(); ) { + PGPSecretKey s = i.next(); + if (s.isSigningKey()) { + EncryptionBuilder.this.signingKeys.add(s); } } - - if (key == null) { - throw new SecretKeyNotFoundException(id); - } - - keys.add(key); } - return signWith(keys, decryptor); + EncryptionBuilder.this.signingKeysDecryptor = decryptor; + return new ArmorImpl(); } @Override - public Armor signWith(Set keyIds, PGPSecretKeyRingCollection keys, SecretKeyRingDecryptor decryptor) + public Armor signWith(Set keyIds, Set keyRings, SecretKeyRingProtector decryptor) throws SecretKeyNotFoundException { - Set rings = new HashSet<>(); - - for (Iterator i = keys.getKeyRings(); i.hasNext();) { - rings.add(i.next()); + for (PGPSecretKeyRingCollection collection : keyRings) { + for (long keyId : keyIds) { + try { + PGPSecretKeyRing ring = collection.getSecretKeyRing(keyId); + if (ring != null) { + rings.add(ring); + keyIds.remove(keyId); + } + } catch (PGPException e) { + throw new SecretKeyNotFoundException(keyId); + } + } } - return signWith(keyIds, rings, decryptor); + return signWith(rings, decryptor); } @Override 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 index e59d10a9..8cb75685 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/EncryptionBuilderInterface.java @@ -1,7 +1,6 @@ package de.vanitasvitae.crypto.pgpainless.encryption_signing; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.Set; @@ -10,11 +9,11 @@ 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 de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector; import org.bouncycastle.openpgp.PGPException; 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; @@ -26,12 +25,9 @@ public interface EncryptionBuilderInterface { WithAlgorithms toRecipient(PGPPublicKey key); - WithAlgorithms toRecipients(Set keys); + WithAlgorithms toRecipients(Set keys); - WithAlgorithms toRecipients(Set keyIds, Set keyRings) - throws PublicKeyNotFoundException; - - WithAlgorithms toRecipients(Set keyIds, PGPPublicKeyRingCollection keys) + WithAlgorithms toRecipients(Set keyIds, Set keys) throws PublicKeyNotFoundException; SignWith doNotEncrypt(); @@ -40,24 +36,26 @@ public interface EncryptionBuilderInterface { interface WithAlgorithms { - WithAlgorithms andToSelf(Set keys); + WithAlgorithms andToSelf(PGPPublicKey key); + + WithAlgorithms andToSelf(Set keys); SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm, HashAlgorithm hashAlgorithm, CompressionAlgorithm compressionAlgorithm); + SignWith usingSecureAlgorithms(); + } interface SignWith { - Armor signWith(PGPSecretKey key, SecretKeyRingDecryptor decryptor); + Armor signWith(PGPSecretKeyRing key, SecretKeyRingProtector decryptor); - Armor signWith(Set keys, SecretKeyRingDecryptor decryptor); - - Armor signWith(Set keyIds, Set keyRings, SecretKeyRingDecryptor decryptor) + Armor signWith(Set keyRings, SecretKeyRingProtector decryptor) throws SecretKeyNotFoundException; - Armor signWith(Set keyIds, PGPSecretKeyRingCollection keys, SecretKeyRingDecryptor decryptor) + Armor signWith(Set keyIds, Set keys, SecretKeyRingProtector decryptor) throws SecretKeyNotFoundException; Armor doNotSign(); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/SecretKeyRingDecryptor.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/SecretKeyRingProtector.java similarity index 69% rename from src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/SecretKeyRingDecryptor.java rename to src/main/java/de/vanitasvitae/crypto/pgpainless/key/SecretKeyRingProtector.java index 5f9a4204..cc9d445f 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/encryption_signing/SecretKeyRingDecryptor.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/SecretKeyRingProtector.java @@ -1,9 +1,9 @@ -package de.vanitasvitae.crypto.pgpainless.encryption_signing; +package de.vanitasvitae.crypto.pgpainless.key; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; -public interface SecretKeyRingDecryptor { +public interface SecretKeyRingProtector { PBESecretKeyDecryptor getDecryptor(Long keyId); diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/UnprotectedKeysProtector.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/UnprotectedKeysProtector.java new file mode 100644 index 00000000..d5828abd --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/UnprotectedKeysProtector.java @@ -0,0 +1,19 @@ +package de.vanitasvitae.crypto.pgpainless.key; + +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; + +/** + * Implementation of the {@link SecretKeyRingProtector} which assumes that all handled keys are not password protected. + */ +public class UnprotectedKeysProtector implements SecretKeyRingProtector { + @Override + public PBESecretKeyDecryptor getDecryptor(Long keyId) { + return null; + } + + @Override + public PBESecretKeyEncryptor getEncryptor(Long keyId) { + return null; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java index 4dc1c50d..69a6b59b 100644 --- a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/generation/KeyRingBuilder.java @@ -31,6 +31,7 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair; diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/KeySelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/KeySelectionStrategy.java new file mode 100644 index 00000000..ce9f4aef --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/KeySelectionStrategy.java @@ -0,0 +1,21 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key; + +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; + +/** + * Interface that describes a selection strategy for OpenPGP keys. + * @param Type of the Key + * @param Type of the KeyRing + * @param Type that describes the owner of this key + */ +public interface KeySelectionStrategy { + + boolean accept(O identifier, K key); + + Set selectKeysFromKeyRing(O identifier, R ring); + + MultiMap selectKeysFromKeyRings(MultiMap rings); + +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/PublicKeySelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/PublicKeySelectionStrategy.java new file mode 100644 index 00000000..5c36717a --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/PublicKeySelectionStrategy.java @@ -0,0 +1,39 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; + +/** + * Key Selection Strategy which accepts {@link PGPPublicKey}s that are accepted by the abstract method + * {@link #accept(Object, Object)}. + * + * @param Type that describes the owner of the key. + */ +public abstract class PublicKeySelectionStrategy implements KeySelectionStrategy { + + @Override + public Set selectKeysFromKeyRing(O identifier, PGPPublicKeyRing ring) { + Set keys = new HashSet<>(); + for (Iterator i = ring.getPublicKeys(); i.hasNext(); ) { + PGPPublicKey key = i.next(); + if (accept(identifier, key)) keys.add(key); + } + return keys; + } + + @Override + public MultiMap selectKeysFromKeyRings(MultiMap keyRings) { + MultiMap keys = new MultiMap<>(); + for (O identifier : keyRings.keySet()) { + for (PGPPublicKeyRing ring : keyRings.get(identifier)) { + keys.put(identifier, selectKeysFromKeyRing(identifier, ring)); + } + } + return keys; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/SecretKeySelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/SecretKeySelectionStrategy.java new file mode 100644 index 00000000..e364f5d1 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/SecretKeySelectionStrategy.java @@ -0,0 +1,39 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; + +/** + * Key Selection Strategy which accepts {@link PGPSecretKey}s that are accepted by the abstract method + * {@link #accept(Object, Object)}. + * + * @param Type that describes the owner of the key. + */ +public abstract class SecretKeySelectionStrategy implements KeySelectionStrategy { + + @Override + public Set selectKeysFromKeyRing(O identifier, PGPSecretKeyRing ring) { + Set keys = new HashSet<>(); + for (Iterator i = ring.getSecretKeys(); i.hasNext(); ) { + PGPSecretKey key = i.next(); + if (accept(identifier, key)) keys.add(key); + } + return keys; + } + + @Override + public MultiMap selectKeysFromKeyRings(MultiMap keyRings) { + MultiMap keys = new MultiMap<>(); + for (O identifier : keyRings.keySet()) { + for (PGPSecretKeyRing ring : keyRings.get(identifier)) { + keys.put(identifier, selectKeysFromKeyRing(identifier, ring)); + } + } + return keys; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/EncryptionKeySelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/EncryptionKeySelectionStrategy.java new file mode 100644 index 00000000..48205939 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/EncryptionKeySelectionStrategy.java @@ -0,0 +1,17 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key.impl; + +import de.vanitasvitae.crypto.pgpainless.key.selection.key.PublicKeySelectionStrategy; +import org.bouncycastle.openpgp.PGPPublicKey; + +/** + * Key Selection Strategy that only accepts {@link PGPPublicKey}s which are capable of encryption. + * + * @param Type that describes the owner of the key (not used for decision). + */ +public class EncryptionKeySelectionStrategy extends PublicKeySelectionStrategy { + + @Override + public boolean accept(O identifier, PGPPublicKey key) { + return key.isEncryptionKey(); + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/NoRevocation.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/NoRevocation.java new file mode 100644 index 00000000..a1310eb3 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/NoRevocation.java @@ -0,0 +1,38 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key.impl; + +import de.vanitasvitae.crypto.pgpainless.key.selection.key.PublicKeySelectionStrategy; +import de.vanitasvitae.crypto.pgpainless.key.selection.key.SecretKeySelectionStrategy; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; + +/** + * Key Selection Strategies that do accept only keys, which have no revocation. + */ +public class NoRevocation { + + /** + * Key Selection Strategy which only accepts {@link PGPPublicKey}s which have no revocation. + * + * @param Type that describes the owner of this key (not used for this decision). + */ + public static class PubKeySelectionStrategy extends PublicKeySelectionStrategy { + + @Override + public boolean accept(O identifier, PGPPublicKey key) { + return !key.hasRevocation(); + } + } + + /** + * Key Selection Strategy which only accepts {@link PGPSecretKey}s which have no revocation. + * + * @param Type that describes the owner of this key (not used for this decision). + */ + public static class SecKeySelectionStrategy extends SecretKeySelectionStrategy { + + @Override + public boolean accept(O identifier, PGPSecretKey key) { + return !key.getPublicKey().hasRevocation(); + } + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/SignatureKeySelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/SignatureKeySelectionStrategy.java new file mode 100644 index 00000000..27ca0a95 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/key/impl/SignatureKeySelectionStrategy.java @@ -0,0 +1,18 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.key.impl; + +import de.vanitasvitae.crypto.pgpainless.key.selection.key.SecretKeySelectionStrategy; +import org.bouncycastle.openpgp.PGPSecretKey; + +/** + * Key Selection Strategy that only accepts {@link PGPSecretKey}s which are capable of signing. + * + * @param Type that describes the owner of the key (not used for this decision). + */ +public class SignatureKeySelectionStrategy extends SecretKeySelectionStrategy { + + @Override + public boolean accept(O identifier, PGPSecretKey key) { + return key.isSigningKey(); + } + +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/KeyRingSelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/KeyRingSelectionStrategy.java new file mode 100644 index 00000000..9daf2eed --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/KeyRingSelectionStrategy.java @@ -0,0 +1,14 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring; + +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; + +public interface KeyRingSelectionStrategy { + + boolean accept(O identifier, R keyRing); + + Set selectKeyRingsFromCollection(O identifier, C keyRingCollection); + + MultiMap selectKeyRingsFromCollections(MultiMap keyRingCollections); +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/PublicKeyRingSelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/PublicKeyRingSelectionStrategy.java new file mode 100644 index 00000000..fca0760f --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/PublicKeyRingSelectionStrategy.java @@ -0,0 +1,33 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; + +public abstract class PublicKeyRingSelectionStrategy implements KeyRingSelectionStrategy { + + @Override + public Set selectKeyRingsFromCollection(O identifier, PGPPublicKeyRingCollection keyRingCollection) { + Set accepted = new HashSet<>(); + for (Iterator i = keyRingCollection.getKeyRings(); i.hasNext(); ) { + PGPPublicKeyRing ring = i.next(); + if (accept(identifier, ring)) accepted.add(ring); + } + return accepted; + } + + @Override + public MultiMap selectKeyRingsFromCollections(MultiMap keyRingCollections) { + MultiMap keyRings = new MultiMap<>(); + for (O identifier : keyRingCollections.keySet()) { + for (PGPPublicKeyRingCollection collection : keyRingCollections.get(identifier)) { + keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection)); + } + } + return keyRings; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/SecretKeyRingSelectionStrategy.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/SecretKeyRingSelectionStrategy.java new file mode 100644 index 00000000..73b25995 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/SecretKeyRingSelectionStrategy.java @@ -0,0 +1,32 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; + +public abstract class SecretKeyRingSelectionStrategy implements KeyRingSelectionStrategy { + @Override + public Set selectKeyRingsFromCollection(O identifier, PGPSecretKeyRingCollection keyRingCollection) { + Set accepted = new HashSet<>(); + for (Iterator i = keyRingCollection.getKeyRings(); i.hasNext(); ) { + PGPSecretKeyRing ring = i.next(); + if (accept(identifier, ring)) accepted.add(ring); + } + return accepted; + } + + @Override + public MultiMap selectKeyRingsFromCollections(MultiMap keyRingCollections) { + MultiMap keyRings = new MultiMap<>(); + for (O identifier : keyRingCollections.keySet()) { + for (PGPSecretKeyRingCollection collection : keyRingCollections.get(identifier)) { + keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection)); + } + } + return keyRings; + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Email.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Email.java new file mode 100644 index 00000000..68357b63 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Email.java @@ -0,0 +1,31 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring.impl; + +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; + +public class Email { + + public static class PubRingSelectionStrategy extends PartialUserId.PubRingSelectionStrategy { + + @Override + public boolean accept(String email, PGPPublicKey key) { + // Ensure, that email address is encapsulated in "<",">" + if (!email.matches("^<.+>$")) { + email = "<" + email + ">"; + } + return super.accept(email, key); + } + } + + public static class SecRingSelectionStrategy extends PartialUserId.SecRingSelectionStrategy { + + @Override + public boolean accept(String email, PGPSecretKey key) { + // Ensure, that email address is encapsulated in "<",">" + if (!email.matches("^<.+>$")) { + email = "<" + email + ">"; + } + return super.accept(email, key); + } + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/ExactUserId.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/ExactUserId.java new file mode 100644 index 00000000..636bf9fe --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/ExactUserId.java @@ -0,0 +1,35 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring.impl; + +import java.util.Iterator; + +import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy; +import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; + +public class ExactUserId { + + public static class PubRingSelectionStrategy extends PublicKeyRingSelectionStrategy { + + @Override + public boolean accept(String identifier, PGPPublicKeyRing keyRing) { + Iterator userIds = keyRing.getPublicKey().getUserIDs(); + while (userIds.hasNext()) { + if (userIds.next().equals(identifier)) return true; + } + return false; + } + } + + public static class SecRingSelectionStrategy extends SecretKeyRingSelectionStrategy { + + @Override + public boolean accept(String identifier, PGPSecretKeyRing keyRing) { + Iterator userIds = keyRing.getPublicKey().getUserIDs(); + while (userIds.hasNext()) { + if (userIds.next().equals(identifier)) return true; + } + return false; + } + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/PartialUserId.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/PartialUserId.java new file mode 100644 index 00000000..ddc55a20 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/PartialUserId.java @@ -0,0 +1,39 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring.impl; + +import java.util.Iterator; + +import de.vanitasvitae.crypto.pgpainless.key.selection.key.PublicKeySelectionStrategy; +import de.vanitasvitae.crypto.pgpainless.key.selection.key.SecretKeySelectionStrategy; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; + +public class PartialUserId { + + public static class PubRingSelectionStrategy extends PublicKeySelectionStrategy { + + @Override + public boolean accept(String identifier, PGPPublicKey key) { + for (Iterator userIds = key.getUserIDs(); userIds.hasNext(); ) { + String userId = userIds.next(); + if (userId.contains(identifier)) { + return true; + } + } + return false; + } + } + + public static class SecRingSelectionStrategy extends SecretKeySelectionStrategy { + + @Override + public boolean accept(String identifier, PGPSecretKey key) { + for (Iterator userIds = key.getUserIDs(); userIds.hasNext(); ) { + String userId = (String) userIds.next(); + if (userId.contains(identifier)) { + return true; + } + } + return false; + } + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Whitelist.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Whitelist.java new file mode 100644 index 00000000..b2838fc7 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/Whitelist.java @@ -0,0 +1,62 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring.impl; + +import java.util.Map; +import java.util.Set; + +import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy; +import de.vanitasvitae.crypto.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy; +import de.vanitasvitae.crypto.pgpainless.util.MultiMap; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; + +public class Whitelist { + + public static class PubRingSelectionStrategy extends PublicKeyRingSelectionStrategy { + + private final MultiMap whitelist; + + public PubRingSelectionStrategy(MultiMap whitelist) { + this.whitelist = whitelist; + } + + public PubRingSelectionStrategy(Map> whitelist) { + this.whitelist = new MultiMap<>(whitelist); + } + + @Override + public boolean accept(O identifier, PGPPublicKeyRing keyRing) { + Set whitelistedKeyIds = whitelist.get(identifier); + + if (whitelistedKeyIds == null) { + return false; + } + + return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID()); + } + } + + public static class SecRingSelectionStrategy extends SecretKeyRingSelectionStrategy { + + private final MultiMap whitelist; + + public SecRingSelectionStrategy(MultiMap whitelist) { + this.whitelist = whitelist; + } + + public SecRingSelectionStrategy(Map> whitelist) { + this.whitelist = new MultiMap<>(whitelist); + } + + @Override + public boolean accept(O identifier, PGPSecretKeyRing keyRing) { + Set whitelistedKeyIds = whitelist.get(identifier); + + if (whitelistedKeyIds == null) { + return false; + } + + return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID()); + } + + } +} \ No newline at end of file diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/XMPP.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/XMPP.java new file mode 100644 index 00000000..4062a4fa --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/key/selection/keyring/impl/XMPP.java @@ -0,0 +1,23 @@ +package de.vanitasvitae.crypto.pgpainless.key.selection.keyring.impl; + +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; + +public class XMPP { + + public static class PubRingSelectionStrategy extends ExactUserId.PubRingSelectionStrategy { + + @Override + public boolean accept(String jid, PGPPublicKeyRing keyRing) { + return super.accept("xmpp:" + jid, keyRing); + } + } + + public static class SecRingSelectionStrategy extends ExactUserId.SecRingSelectionStrategy { + + @Override + public boolean accept(String jid, PGPSecretKeyRing keyRing) { + return super.accept("xmpp:" + jid, keyRing); + } + } +} diff --git a/src/main/java/de/vanitasvitae/crypto/pgpainless/util/MultiMap.java b/src/main/java/de/vanitasvitae/crypto/pgpainless/util/MultiMap.java new file mode 100644 index 00000000..52e090a3 --- /dev/null +++ b/src/main/java/de/vanitasvitae/crypto/pgpainless/util/MultiMap.java @@ -0,0 +1,120 @@ +package de.vanitasvitae.crypto.pgpainless.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MultiMap { + + private final Map> map; + + public MultiMap() { + map = new HashMap<>(); + } + + public MultiMap(MultiMap other) { + this.map = new HashMap<>(); + for (K k : other.map.keySet()) { + map.put(k, new HashSet<>(other.map.get(k))); + } + } + + public MultiMap(Map> content) { + this.map = new HashMap<>(content); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public boolean containsKey(Object o) { + return map.containsKey(o); + } + + public boolean containsValue(Object o) { + for (Set values : map.values()) { + if (values.contains(o)) return true; + } + return false; + } + + public Set get(Object o) { + return map.get(o); + } + + public void put(K k, V v) { + Set values = map.get(k); + if (values == null) { + values = new HashSet<>(); + map.put(k, values); + } + values.add(v); + } + + public void put(K k, Set vs) { + for (V v : vs) { + put(k, v); + } + } + + public void remove(Object o) { + for (Set values : map.values()) { + values.remove(o); + } + } + + public void putAll(Map> _map) { + for (K key : _map.keySet()) { + Set vs = this.map.get(key); + if (vs == null) { + vs = new HashSet<>(); + this.map.put(key, vs); + } + vs.addAll(_map.get(key)); + } + } + + public void clear() { + map.clear(); + } + + public Set keySet() { + return map.keySet(); + } + + public Collection> values() { + return map.values(); + } + + public Set>> entrySet() { + return map.entrySet(); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + + if (!(o instanceof MultiMap)) { + return false; + } + + if (this == o) { + return true; + } + + return map.equals(((MultiMap) o).map); + } + + @Override + public int hashCode() { + return map.hashCode(); + } +} diff --git a/src/test/java/de/vanitasvitae/crypto/pgpainless/EncryptDecryptTest.java b/src/test/java/de/vanitasvitae/crypto/pgpainless/EncryptDecryptTest.java new file mode 100644 index 00000000..d8bab55c --- /dev/null +++ b/src/test/java/de/vanitasvitae/crypto/pgpainless/EncryptDecryptTest.java @@ -0,0 +1,142 @@ +package de.vanitasvitae.crypto.pgpainless; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Security; +import java.util.Arrays; +import java.util.Collections; + +import de.vanitasvitae.crypto.pgpainless.key.UnprotectedKeysProtector; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.util.io.Streams; +import org.junit.Test; + +public class EncryptDecryptTest { + PGPSecretKeyRing juliet = new PGPSecretKeyRing(PGPUtil.getDecoderStream(new ByteArrayInputStream(TestKeys.JULIET_PRIV.getBytes())), new BcKeyFingerprintCalculator()); + PGPSecretKeyRing romeo = new PGPSecretKeyRing(PGPUtil.getDecoderStream(new ByteArrayInputStream(TestKeys.ROMEO_PRIV.getBytes())), new BcKeyFingerprintCalculator()); + + public EncryptDecryptTest() throws IOException, PGPException { + + } + + @Test + public void keyIdTest() { + assertEquals("b4b509cb5936e03e", Long.toHexString(juliet.getSecretKey().getKeyID())); + } + + @Test + public void test() throws IOException, PGPException { + PGPPublicKeyRing jPub = new PGPPublicKeyRing(PGPUtil.getDecoderStream(new ByteArrayInputStream(TestKeys.JULIET_PUB.getBytes())), new BcKeyFingerprintCalculator()); + + ByteArrayOutputStream toEncrypted = new ByteArrayOutputStream(); + + OutputStream encryptor = PGPainless.createEncryptor().onOutputStream(toEncrypted) + .toRecipient(jPub.getPublicKey()) + .usingSecureAlgorithms() + .signWith(new PGPSecretKeyRing(PGPUtil.getDecoderStream(new ByteArrayInputStream(TestKeys.JULIET_PRIV.getBytes())), new BcKeyFingerprintCalculator()), new UnprotectedKeysProtector()) + .asciiArmor(); + + String message = "This message is encrypted using OpenPGP"; + ByteArrayInputStream fromPlain = new ByteArrayInputStream(message.getBytes()); + + Streams.pipeAll(fromPlain, encryptor); + fromPlain.close(); + encryptor.close(); + + System.out.println(new String(toEncrypted.toByteArray())); + + + ByteArrayInputStream fromEncrypted = new ByteArrayInputStream(toEncrypted.toByteArray()); + ByteArrayOutputStream toPlain = new ByteArrayOutputStream(); + + PainlessResult.ResultAndInputStream resultAndInputStream = PGPainless.createDecryptor() + .onInputStream(fromEncrypted) + .decryptWith(new PGPSecretKeyRingCollection(Collections.singleton(juliet)), + new UnprotectedKeysProtector()) + .doNotVerify() + .build(); + + InputStream decryptor = resultAndInputStream.getInputStream(); + + Streams.pipeAll(decryptor, toPlain); + + fromEncrypted.close(); + decryptor.close(); + + assertTrue(Arrays.equals(message.getBytes(), toPlain.toByteArray())); + } + + public static void main(String[] args) throws IOException, PGPException { + Security.addProvider(new BouncyCastleProvider()); + EncryptDecryptTest test = new EncryptDecryptTest(); + test.decryptVerifyTest(); + } + + @Test + public void decryptVerifyTest() throws IOException, PGPException { + String encryptedMessage = "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "hQGMAwAAAAAAAAAAAQwAoJtfpcBPCwhUzzHuVIcBzBLyfIWT/EJ527neb46lN56S\n" + + "B05BTIRudIeCsPYz81jwiFi/k0MBecRfozZ1xCPByo8ohSvRgzEHEkCNgObQ1bz0\n" + + "iB+Xb76OEzFOCPUebTaVscLNf8ak/GSzaW7jDc+5vnvDf7cV0x26pe4odpS/U5Tr\n" + + "cO3wb/47K+sJ1cxJmPtcD41O02xu3QisQKPrimM0Kue6ziGeKyw1RkSowv9U47TK\n" + + "wppPCHOTli2Nf+gZizF1oyQZzPGst4fjujygcIoajplfW9nZvxsbmYRSLSdmV9m6\n" + + "k1jQbPDUhVs0gstH92C6hPpoBWxoxkHcwz8gy36nCyB6cYGyq3oN1UnGU4afPyD5\n" + + "SmmEjELBd2i2Ll/DYk2x06SnKZMQuWrSCZzWgl/9HsPo5ydVb97OjuEpWtW9xDMA\n" + + "KlYPNWEq+b+akOEstNraC3pfVKvypz6ZzaMAS1gWWNYg8dlwBJOUVMSo7iLaUQkK\n" + + "yp4uH1DlsyVu1atCUc8thQIMAwAAAAAAAAAAAQ/5AdiZ/sG859Y/rGR7U/8MzGg0\n" + + "j3f2vrgDF/0NRRk5aqd1lb4CaZvrztcYqW3cEK7iF9rKwImZZiWIptjJ9Mz6f1Zl\n" + + "FbODObSVRZAcZqYGswEEfsQvpQFlwG6Qx48OaQaDPr147raFI3C3kEU9Nb2VBg8+\n" + + "MevJaXJft5PXwUTG2Qvfxqr/3hfGAwB4/zHwA8vFd1np3spryfrC9Dq8UXUoRXIS\n" + + "xaFPiLEYt8rLef8f11OypEpmknIibu9jjJtuVZo+SjP6jgLHDwM7rqCZFITM2Qra\n" + + "2iBCt8YVcIiTK137t+EfsdVN/KHiRbc++e9zUbGMEextbtNbdoFOU4dnKBm6Su8l\n" + + "Z5UerNbR8D7+xJKfAEabdi0qI7QFmhTZ/4H/22yrvoD9jMFSBXUTE9ENIX9Hfqom\n" + + "UdsHfuE+5PC0JjkZkhchDO1M7XBX++lBCFsq2abfdpmaX+roVX0iTGboxr5Ag1Cf\n" + + "T2zWyRX/XKnvmdeGICV5qjy/ThuSWvAclazyFxWLamMztJq5BRpfAzKNQRDqlmKw\n" + + "eePtKW2EWUIjFQ5/UAM6Edu/K34ksFxb0w6YGLzQSskGr7gGAipLmpek6vcUSUA1\n" + + "oc9XJGdpx93GDRcqDjKDt/ej06VxG33/pW65ntf5QM/+LScGqaLhAHyEOsBzVIXY\n" + + "BONcadSgzkTrlbSMGAmFAQwDtLUJy1k24D4BB/0brqR0UN1LtO+Lc/vN6X/Um2CZ\n" + + "CM6MRhPnXP63Q9HHkGJ2S8zGWvQLwWL9Y14CFCgm6rACLBSIyPbihhC2OC8afhSy\n" + + "apGkdHtdghS2egs2U8qlJ2Y32IAG9CcUtNkRjxp+/RWSrmZeuL4l7DXCyH5lUadx\n" + + "5bPZhAHqW9408q2rQd9dBg2o7ciGXTJSKVahjuiB/O0gchOnbqnlYJbKbCkntXUo\n" + + "c7h4w1e8MutisSJorh7kbxgxUJSboZzEkiUfnoacPTz6bL+re9tmnpvlee70sIyM\n" + + "BiYRCyPw7Ice4R3XyWtsMTjT/wjZ//whMpWdy2drcJSyhh+GQMbekTVsNWod0lQB\n" + + "JTPUfti2VU7PMB3LjJA+l/T9iWPPx8lirnLhXOOerWKH9I5Wo4Kqv/47aJhfMO6+\n" + + "jmLekAOylq+9DizrslW/EUgQyjIbcWfmyMiV6E2RwbI93tE=\n" + + "=GAhR\n" + + "-----END PGP MESSAGE-----"; + + PainlessResult.ResultAndInputStream resultAndInputStream = PGPainless.createDecryptor() + .onInputStream(new ByteArrayInputStream(encryptedMessage.getBytes())) + .decryptWith(new PGPSecretKeyRingCollection(Collections.singleton(juliet)), new UnprotectedKeysProtector()) + .verifyWith( + Collections.singleton(juliet.getPublicKey().getKeyID()), + Collections.singleton( + new PGPPublicKeyRing( + PGPUtil.getDecoderStream(new ByteArrayInputStream(TestKeys.JULIET_PUB.getBytes())), + new BcKeyFingerprintCalculator()))) + .ignoreMissingPublicKeys() + .build(); + + InputStream decryptor = resultAndInputStream.getInputStream(); + ByteArrayOutputStream toPlain = new ByteArrayOutputStream(); + Streams.pipeAll(decryptor, toPlain); + decryptor.close(); + toPlain.close(); + + assertTrue(Arrays.equals("This message is encrypted".getBytes(), toPlain.toByteArray())); + } +} diff --git a/src/test/java/de/vanitasvitae/crypto/pgpainless/TestKeys.java b/src/test/java/de/vanitasvitae/crypto/pgpainless/TestKeys.java new file mode 100644 index 00000000..106ef0ab --- /dev/null +++ b/src/test/java/de/vanitasvitae/crypto/pgpainless/TestKeys.java @@ -0,0 +1,142 @@ +/** + * + * 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 de.vanitasvitae.crypto.pgpainless; + +public class TestKeys { + + public static final String JULIET_UID = "xmpp:juliet@capulet.lit"; + + /** + * Public key of xmpp:juliet@capulet.lit. + */ + public static final String JULIET_PUB = "" + + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "mQENBFrxov4BCAChZwPrBxxIlwzpieR5T2pnaOZLWH0WqSON6rVjvfbJHWdDi3Th\n" + + "remHW4gg4IBSTXkVFDIeQNVcOvGNgMg3Oe/x0I6FK12jrw9prycmjFxQ7A0ix7ZG\n" + + "UkTF5jITgzJbkH100gYfXtZsfTyvgISSAT//6vvvQPZ3zCr09XvAG0CyQ1BhULsv\n" + + "mVRe4Oh5b0VK4kLdv+GiA/T+49UKZj6lne9Vdti16ZIj7teVCbicfdhpTzsjur42\n" + + "r8ptouKAuyFPw9KnGNwVlIiv5jt/Kit/LoOBenh74sitsCXq8IQ9kKp/eNt8TF4u\n" + + "D4IGpxnJfB8XCiixYHoFEajmQBVJXNYtvoPvABEBAAG0F3htcHA6anVsaWV0QGNh\n" + + "cHVsZXQubGl0iQFOBBMBCAA4FiEEHQGMdy34xe+GodzJtLUJy1k24D4FAlrxov4C\n" + + "Gy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQtLUJy1k24D6H7AgAoTjx4ezc\n" + + "A83NeOY3tMHVQTM7hKuy0wMcSzQgVgJmhLYRZS8r+FocPZua/eke49GPhe2yozvl\n" + + "ByWHtotklQeJiwOKxuPKMzneVA1ZK3/9LdGvtZlHMcAkEKDhit8HIaEcsFd4Z1re\n" + + "EhF2lyvY/E+rrx9YxV0QjisSWV2dSptv6FeGSztr9e5E+Head6hEQhsugiTVRF+1\n" + + "6mG90te0WGQ9YNiJ2FJovx5kBLTTuhwUz8Oacqihd2+RDDI5p3wJoogVL31aNb4n\n" + + "c7dGo8ieJPHGlkBsOfmreSxijTodZz9MXsgcx7b//u0uQryViJoZHWbtnXOFjjNc\n" + + "GWBtS084NKWl9w==\n" + + "=ecwX\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + /** + * Private key of xmpp:juliet@capulet.lit. + */ + public static final String JULIET_PRIV = "" + + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "\n" + + "lQOYBFrxov4BCAChZwPrBxxIlwzpieR5T2pnaOZLWH0WqSON6rVjvfbJHWdDi3Th\n" + + "remHW4gg4IBSTXkVFDIeQNVcOvGNgMg3Oe/x0I6FK12jrw9prycmjFxQ7A0ix7ZG\n" + + "UkTF5jITgzJbkH100gYfXtZsfTyvgISSAT//6vvvQPZ3zCr09XvAG0CyQ1BhULsv\n" + + "mVRe4Oh5b0VK4kLdv+GiA/T+49UKZj6lne9Vdti16ZIj7teVCbicfdhpTzsjur42\n" + + "r8ptouKAuyFPw9KnGNwVlIiv5jt/Kit/LoOBenh74sitsCXq8IQ9kKp/eNt8TF4u\n" + + "D4IGpxnJfB8XCiixYHoFEajmQBVJXNYtvoPvABEBAAEAB/4jMbXagW3q7DkOEZnm\n" + + "0+jVTLvu0QhRsScGEphj+++8sfMq+NVPQp9p+w0Hcjy49ZjB/mnhS+zaVCYI33yJ\n" + + "AlKubXYuVqLwBsO7HUzRrIiSwq4ol9jIo7bIWmYv+As6iRq6JvPb0k+6T2K0uDbw\n" + + "KWKduM0fwhAcVkJFsOO/o5GrbQaJc3oioFk8uFWTnO+FPBRTJ9oTlVG2M/tEatZK\n" + + "gl7I8Ukl0YYruCNUFKZ0tvO8HqulxBgUbGPBer1uOlfUD4RXdc8/PUiFKNo48XSu\n" + + "ZUEAZKGbFBjuX5Z8ha7+sUMEYEt70qlbkiLQxgHKAmpyridAk3q/SB3y2VB8Ik7I\n" + + "gpExBADInzLROYuUcXqmty+znVwm6nRIB75JBAy778zgIxx1v0O3QlVnR+YI8gJM\n" + + "mQ/9pD6LyP9hktWDmJxG8tX+kSuIp3wNJc5EMeXtCCmkUW0CP1gUhAbNW3MezKa5\n" + + "II5IhE9RgIsYqSU8ZgeIh72ON8XTp8i/wGipCXvJPggSAMXukQQAzfRmtLW+JHEK\n" + + "B8ETIYh8IUjXJ6TVlmuBwZ0eXjCpqy9arJi6tacesDJwnL3sqOMQWUmqGsCGSKA5\n" + + "cLITkVsxX/htIq8GFyludjg8t4Nr+fOGfChEq8QE0PHE2CgskQMHpfHvfIdnwKve\n" + + "Fg2Q8twoMw849O6PF3k/848Z65lDin8EAMDbuPWL7KU2sWeqvDEuoulS5K1gsq8X\n" + + "p3Od3+f0OG8YViMjKcVlSKHVvdlK4dlsccJrJJx6VzotV47LsmvVbzDwUE//MYq7\n" + + "QwwQetZbpdQZDysSGVqHMTuAg/1pr2u5rqh4cFqCYatgZwinEI2TQMXEqnSc+mj8\n" + + "xp/LNq5BZZQuO4y0F3htcHA6anVsaWV0QGNhcHVsZXQubGl0iQFOBBMBCAA4FiEE\n" + + "HQGMdy34xe+GodzJtLUJy1k24D4FAlrxov4CGy8FCwkIBwIGFQoJCAsCBBYCAwEC\n" + + "HgECF4AACgkQtLUJy1k24D6H7AgAoTjx4ezcA83NeOY3tMHVQTM7hKuy0wMcSzQg\n" + + "VgJmhLYRZS8r+FocPZua/eke49GPhe2yozvlByWHtotklQeJiwOKxuPKMzneVA1Z\n" + + "K3/9LdGvtZlHMcAkEKDhit8HIaEcsFd4Z1reEhF2lyvY/E+rrx9YxV0QjisSWV2d\n" + + "Sptv6FeGSztr9e5E+Head6hEQhsugiTVRF+16mG90te0WGQ9YNiJ2FJovx5kBLTT\n" + + "uhwUz8Oacqihd2+RDDI5p3wJoogVL31aNb4nc7dGo8ieJPHGlkBsOfmreSxijTod\n" + + "Zz9MXsgcx7b//u0uQryViJoZHWbtnXOFjjNcGWBtS084NKWl9w==\n" + + "=yPPE\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + + public static final String ROMEO_UID = "xmpp:romeo@montague.lit"; + + /** + * Public key of xmpp:romeo@montague.lit. + */ + public static final String ROMEO_PUB = "" + + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "mQENBFrxopkBCADiYg/+mEObXgxuMW6/LFKpEyaJK9pBMgutuxnYZ9PXWZmOhDIT\n" + + "Ugm9X9YJ3Qh94KaHge9F4uCeFASmM1vvUTRFTEb1W5RR9ZE/sy/cdAttnZ5JloPi\n" + + "CT3HDMIJAxIXhRJkeUR9GUb51ql27bMXl6lFh865VdNSXN/B8FzRQHENxv1Bq/6Z\n" + + "iQOViIETeRRgO+u6u2iZkYlHgYMaoMK7+YiNlHXanU9Atcuaz0ZCJS/XFNH89iqB\n" + + "Kvnv7KCQh4FhrNMLJRzNPXV8MY05nn0zF72qeEsniB16Xde18lMro8fQehg2mLwc\n" + + "XGtCwCKI6QbZVxYQt77r3ZACiwl66soFWijVABEBAAG0F3htcHA6cm9tZW9AbW9u\n" + + "dGFndWUubGl0iQFOBBMBCAA4FiEENdKZ0IovfYAjCwldBKMhguBeIfcFAlrxopkC\n" + + "Gy8FCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQBKMhguBeIfcj8AgAu1wubUwr\n" + + "2aQmDN3OqRM4M4yRL3oyYMkCKIjqD6KEeFsIXSSkXOuREJKEo8Mb1+ewV0SYmHCC\n" + + "K3bKKq3m71AQ7evDhKGshacPYesiDvMdHWQdQnjfaoHhyn9qIKl7H0Xv1yf/wyuG\n" + + "ANy1jYgtCEuYw7D+EsqNDdn8Xh+k/9s4aMI/6mfC0yGZgG8EyLTfbZkGPoS4aZfV\n" + + "AGFbuqryg48dXtnuzAPKcdgMTTMSnmR729YlfkjCffcFaldyXoe1VMbudUO7nkO9\n" + + "g65i5EXenkbc2h0TRDQ4lDFQyModqFTwYFYxAf/RA6tuhIQEoCnpCytFMvrRKMb3\n" + + "Bx5vYRDVmE3jeg==\n" + + "=2jSg\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + /** + * Private key of xmpp:romeo@montague.lit. + */ + public static final String ROMEO_PRIV = "" + + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "\n" + + "lQOYBFrxopkBCADiYg/+mEObXgxuMW6/LFKpEyaJK9pBMgutuxnYZ9PXWZmOhDIT\n" + + "Ugm9X9YJ3Qh94KaHge9F4uCeFASmM1vvUTRFTEb1W5RR9ZE/sy/cdAttnZ5JloPi\n" + + "CT3HDMIJAxIXhRJkeUR9GUb51ql27bMXl6lFh865VdNSXN/B8FzRQHENxv1Bq/6Z\n" + + "iQOViIETeRRgO+u6u2iZkYlHgYMaoMK7+YiNlHXanU9Atcuaz0ZCJS/XFNH89iqB\n" + + "Kvnv7KCQh4FhrNMLJRzNPXV8MY05nn0zF72qeEsniB16Xde18lMro8fQehg2mLwc\n" + + "XGtCwCKI6QbZVxYQt77r3ZACiwl66soFWijVABEBAAEAB/4mu5p69/hRQ+UikWie\n" + + "Yun9rZ4hSBR+pR5kaifA4/rV1Km2PZ4HujiaYyRO6beDOgWkF7IlpezCfzBQc2ce\n" + + "ailkVemqHzIgV8CzQmhE8sHlzlr/wjXsXaJpRSCJxDG7PnRoJmt2b/W512WFSKQk\n" + + "vDklAVh4U1vlsqhCGWr4DmuJbJkRyDhcX01tplRwim283F7bGqRcMBmKMZHiMgVc\n" + + "0u84EYKKVizJ3YAaaVqZyHb4qdeKK2ak3fPNuGT/oGd2sxnkL+BZGjJpu3RGpTA1\n" + + "tbOvOQnJGHQtABFxE8n6H9dHPJGtgyz2+udjUhL/P/E3PDoXazZkXRq2oHZKgg0f\n" + + "AwOBBADsWncHgvz15rXPF7O6AivbGTJ5ctkgVy4U3Fu2sk9rf0fx0sryBSqtTBw1\n" + + "Uvn/p9RwTsKw6fng6Nf78xpZFlUDB00YCcuWkGodxvjTAyB0dtBmkhopeKi0dmHh\n" + + "ndnR6Pv0CsXu8nG7lUi+q6s3oc4h2OfDBhrqsyYY5M2gGit3dQQA9TNuinJD9XXv\n" + + "QRyauMnSJ5xRcfOu8QCxZlllCvffZjSGCPoVjUpJEe9qsVbXVj2GYCxjLCSXV0V+\n" + + "vlJfdPrl1BhZ3fmEpg0u7SyGDDOe8fe1ehk5sAeL8O0eFWlPSEaEccsjlpJ2FO0n\n" + + "P04SZdOeM6wmhDTEDzpFnjbPndQTH+ED/R1zNzr55DvxQodmrW/BvTmhGQ22rHtk\n" + + "IUfbeMaVfUvNLJA/JksrUIx3Gga9QCDZgfm1RsRhLUlHiqTQe23sPWgKOsbf5O1j\n" + + "XJZaCNZ7LloVQbkG7xFcnb/n1+JjBr4FxXjAA6cY/iRGlznjIIaasyklKm1/4LuQ\n" + + "hnH3QqTvCN3dOFS0F3htcHA6cm9tZW9AbW9udGFndWUubGl0iQFOBBMBCAA4FiEE\n" + + "NdKZ0IovfYAjCwldBKMhguBeIfcFAlrxopkCGy8FCwkIBwIGFQoJCAsCBBYCAwEC\n" + + "HgECF4AACgkQBKMhguBeIfcj8AgAu1wubUwr2aQmDN3OqRM4M4yRL3oyYMkCKIjq\n" + + "D6KEeFsIXSSkXOuREJKEo8Mb1+ewV0SYmHCCK3bKKq3m71AQ7evDhKGshacPYesi\n" + + "DvMdHWQdQnjfaoHhyn9qIKl7H0Xv1yf/wyuGANy1jYgtCEuYw7D+EsqNDdn8Xh+k\n" + + "/9s4aMI/6mfC0yGZgG8EyLTfbZkGPoS4aZfVAGFbuqryg48dXtnuzAPKcdgMTTMS\n" + + "nmR729YlfkjCffcFaldyXoe1VMbudUO7nkO9g65i5EXenkbc2h0TRDQ4lDFQyMod\n" + + "qFTwYFYxAf/RA6tuhIQEoCnpCytFMvrRKMb3Bx5vYRDVmE3jeg==\n" + + "=LZ1b\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; +}