1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-18 02:12:06 +01:00

Still WiP, add selection strategies

This commit is contained in:
Paul Schaub 2018-06-07 18:12:13 +02:00
parent 2ba0200047
commit 8a2ae0a989
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
28 changed files with 977 additions and 108 deletions

View file

@ -15,21 +15,16 @@ import java.util.Collections;
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; 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 de.vanitasvitae.crypto.pgpainless.key.generation.type.length.RsaLength;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
public class Main { public class Main {
@ -43,7 +38,7 @@ public class Main {
PGPSecretKeyRing a = PGPainless.generateKeyRing().simpleRsaKeyRing("a@b.c", RsaLength._2048); PGPSecretKeyRing a = PGPainless.generateKeyRing().simpleRsaKeyRing("a@b.c", RsaLength._2048);
PGPSecretKeyRing b = PGPainless.generateKeyRing().simpleRsaKeyRing("b@c.d", RsaLength._2048); PGPSecretKeyRing b = PGPainless.generateKeyRing().simpleRsaKeyRing("b@c.d", RsaLength._2048);
SecretKeyRingDecryptor secretKeyRingDecryptor = new SecretKeyRingDecryptor() { SecretKeyRingProtector secretKeyRingDecryptor = new SecretKeyRingProtector() {
@Override @Override
public PBESecretKeyDecryptor getDecryptor(Long keyId) { public PBESecretKeyDecryptor getDecryptor(Long keyId) {
return null; return null;
@ -62,7 +57,7 @@ public class Main {
OutputStream encryptor = PGPainless.createEncryptor().onOutputStream(toEncrypted) OutputStream encryptor = PGPainless.createEncryptor().onOutputStream(toEncrypted)
.toRecipient(b.getPublicKey()) .toRecipient(b.getPublicKey())
.usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED) .usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED)
.signWith(a.getSecretKey(), secretKeyRingDecryptor) .signWith(a, secretKeyRingDecryptor)
.asciiArmor(); .asciiArmor();
Streams.pipeAll(fromPlain, encryptor); Streams.pipeAll(fromPlain, encryptor);
@ -94,7 +89,7 @@ public class Main {
System.out.println(new String(toPlain.toByteArray())); 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 { throws IOException, PGPException {
String gpg = "-----BEGIN PGP MESSAGE-----\n" + String gpg = "-----BEGIN PGP MESSAGE-----\n" +
"\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 { throws IOException, PGPException {
byte[] bytes = "Diese Nachricht ist streng geheim!!!".getBytes(Charset.forName("UTF-8")); byte[] bytes = "Diese Nachricht ist streng geheim!!!".getBytes(Charset.forName("UTF-8"));
ByteArrayInputStream fromPlain = new ByteArrayInputStream(bytes); ByteArrayInputStream fromPlain = new ByteArrayInputStream(bytes);
@ -142,7 +137,7 @@ public class Main {
.onOutputStream(toEncrypted) .onOutputStream(toEncrypted)
.toRecipient(b.getPublicKey()) .toRecipient(b.getPublicKey())
.usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED) .usingAlgorithms(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA512, CompressionAlgorithm.UNCOMPRESSED)
.signWith(a.getSecretKey(), secretKeyRingDecryptor) .signWith(a, secretKeyRingDecryptor)
.noArmor(); .noArmor();
Streams.pipeAll(fromPlain, encryptor); Streams.pipeAll(fromPlain, encryptor);

View file

@ -1,16 +1,22 @@
package de.vanitasvitae.crypto.pgpainless; package de.vanitasvitae.crypto.pgpainless;
import org.bouncycastle.openpgp.PGPException;
public class PublicKeyNotFoundException extends Exception { public class PublicKeyNotFoundException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final long keyId; private long keyId;
public PublicKeyNotFoundException(long keyId) { public PublicKeyNotFoundException(long keyId) {
super("No PGPPublicKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); super("No PGPPublicKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found.");
this.keyId = keyId; this.keyId = keyId;
} }
public PublicKeyNotFoundException(PGPException e) {
}
public long getKeyId() { public long getKeyId() {
return keyId; return keyId;
} }

View file

@ -4,7 +4,7 @@ public class SecretKeyNotFoundException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final long keyId; private long keyId;
public SecretKeyNotFoundException(long keyId) { public SecretKeyNotFoundException(long keyId) {
super("No PGPSecretKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found."); super("No PGPSecretKey with id " + Long.toHexString(keyId) + " (" + keyId + ") found.");

View file

@ -7,7 +7,7 @@ import java.util.Iterator;
import java.util.Set; import java.util.Set;
import de.vanitasvitae.crypto.pgpainless.PainlessResult; 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.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
@ -17,7 +17,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
private InputStream inputStream; private InputStream inputStream;
private PGPSecretKeyRingCollection decryptionKeys; private PGPSecretKeyRingCollection decryptionKeys;
private SecretKeyRingDecryptor decryptionKeyDecryptor; private SecretKeyRingProtector decryptionKeyDecryptor;
private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>(); private Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private Set<Long> trustedKeyIds = new HashSet<>(); private Set<Long> trustedKeyIds = new HashSet<>();
private MissingPublicKeyCallback missingPublicKeyCallback = null; private MissingPublicKeyCallback missingPublicKeyCallback = null;
@ -31,7 +31,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
class DecryptWithImpl implements DecryptWith { class DecryptWithImpl implements DecryptWith {
@Override @Override
public VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor) { public VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingProtector decryptor) {
DecryptionBuilder.this.decryptionKeys = secretKeyRings; DecryptionBuilder.this.decryptionKeys = secretKeyRings;
DecryptionBuilder.this.decryptionKeyDecryptor = decryptor; DecryptionBuilder.this.decryptionKeyDecryptor = decryptor;
return new VerifyWithImpl(); return new VerifyWithImpl();

View file

@ -5,7 +5,7 @@ import java.io.InputStream;
import java.util.Set; import java.util.Set;
import de.vanitasvitae.crypto.pgpainless.PainlessResult; 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.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
@ -17,7 +17,7 @@ public interface DecryptionBuilderInterface {
interface DecryptWith { interface DecryptWith {
VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingDecryptor decryptor); VerifyWith decryptWith(PGPSecretKeyRingCollection secretKeyRings, SecretKeyRingProtector decryptor);
VerifyWith doNotDecrypt(); VerifyWith doNotDecrypt();

View file

@ -13,7 +13,7 @@ import de.vanitasvitae.crypto.pgpainless.PainlessResult;
import de.vanitasvitae.crypto.pgpainless.PainlessStream; import de.vanitasvitae.crypto.pgpainless.PainlessStream;
import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; 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.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
@ -40,7 +40,7 @@ public class InputStreamFactory {
private InputStream inputStream; private InputStream inputStream;
private final PGPSecretKeyRingCollection decryptionKeys; private final PGPSecretKeyRingCollection decryptionKeys;
private final SecretKeyRingDecryptor decryptionKeyDecryptor; private final SecretKeyRingProtector decryptionKeyDecryptor;
private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>(); private final Set<PGPPublicKeyRing> verificationKeys = new HashSet<>();
private final Set<Long> trustedKeyIds = new HashSet<>(); private final Set<Long> trustedKeyIds = new HashSet<>();
private final MissingPublicKeyCallback missingPublicKeyCallback; private final MissingPublicKeyCallback missingPublicKeyCallback;
@ -50,7 +50,7 @@ public class InputStreamFactory {
private final Map<Long, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>(); private final Map<Long, PGPOnePassSignature> verifiableOnePassSignatures = new HashMap<>();
private InputStreamFactory(PGPSecretKeyRingCollection decryptionKeys, private InputStreamFactory(PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingDecryptor decryptor, SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys, Set<PGPPublicKeyRing> verificationKeys,
Set<Long> trustedKeyIds, Set<Long> trustedKeyIds,
MissingPublicKeyCallback missingPublicKeyCallback) MissingPublicKeyCallback missingPublicKeyCallback)
@ -64,7 +64,7 @@ public class InputStreamFactory {
public static PainlessResult.ResultAndInputStream create(InputStream inputStream, public static PainlessResult.ResultAndInputStream create(InputStream inputStream,
PGPSecretKeyRingCollection decryptionKeys, PGPSecretKeyRingCollection decryptionKeys,
SecretKeyRingDecryptor decryptor, SecretKeyRingProtector decryptor,
Set<PGPPublicKeyRing> verificationKeys, Set<PGPPublicKeyRing> verificationKeys,
Set<Long> trustedKeyIds, Set<Long> trustedKeyIds,
MissingPublicKeyCallback missingPublicKeyCallback) MissingPublicKeyCallback missingPublicKeyCallback)

View file

@ -2,6 +2,7 @@ package de.vanitasvitae.crypto.pgpainless.encryption_signing;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; 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.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
@ -25,7 +27,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
private OutputStream outputStream; private OutputStream outputStream;
private final Set<PGPPublicKey> encryptionKeys = new HashSet<>(); private final Set<PGPPublicKey> encryptionKeys = new HashSet<>();
private final Set<PGPSecretKey> signingKeys = new HashSet<>(); private final Set<PGPSecretKey> signingKeys = new HashSet<>();
private SecretKeyRingDecryptor signingKeysDecryptor; private SecretKeyRingProtector signingKeysDecryptor;
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128; private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.AES_128;
private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256; private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED; private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
@ -41,52 +43,45 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
@Override @Override
public WithAlgorithms toRecipient(PGPPublicKey key) { 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); EncryptionBuilder.this.encryptionKeys.add(key);
return new WithAlgorithmsImpl(); return new WithAlgorithmsImpl();
} }
@Override @Override
public WithAlgorithms toRecipients(Set<PGPPublicKey> keys) { public WithAlgorithms toRecipients(Set<PGPPublicKeyRing> keys) {
EncryptionBuilder.this.encryptionKeys.addAll(keys); for (PGPPublicKeyRing ring : keys) {
for (PGPPublicKey k : ring) {
if (k.isEncryptionKey()) {
EncryptionBuilder.this.encryptionKeys.add(k);
}
}
}
return new WithAlgorithmsImpl(); return new WithAlgorithmsImpl();
} }
@Override @Override
public WithAlgorithms toRecipients(Set<Long> keyIds, Set<PGPPublicKeyRing> keyRings) public WithAlgorithms toRecipients(Set<Long> keyIds, Set<PGPPublicKeyRingCollection> keys)
throws PublicKeyNotFoundException { throws PublicKeyNotFoundException {
Set<PGPPublicKey> 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<Long> keyIds, PGPPublicKeyRingCollection keyRings)
throws PublicKeyNotFoundException {
Set<PGPPublicKeyRing> rings = new HashSet<>(); Set<PGPPublicKeyRing> rings = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = keyRings.getKeyRings(); i.hasNext();) { for (PGPPublicKeyRingCollection collection : keys) {
rings.add(i.next()); 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 @Override
@ -98,8 +93,21 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
class WithAlgorithmsImpl implements WithAlgorithms { class WithAlgorithmsImpl implements WithAlgorithms {
@Override @Override
public WithAlgorithms andToSelf(Set<PGPPublicKey> keys) { public WithAlgorithms andToSelf(PGPPublicKey key) {
EncryptionBuilder.this.encryptionKeys.addAll(keys); EncryptionBuilder.this.encryptionKeys.add(key);
return this;
}
@Override
public WithAlgorithms andToSelf(Set<PGPPublicKeyRing> keyRings) {
for (PGPPublicKeyRing ring : keyRings) {
for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (key.isEncryptionKey()) {
EncryptionBuilder.this.encryptionKeys.add(key);
}
}
}
return this; return this;
} }
@ -114,59 +122,56 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
return new SignWithImpl(); 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 { class SignWithImpl implements SignWith {
@Override @Override
public Armor signWith(PGPSecretKey key, SecretKeyRingDecryptor decryptor) { public Armor signWith(PGPSecretKeyRing key, SecretKeyRingProtector decryptor) {
EncryptionBuilder.this.signingKeys.add(key); return signWith(Collections.singleton(key), decryptor);
}
@Override
public Armor signWith(Set<PGPSecretKeyRing> keys, SecretKeyRingProtector decryptor) {
for (PGPSecretKeyRing key : keys) {
for (Iterator<PGPSecretKey> i = key.getSecretKeys(); i.hasNext(); ) {
PGPSecretKey s = i.next();
if (s.isSigningKey()) {
EncryptionBuilder.this.signingKeys.add(s);
}
}
}
EncryptionBuilder.this.signingKeysDecryptor = decryptor; EncryptionBuilder.this.signingKeysDecryptor = decryptor;
return new ArmorImpl(); return new ArmorImpl();
} }
@Override @Override
public Armor signWith(Set<PGPSecretKey> keys, SecretKeyRingDecryptor decryptor) { public Armor signWith(Set<Long> keyIds, Set<PGPSecretKeyRingCollection> keyRings, SecretKeyRingProtector decryptor)
EncryptionBuilder.this.signingKeys.addAll(keys);
EncryptionBuilder.this.signingKeysDecryptor = decryptor;
return new ArmorImpl();
}
@Override
public Armor signWith(Set<Long> keyIds, Set<PGPSecretKeyRing> keyRings, SecretKeyRingDecryptor decryptor)
throws SecretKeyNotFoundException { throws SecretKeyNotFoundException {
Set<PGPSecretKey> 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, decryptor);
}
@Override
public Armor signWith(Set<Long> keyIds, PGPSecretKeyRingCollection keys, SecretKeyRingDecryptor decryptor)
throws SecretKeyNotFoundException {
Set<PGPSecretKeyRing> rings = new HashSet<>(); Set<PGPSecretKeyRing> rings = new HashSet<>();
for (PGPSecretKeyRingCollection collection : keyRings) {
for (Iterator<PGPSecretKeyRing> i = keys.getKeyRings(); i.hasNext();) { for (long keyId : keyIds) {
rings.add(i.next()); try {
PGPSecretKeyRing ring = collection.getSecretKeyRing(keyId);
if (ring != null) {
rings.add(ring);
keyIds.remove(keyId);
} }
return signWith(keyIds, rings, decryptor); } catch (PGPException e) {
throw new SecretKeyNotFoundException(keyId);
}
}
}
return signWith(rings, decryptor);
} }
@Override @Override

View file

@ -1,7 +1,6 @@
package de.vanitasvitae.crypto.pgpainless.encryption_signing; package de.vanitasvitae.crypto.pgpainless.encryption_signing;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Set; 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.CompressionAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.HashAlgorithm;
import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm; import de.vanitasvitae.crypto.pgpainless.algorithm.SymmetricKeyAlgorithm;
import de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
@ -26,12 +25,9 @@ public interface EncryptionBuilderInterface {
WithAlgorithms toRecipient(PGPPublicKey key); WithAlgorithms toRecipient(PGPPublicKey key);
WithAlgorithms toRecipients(Set<PGPPublicKey> keys); WithAlgorithms toRecipients(Set<PGPPublicKeyRing> keys);
WithAlgorithms toRecipients(Set<Long> keyIds, Set<PGPPublicKeyRing> keyRings) WithAlgorithms toRecipients(Set<Long> keyIds, Set<PGPPublicKeyRingCollection> keys)
throws PublicKeyNotFoundException;
WithAlgorithms toRecipients(Set<Long> keyIds, PGPPublicKeyRingCollection keys)
throws PublicKeyNotFoundException; throws PublicKeyNotFoundException;
SignWith doNotEncrypt(); SignWith doNotEncrypt();
@ -40,24 +36,26 @@ public interface EncryptionBuilderInterface {
interface WithAlgorithms { interface WithAlgorithms {
WithAlgorithms andToSelf(Set<PGPPublicKey> keys); WithAlgorithms andToSelf(PGPPublicKey key);
WithAlgorithms andToSelf(Set<PGPPublicKeyRing> keys);
SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm, SignWith usingAlgorithms(SymmetricKeyAlgorithm symmetricKeyAlgorithm,
HashAlgorithm hashAlgorithm, HashAlgorithm hashAlgorithm,
CompressionAlgorithm compressionAlgorithm); CompressionAlgorithm compressionAlgorithm);
SignWith usingSecureAlgorithms();
} }
interface SignWith { interface SignWith {
Armor signWith(PGPSecretKey key, SecretKeyRingDecryptor decryptor); Armor signWith(PGPSecretKeyRing key, SecretKeyRingProtector decryptor);
Armor signWith(Set<PGPSecretKey> keys, SecretKeyRingDecryptor decryptor); Armor signWith(Set<PGPSecretKeyRing> keyRings, SecretKeyRingProtector decryptor)
Armor signWith(Set<Long> keyIds, Set<PGPSecretKeyRing> keyRings, SecretKeyRingDecryptor decryptor)
throws SecretKeyNotFoundException; throws SecretKeyNotFoundException;
Armor signWith(Set<Long> keyIds, PGPSecretKeyRingCollection keys, SecretKeyRingDecryptor decryptor) Armor signWith(Set<Long> keyIds, Set<PGPSecretKeyRingCollection> keys, SecretKeyRingProtector decryptor)
throws SecretKeyNotFoundException; throws SecretKeyNotFoundException;
Armor doNotSign(); Armor doNotSign();

View file

@ -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.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
public interface SecretKeyRingDecryptor { public interface SecretKeyRingProtector {
PBESecretKeyDecryptor getDecryptor(Long keyId); PBESecretKeyDecryptor getDecryptor(Long keyId);

View file

@ -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;
}
}

View file

@ -31,6 +31,7 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator; 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.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;

View file

@ -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 <K> Type of the Key
* @param <R> Type of the KeyRing
* @param <O> Type that describes the owner of this key
*/
public interface KeySelectionStrategy<K, R, O> {
boolean accept(O identifier, K key);
Set<K> selectKeysFromKeyRing(O identifier, R ring);
MultiMap<O, K> selectKeysFromKeyRings(MultiMap<O, R> rings);
}

View file

@ -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 <O> Type that describes the owner of the key.
*/
public abstract class PublicKeySelectionStrategy<O> implements KeySelectionStrategy<PGPPublicKey, PGPPublicKeyRing, O> {
@Override
public Set<PGPPublicKey> selectKeysFromKeyRing(O identifier, PGPPublicKeyRing ring) {
Set<PGPPublicKey> keys = new HashSet<>();
for (Iterator<PGPPublicKey> i = ring.getPublicKeys(); i.hasNext(); ) {
PGPPublicKey key = i.next();
if (accept(identifier, key)) keys.add(key);
}
return keys;
}
@Override
public MultiMap<O, PGPPublicKey> selectKeysFromKeyRings(MultiMap<O, PGPPublicKeyRing> keyRings) {
MultiMap<O, PGPPublicKey> keys = new MultiMap<>();
for (O identifier : keyRings.keySet()) {
for (PGPPublicKeyRing ring : keyRings.get(identifier)) {
keys.put(identifier, selectKeysFromKeyRing(identifier, ring));
}
}
return keys;
}
}

View file

@ -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 <O> Type that describes the owner of the key.
*/
public abstract class SecretKeySelectionStrategy<O> implements KeySelectionStrategy<PGPSecretKey, PGPSecretKeyRing, O> {
@Override
public Set<PGPSecretKey> selectKeysFromKeyRing(O identifier, PGPSecretKeyRing ring) {
Set<PGPSecretKey> keys = new HashSet<>();
for (Iterator<PGPSecretKey> i = ring.getSecretKeys(); i.hasNext(); ) {
PGPSecretKey key = i.next();
if (accept(identifier, key)) keys.add(key);
}
return keys;
}
@Override
public MultiMap<O, PGPSecretKey> selectKeysFromKeyRings(MultiMap<O, PGPSecretKeyRing> keyRings) {
MultiMap<O, PGPSecretKey> keys = new MultiMap<>();
for (O identifier : keyRings.keySet()) {
for (PGPSecretKeyRing ring : keyRings.get(identifier)) {
keys.put(identifier, selectKeysFromKeyRing(identifier, ring));
}
}
return keys;
}
}

View file

@ -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 <O> Type that describes the owner of the key (not used for decision).
*/
public class EncryptionKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPPublicKey key) {
return key.isEncryptionKey();
}
}

View file

@ -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 <O> Type that describes the owner of this key (not used for this decision).
*/
public static class PubKeySelectionStrategy<O> extends PublicKeySelectionStrategy<O> {
@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 <O> Type that describes the owner of this key (not used for this decision).
*/
public static class SecKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return !key.getPublicKey().hasRevocation();
}
}
}

View file

@ -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 <O> Type that describes the owner of the key (not used for this decision).
*/
public class SignatureKeySelectionStrategy<O> extends SecretKeySelectionStrategy<O> {
@Override
public boolean accept(O identifier, PGPSecretKey key) {
return key.isSigningKey();
}
}

View file

@ -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<R, C, O> {
boolean accept(O identifier, R keyRing);
Set<R> selectKeyRingsFromCollection(O identifier, C keyRingCollection);
MultiMap<O, R> selectKeyRingsFromCollections(MultiMap<O, C> keyRingCollections);
}

View file

@ -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<O> implements KeyRingSelectionStrategy<PGPPublicKeyRing, PGPPublicKeyRingCollection, O> {
@Override
public Set<PGPPublicKeyRing> selectKeyRingsFromCollection(O identifier, PGPPublicKeyRingCollection keyRingCollection) {
Set<PGPPublicKeyRing> accepted = new HashSet<>();
for (Iterator<PGPPublicKeyRing> i = keyRingCollection.getKeyRings(); i.hasNext(); ) {
PGPPublicKeyRing ring = i.next();
if (accept(identifier, ring)) accepted.add(ring);
}
return accepted;
}
@Override
public MultiMap<O, PGPPublicKeyRing> selectKeyRingsFromCollections(MultiMap<O, PGPPublicKeyRingCollection> keyRingCollections) {
MultiMap<O, PGPPublicKeyRing> keyRings = new MultiMap<>();
for (O identifier : keyRingCollections.keySet()) {
for (PGPPublicKeyRingCollection collection : keyRingCollections.get(identifier)) {
keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection));
}
}
return keyRings;
}
}

View file

@ -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<O> implements KeyRingSelectionStrategy<PGPSecretKeyRing, PGPSecretKeyRingCollection, O> {
@Override
public Set<PGPSecretKeyRing> selectKeyRingsFromCollection(O identifier, PGPSecretKeyRingCollection keyRingCollection) {
Set<PGPSecretKeyRing> accepted = new HashSet<>();
for (Iterator<PGPSecretKeyRing> i = keyRingCollection.getKeyRings(); i.hasNext(); ) {
PGPSecretKeyRing ring = i.next();
if (accept(identifier, ring)) accepted.add(ring);
}
return accepted;
}
@Override
public MultiMap<O, PGPSecretKeyRing> selectKeyRingsFromCollections(MultiMap<O, PGPSecretKeyRingCollection> keyRingCollections) {
MultiMap<O, PGPSecretKeyRing> keyRings = new MultiMap<>();
for (O identifier : keyRingCollections.keySet()) {
for (PGPSecretKeyRingCollection collection : keyRingCollections.get(identifier)) {
keyRings.put(identifier, selectKeyRingsFromCollection(identifier, collection));
}
}
return keyRings;
}
}

View file

@ -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);
}
}
}

View file

@ -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<String> {
@Override
public boolean accept(String identifier, PGPPublicKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
}
return false;
}
}
public static class SecRingSelectionStrategy extends SecretKeyRingSelectionStrategy<String> {
@Override
public boolean accept(String identifier, PGPSecretKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
}
return false;
}
}
}

View file

@ -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<String> {
@Override
public boolean accept(String identifier, PGPPublicKey key) {
for (Iterator<String> userIds = key.getUserIDs(); userIds.hasNext(); ) {
String userId = userIds.next();
if (userId.contains(identifier)) {
return true;
}
}
return false;
}
}
public static class SecRingSelectionStrategy extends SecretKeySelectionStrategy<String> {
@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;
}
}
}

View file

@ -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<O> extends PublicKeyRingSelectionStrategy<O> {
private final MultiMap<O, Long> whitelist;
public PubRingSelectionStrategy(MultiMap<O, Long> whitelist) {
this.whitelist = whitelist;
}
public PubRingSelectionStrategy(Map<O, Set<Long>> whitelist) {
this.whitelist = new MultiMap<>(whitelist);
}
@Override
public boolean accept(O identifier, PGPPublicKeyRing keyRing) {
Set<Long> whitelistedKeyIds = whitelist.get(identifier);
if (whitelistedKeyIds == null) {
return false;
}
return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID());
}
}
public static class SecRingSelectionStrategy<O> extends SecretKeyRingSelectionStrategy<O> {
private final MultiMap<O, Long> whitelist;
public SecRingSelectionStrategy(MultiMap<O, Long> whitelist) {
this.whitelist = whitelist;
}
public SecRingSelectionStrategy(Map<O, Set<Long>> whitelist) {
this.whitelist = new MultiMap<>(whitelist);
}
@Override
public boolean accept(O identifier, PGPSecretKeyRing keyRing) {
Set<Long> whitelistedKeyIds = whitelist.get(identifier);
if (whitelistedKeyIds == null) {
return false;
}
return whitelistedKeyIds.contains(keyRing.getPublicKey().getKeyID());
}
}
}

View file

@ -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);
}
}
}

View file

@ -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<K, V> {
private final Map<K, Set<V>> map;
public MultiMap() {
map = new HashMap<>();
}
public MultiMap(MultiMap<K, V> other) {
this.map = new HashMap<>();
for (K k : other.map.keySet()) {
map.put(k, new HashSet<>(other.map.get(k)));
}
}
public MultiMap(Map<K, Set<V>> 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<V> values : map.values()) {
if (values.contains(o)) return true;
}
return false;
}
public Set<V> get(Object o) {
return map.get(o);
}
public void put(K k, V v) {
Set<V> values = map.get(k);
if (values == null) {
values = new HashSet<>();
map.put(k, values);
}
values.add(v);
}
public void put(K k, Set<V> vs) {
for (V v : vs) {
put(k, v);
}
}
public void remove(Object o) {
for (Set<V> values : map.values()) {
values.remove(o);
}
}
public void putAll(Map<? extends K, ? extends Set<V>> _map) {
for (K key : _map.keySet()) {
Set<V> 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<K> keySet() {
return map.keySet();
}
public Collection<Set<V>> values() {
return map.values();
}
public Set<Map.Entry<K, Set<V>>> 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();
}
}

View file

@ -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()));
}
}

View file

@ -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-----";
}