diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-01-27 14:00:22 +0100 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-01-27 14:00:22 +0100 |
commit | 5aec25ac0501352e4cb6645c86869dde6e91f0d0 (patch) | |
tree | ee9adfd55cddf25f098e5e028d585a72de7cd70c /libraries/spongycastle/pg/src | |
parent | 8ca42b9bf953c6195ee0c17ef48a3154c126cc04 (diff) | |
download | open-keychain-5aec25ac0501352e4cb6645c86869dde6e91f0d0.tar.gz open-keychain-5aec25ac0501352e4cb6645c86869dde6e91f0d0.tar.bz2 open-keychain-5aec25ac0501352e4cb6645c86869dde6e91f0d0.zip |
Add spongy castle sources to libraries folder
Diffstat (limited to 'libraries/spongycastle/pg/src')
259 files changed, 76182 insertions, 0 deletions
diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java new file mode 100644 index 000000000..449964abf --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java @@ -0,0 +1,360 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.PGPDataEncryptor; +import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.TeeOutputStream; + +/** + * Generator for encrypted objects. + */ +public class PGPEncryptedDataGenerator + implements SymmetricKeyAlgorithmTags, StreamGenerator +{ + /** + * Specifier for SHA-1 S2K PBE generator. + */ + public static final int S2K_SHA1 = HashAlgorithmTags.SHA1; + + /** + * Specifier for SHA-224 S2K PBE generator. + */ + public static final int S2K_SHA224 = HashAlgorithmTags.SHA224; + + /** + * Specifier for SHA-256 S2K PBE generator. + */ + public static final int S2K_SHA256 = HashAlgorithmTags.SHA256; + + /** + * Specifier for SHA-384 S2K PBE generator. + */ + public static final int S2K_SHA384 = HashAlgorithmTags.SHA384; + + /** + * Specifier for SHA-512 S2K PBE generator. + */ + public static final int S2K_SHA512 = HashAlgorithmTags.SHA512; + + private BCPGOutputStream pOut; + private OutputStream cOut; + private boolean oldFormat = false; + private PGPDigestCalculator digestCalc; + private OutputStream genOut; + private PGPDataEncryptorBuilder dataEncryptorBuilder; + + private List methods = new ArrayList(); + private int defAlgorithm; + private SecureRandom rand; + + /** + * Base constructor. + * + * @param encryptorBuilder builder to create actual data encryptor. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder) + { + this(encryptorBuilder, false); + } + + /** + * Base constructor with the option to turn on formatting for PGP 2.6.x compatibility. + * + * @param encryptorBuilder builder to create actual data encryptor. + * @param oldFormat PGP 2.6.x compatibility required. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder, boolean oldFormat) + { + this.dataEncryptorBuilder = encryptorBuilder; + this.oldFormat = oldFormat; + + this.defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + this.rand = dataEncryptorBuilder.getSecureRandom(); + } + + /** + * Added a key encryption method to be used to encrypt the session data associated + * with this encrypted data. + * + * @param method key encryption method to use. + */ + public void addMethod(PGPKeyEncryptionMethodGenerator method) + { + methods.add(method); + } + + private void addCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8); + sessionInfo[sessionInfo.length - 1] = (byte)(check); + } + + private byte[] createSessionInfo( + int algorithm, + byte[] keyBytes) + { + byte[] sessionInfo = new byte[keyBytes.length + 3]; + sessionInfo[0] = (byte) algorithm; + System.arraycopy(keyBytes, 0, sessionInfo, 1, keyBytes.length); + addCheckSum(sessionInfo); + return sessionInfo; + } + + /** + * If buffer is non null stream assumed to be partial, otherwise the + * length will be used to output a fixed length packet. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @param buffer + * @return + * @throws java.io.IOException + * @throws PGPException + * @throws IllegalStateException + */ + private OutputStream open( + OutputStream out, + long length, + byte[] buffer) + throws IOException, PGPException, IllegalStateException + { + if (cOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + if (methods.size() == 0) + { + throw new IllegalStateException("no encryption methods specified"); + } + + byte[] key = null; + + pOut = new BCPGOutputStream(out); + + defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + rand = dataEncryptorBuilder.getSecureRandom(); + + if (methods.size() == 1) + { + + if (methods.get(0) instanceof PBEKeyEncryptionMethodGenerator) + { + PBEKeyEncryptionMethodGenerator m = (PBEKeyEncryptionMethodGenerator)methods.get(0); + + key = m.getKey(dataEncryptorBuilder.getAlgorithm()); + + pOut.writePacket(((PGPKeyEncryptionMethodGenerator)methods.get(0)).generate(defAlgorithm, null)); + } + else + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(0); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + else // multiple methods + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + + for (int i = 0; i != methods.size(); i++) + { + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(i); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + + try + { + PGPDataEncryptor dataEncryptor = dataEncryptorBuilder.build(key); + + digestCalc = dataEncryptor.getIntegrityCalculator(); + + if (buffer == null) + { + // + // we have to add block size + 2 for the generated IV and + 1 + 22 if integrity protected + // + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, length + dataEncryptor.getBlockSize() + 2 + 1 + 22); + + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, length + dataEncryptor.getBlockSize() + 2, oldFormat); + } + } + else + { + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, buffer); + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, buffer); + } + } + + genOut = cOut = dataEncryptor.getOutputStream(pOut); + + if (digestCalc != null) + { + genOut = new TeeOutputStream(digestCalc.getOutputStream(), cOut); + } + + byte[] inLineIv = new byte[dataEncryptor.getBlockSize() + 2]; + rand.nextBytes(inLineIv); + inLineIv[inLineIv.length - 1] = inLineIv[inLineIv.length - 3]; + inLineIv[inLineIv.length - 2] = inLineIv[inLineIv.length - 4]; + + genOut.write(inLineIv); + + return new WrappedGeneratorStream(genOut, this); + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + long length) + throws IOException, PGPException + { + return this.open(out, length, null); + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. The stream will be written out in chunks according to the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * + * @param out + * @param buffer the buffer to use. + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + byte[] buffer) + throws IOException, PGPException + { + return this.open(out, 0, buffer); + } + + /** + * Close off the encrypted object - this is equivalent to calling close on the stream + * returned by the open() method. + * <p> + * <b>Note</b>: This does not close the underlying output stream, only the stream on top of it created by the open() method. + * @throws java.io.IOException + */ + public void close() + throws IOException + { + if (cOut != null) + { + if (digestCalc != null) + { + // + // hand code a mod detection packet + // + BCPGOutputStream bOut = new BCPGOutputStream(genOut, PacketTags.MOD_DETECTION_CODE, 20); + + bOut.flush(); + + byte[] dig = digestCalc.getDigest(); + + cOut.write(dig); + } + + cOut.close(); + + cOut = null; + pOut = null; + } + } + + private class ClosableBCPGOutputStream + extends BCPGOutputStream + { + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, byte[] buffer) + throws IOException + { + super(out, symmetricKeyEnc, buffer); + } + + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, long length, boolean oldFormat) + throws IOException + { + super(out, symmetricKeyEnc, length, oldFormat); + } + + public ClosableBCPGOutputStream(OutputStream out, int symEncIntegrityPro, long length) + throws IOException + { + super(out, symEncIntegrityPro, length); + } + + public void close() + throws IOException + { + this.finish(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyPair.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyPair.java new file mode 100644 index 000000000..fc2c891cb --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyPair.java @@ -0,0 +1,62 @@ +package org.spongycastle.openpgp; + +import java.util.Date; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.RSASecretBCPGKey; + + +/** + * General class to handle JCA key pairs and convert them into OpenPGP ones. + * <p> + * A word for the unwary, the KeyID for a OpenPGP public key is calculated from + * a hash that includes the time of creation, if you pass a different date to the + * constructor below with the same public private key pair the KeyID will not be the + * same as for previous generations of the key, so ideally you only want to do + * this once. + */ +public class PGPKeyPair +{ + protected PGPPublicKey pub; + protected PGPPrivateKey priv; + + protected PGPKeyPair() + { + } + + /** + * Create a key pair from a PGPPrivateKey and a PGPPublicKey. + * + * @param pub the public key + * @param priv the private key + */ + public PGPKeyPair( + PGPPublicKey pub, + PGPPrivateKey priv) + { + this.pub = pub; + this.priv = priv; + } + + /** + * Return the keyID associated with this key pair. + * + * @return keyID + */ + public long getKeyID() + { + return pub.getKeyID(); + } + + public PGPPublicKey getPublicKey() + { + return pub; + } + + public PGPPrivateKey getPrivateKey() + { + return priv; + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyRingGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyRingGenerator.java new file mode 100644 index 000000000..b631bb8bd --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPKeyRingGenerator.java @@ -0,0 +1,151 @@ +package org.spongycastle.openpgp; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.PublicSubkeyPacket; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +/** + * Generator for a PGP master and subkey ring. This class will generate + * both the secret and public key rings + */ +public class PGPKeyRingGenerator +{ + List keys = new ArrayList(); + + private PBESecretKeyEncryptor keyEncryptor; + private PGPDigestCalculator checksumCalculator; + private PGPKeyPair masterKey; + private PGPSignatureSubpacketVector hashedPcks; + private PGPSignatureSubpacketVector unhashedPcks; + private PGPContentSignerBuilder keySignerBuilder; + + /** + * Create a new key ring generator. + * + * @param certificationLevel + * @param masterKey + * @param id + * @param checksumCalculator + * @param hashedPcks + * @param unhashedPcks + * @param keySignerBuilder + * @param keyEncryptor + * @throws PGPException + */ + public PGPKeyRingGenerator( + int certificationLevel, + PGPKeyPair masterKey, + String id, + PGPDigestCalculator checksumCalculator, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder keySignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this.masterKey = masterKey; + this.keyEncryptor = keyEncryptor; + this.checksumCalculator = checksumCalculator; + this.keySignerBuilder = keySignerBuilder; + this.hashedPcks = hashedPcks; + this.unhashedPcks = unhashedPcks; + + keys.add(new PGPSecretKey(certificationLevel, masterKey, id, checksumCalculator, hashedPcks, unhashedPcks, keySignerBuilder, keyEncryptor)); + } + + /** + * Add a sub key to the key ring to be generated with default certification and inheriting + * the hashed/unhashed packets of the master key. + * + * @param keyPair + * @throws PGPException + */ + public void addSubKey( + PGPKeyPair keyPair) + throws PGPException + { + addSubKey(keyPair, hashedPcks, unhashedPcks); + } + + /** + * Add a subkey with specific hashed and unhashed packets associated with it and default + * certification. + * + * @param keyPair public/private key pair. + * @param hashedPcks hashed packet values to be included in certification. + * @param unhashedPcks unhashed packets values to be included in certification. + * @throws PGPException + */ + public void addSubKey( + PGPKeyPair keyPair, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks) + throws PGPException + { + try + { + // + // generate the certification + // + PGPSignatureGenerator sGen = new PGPSignatureGenerator(keySignerBuilder); + + sGen.init(PGPSignature.SUBKEY_BINDING, masterKey.getPrivateKey()); + + sGen.setHashedSubpackets(hashedPcks); + sGen.setUnhashedSubpackets(unhashedPcks); + + List subSigs = new ArrayList(); + + subSigs.add(sGen.generateCertification(masterKey.getPublicKey(), keyPair.getPublicKey())); + + keys.add(new PGPSecretKey(keyPair.getPrivateKey(), new PGPPublicKey(keyPair.getPublicKey(), null, subSigs), checksumCalculator, keyEncryptor)); + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("exception adding subkey: ", e); + } + } + + /** + * Return the secret key ring. + * + * @return a secret key ring. + */ + public PGPSecretKeyRing generateSecretKeyRing() + { + return new PGPSecretKeyRing(keys); + } + + /** + * Return the public key ring that corresponds to the secret key ring. + * + * @return a public key ring. + */ + public PGPPublicKeyRing generatePublicKeyRing() + { + Iterator it = keys.iterator(); + List pubKeys = new ArrayList(); + + pubKeys.add(((PGPSecretKey)it.next()).getPublicKey()); + + while (it.hasNext()) + { + PGPPublicKey k = new PGPPublicKey(((PGPSecretKey)it.next()).getPublicKey()); + + k.publicPk = new PublicSubkeyPacket(k.getAlgorithm(), k.getCreationTime(), k.publicPk.getKey()); + + pubKeys.add(k); + } + + return new PGPPublicKeyRing(pubKeys); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPLiteralDataGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPLiteralDataGenerator.java new file mode 100644 index 000000000..5792a0b82 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPLiteralDataGenerator.java @@ -0,0 +1,167 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.util.Strings; + +/** + * Class for producing literal data packets. + */ +public class PGPLiteralDataGenerator implements StreamGenerator +{ + public static final char BINARY = PGPLiteralData.BINARY; + public static final char TEXT = PGPLiteralData.TEXT; + public static final char UTF8 = PGPLiteralData.UTF8; + + /** + * The special name indicating a "for your eyes only" packet. + */ + public static final String CONSOLE = PGPLiteralData.CONSOLE; + + /** + * The special time for a modification time of "now" or + * the present time. + */ + public static final Date NOW = PGPLiteralData.NOW; + + private BCPGOutputStream pkOut; + private boolean oldFormat = false; + + public PGPLiteralDataGenerator() + { + } + + /** + * Generates literal data objects in the old format, this is + * important if you need compatability with PGP 2.6.x. + * + * @param oldFormat + */ + public PGPLiteralDataGenerator( + boolean oldFormat) + { + this.oldFormat = oldFormat; + } + + private void writeHeader( + OutputStream out, + char format, + byte[] encName, + long modificationTime) + throws IOException + { + out.write(format); + + out.write((byte)encName.length); + + for (int i = 0; i != encName.length; i++) + { + out.write(encName[i]); + } + + long modDate = modificationTime / 1000; + + out.write((byte)(modDate >> 24)); + out.write((byte)(modDate >> 16)); + out.write((byte)(modDate >> 8)); + out.write((byte)(modDate)); + } + + /** + * Open a literal data packet, returning a stream to store the data inside + * the packet. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out the stream we want the packet in + * @param format the format we are using + * @param name the name of the "file" + * @param length the length of the data we will write + * @param modificationTime the time of last modification we want stored. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + long length, + Date modificationTime) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + byte[] encName = Strings.toUTF8ByteArray(name); + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, length + 2 + encName.length + 4, oldFormat); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Open a literal data packet, returning a stream to store the data inside + * the packet as an indefinite length stream. The stream is written out as a + * series of partial packets with a chunk size determined by the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * + * @param out the stream we want the packet in + * @param format the format we are using + * @param name the name of the "file" + * @param modificationTime the time of last modification we want stored. + * @param buffer the buffer to use for collecting data to put into chunks. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + Date modificationTime, + byte[] buffer) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, buffer); + + byte[] encName = Strings.toUTF8ByteArray(name); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Close the literal data packet - this is equivalent to calling close on the stream + * returned by the open() method. + * + * @throws IOException + */ + public void close() + throws IOException + { + if (pkOut != null) + { + pkOut.finish(); + pkOut.flush(); + pkOut = null; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPObjectFactory.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPObjectFactory.java new file mode 100644 index 000000000..50e292189 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPObjectFactory.java @@ -0,0 +1,151 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; + +/** + * General class for reading a PGP object stream. + * <p> + * Note: if this class finds a PGPPublicKey or a PGPSecretKey it + * will create a PGPPublicKeyRing, or a PGPSecretKeyRing for each + * key found. If all you are trying to do is read a key ring file use + * either PGPPublicKeyRingCollection or PGPSecretKeyRingCollection. + */ +public class PGPObjectFactory +{ + private BCPGInputStream in; + private KeyFingerPrintCalculator fingerPrintCalculator; + + public PGPObjectFactory( + InputStream in) + { + this(in, new BcKeyFingerprintCalculator()); + } + + /** + * Create an object factor suitable for reading keys, key rings and key ring collections. + * + * @param in stream to read from + * @param fingerPrintCalculator calculator to use in key finger print calculations. + */ + public PGPObjectFactory( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + { + this.in = new BCPGInputStream(in); + this.fingerPrintCalculator = fingerPrintCalculator; + } + + public PGPObjectFactory( + byte[] bytes) + { + this(new ByteArrayInputStream(bytes)); + } + + /** + * Create an object factor suitable for reading keys, key rings and key ring collections. + * + * @param bytes stream to read from + * @param fingerPrintCalculator calculator to use in key finger print calculations. + */ + public PGPObjectFactory( + byte[] bytes, + KeyFingerPrintCalculator fingerPrintCalculator) + { + this(new ByteArrayInputStream(bytes), fingerPrintCalculator); + } + + /** + * Return the next object in the stream, or null if the end is reached. + * + * @return Object + * @throws IOException on a parse error + */ + public Object nextObject() + throws IOException + { + List l; + + switch (in.nextPacketTag()) + { + case -1: + return null; + case PacketTags.SIGNATURE: + l = new ArrayList(); + + while (in.nextPacketTag() == PacketTags.SIGNATURE) + { + try + { + l.add(new PGPSignature(in)); + } + catch (PGPException e) + { + throw new IOException("can't create signature object: " + e); + } + } + + return new PGPSignatureList((PGPSignature[])l.toArray(new PGPSignature[l.size()])); + case PacketTags.SECRET_KEY: + try + { + return new PGPSecretKeyRing(in, fingerPrintCalculator); + } + catch (PGPException e) + { + throw new IOException("can't create secret key object: " + e); + } + case PacketTags.PUBLIC_KEY: + return new PGPPublicKeyRing(in, fingerPrintCalculator); + case PacketTags.PUBLIC_SUBKEY: + try + { + return PGPPublicKeyRing.readSubkey(in, fingerPrintCalculator); + } + catch (PGPException e) + { + throw new IOException("processing error: " + e.getMessage()); + } + case PacketTags.COMPRESSED_DATA: + throw new IOException("processing error: " + "compressed data not supported"); + case PacketTags.LITERAL_DATA: + return new PGPLiteralData(in); + case PacketTags.PUBLIC_KEY_ENC_SESSION: + case PacketTags.SYMMETRIC_KEY_ENC_SESSION: + return new PGPEncryptedDataList(in); + case PacketTags.ONE_PASS_SIGNATURE: + l = new ArrayList(); + + while (in.nextPacketTag() == PacketTags.ONE_PASS_SIGNATURE) + { + try + { + l.add(new PGPOnePassSignature(in)); + } + catch (PGPException e) + { + throw new IOException("can't create one pass signature object: " + e); + } + } + + return new PGPOnePassSignatureList((PGPOnePassSignature[])l.toArray(new PGPOnePassSignature[l.size()])); + case PacketTags.MARKER: + return new PGPMarker(in); + case PacketTags.EXPERIMENTAL_1: + case PacketTags.EXPERIMENTAL_2: + case PacketTags.EXPERIMENTAL_3: + case PacketTags.EXPERIMENTAL_4: + return in.readPacket(); + } + + throw new IOException("unknown object in stream: " + in.nextPacketTag()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPOnePassSignature.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPOnePassSignature.java new file mode 100644 index 000000000..4edb9a974 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPOnePassSignature.java @@ -0,0 +1,227 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; + +/** + * A one pass signature object. + */ +public class PGPOnePassSignature +{ + private OnePassSignaturePacket sigPack; + private int signatureType; + + private PGPContentVerifier verifier; + private byte lastb; + private OutputStream sigOut; + + PGPOnePassSignature( + BCPGInputStream pIn) + throws IOException, PGPException + { + this((OnePassSignaturePacket)pIn.readPacket()); + } + + PGPOnePassSignature( + OnePassSignaturePacket sigPack) + throws PGPException + { + this.sigPack = sigPack; + this.signatureType = sigPack.getSignatureType(); + } + + /** + * Initialise the signature object for verification. + * + * @param verifierBuilderProvider provider for a content verifier builder for the signature type of interest. + * @param pubKey the public key to use for verification + * @throws PGPException if there's an issue with creating the verifier. + */ + public void init(PGPContentVerifierBuilderProvider verifierBuilderProvider, PGPPublicKey pubKey) + throws PGPException + { + PGPContentVerifierBuilder verifierBuilder = verifierBuilderProvider.get(sigPack.getKeyAlgorithm(), sigPack.getHashAlgorithm()); + + verifier = verifierBuilder.build(pubKey); + + lastb = 0; + sigOut = verifier.getOutputStream(); + } + + public void update( + byte b) + throws PGPSignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] bytes) + throws PGPSignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + for (int i = 0; i != bytes.length; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, 0, bytes.length); + } + } + + public void update( + byte[] bytes, + int off, + int length) + throws PGPSignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, off, length); + } + } + + private void byteUpdate(byte b) + throws PGPSignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws PGPSignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + /** + * Verify the calculated signature against the passed in PGPSignature. + * + * @param pgpSig + * @return boolean + * @throws PGPException + */ + public boolean verify( + PGPSignature pgpSig) + throws PGPException + { + try + { + sigOut.write(pgpSig.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new PGPException("unable to add trailer: " + e.getMessage(), e); + } + + return verifier.verify(pgpSig.getSignature()); + } + + public long getKeyID() + { + return sigPack.getKeyID(); + } + + public int getSignatureType() + { + return sigPack.getSignatureType(); + } + + public int getHashAlgorithm() + { + return sigPack.getHashAlgorithm(); + } + + public int getKeyAlgorithm() + { + return sigPack.getKeyAlgorithm(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(sigPack); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java new file mode 100644 index 000000000..d13edf6a9 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPBEEncryptedData.java @@ -0,0 +1,141 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.InputStream; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; +import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.util.io.TeeInputStream; + +/** + * A password based encryption object. + */ +public class PGPPBEEncryptedData + extends PGPEncryptedData +{ + SymmetricKeyEncSessionPacket keyData; + + PGPPBEEncryptedData( + SymmetricKeyEncSessionPacket keyData, + InputStreamPacket encData) + { + super(encData); + + this.keyData = keyData; + } + + /** + * Return the raw input stream for the data stream. + * + * @return InputStream + */ + public InputStream getInputStream() + { + return encData.getInputStream(); + } + + /** + * Return the symmetric key algorithm required to decrypt the data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data. + * @return the integer encryption algorithm code. + * @throws PGPException if the session data cannot be recovered. + */ + public int getSymmetricAlgorithm( + PBEDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyData.getEncAlgorithm(), keyData.getS2K()); + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); + + return sessionData[0]; + } + + /** + * Open an input stream which will provide the decrypted data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data and provide the stream. + * @return the resulting input stream + * @throws PGPException if the session data cannot be recovered or the stream cannot be created. + */ + public InputStream getDataStream( + PBEDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + try + { + int keyAlgorithm = keyData.getEncAlgorithm(); + byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyAlgorithm, keyData.getS2K()); + boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket; + + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); + byte[] sessionKey = new byte[sessionData.length - 1]; + + System.arraycopy(sessionData, 1, sessionKey, 0, sessionKey.length); + + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionData[0] & 0xff, sessionKey); + + encStream = new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); + + if (withIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + integrityCalculator = dataDecryptor.getIntegrityCalculator(); + + encStream = new TeeInputStream(truncStream, integrityCalculator.getOutputStream()); + } + + byte[] iv = new byte[dataDecryptor.getBlockSize()]; + for (int i = 0; i != iv.length; i++) + { + int ch = encStream.read(); + + if (ch < 0) + { + throw new EOFException("unexpected end of stream."); + } + + iv[i] = (byte)ch; + } + + int v1 = encStream.read(); + int v2 = encStream.read(); + + if (v1 < 0 || v2 < 0) + { + throw new EOFException("unexpected end of stream."); + } + + + // Note: the oracle attack on "quick check" bytes is not deemed + // a security risk for PBE (see PGPPublicKeyEncryptedData) + + boolean repeatCheckPassed = iv[iv.length - 2] == (byte) v1 + && iv[iv.length - 1] == (byte) v2; + + // Note: some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + boolean zeroesCheckPassed = v1 == 0 && v2 == 0; + + if (!repeatCheckPassed && !zeroesCheckPassed) + { + throw new PGPDataValidationException("data check failed."); + } + + return encStream; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPrivateKey.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPrivateKey.java new file mode 100644 index 000000000..23c962b39 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPrivateKey.java @@ -0,0 +1,48 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSASecretBCPGKey; + +/** + * general class to contain a private key for use with other openPGP + * objects. + */ +public class PGPPrivateKey +{ + private long keyID; + private PublicKeyPacket publicKeyPacket; + private BCPGKey privateKeyDataPacket; + + public PGPPrivateKey( + long keyID, + PublicKeyPacket publicKeyPacket, + BCPGKey privateKeyDataPacket) + { + this.keyID = keyID; + this.publicKeyPacket = publicKeyPacket; + this.privateKeyDataPacket = privateKeyDataPacket; + } + + /** + * Return the keyID associated with the contained private key. + * + * @return long + */ + public long getKeyID() + { + return keyID; + } + + public PublicKeyPacket getPublicKeyPacket() + { + return publicKeyPacket; + } + + public BCPGKey getPrivateKeyDataPacket() + { + return privateKeyDataPacket; + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKey.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKey.java new file mode 100644 index 000000000..4318e3aa2 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKey.java @@ -0,0 +1,893 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.DSAPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalPublicBCPGKey; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserIDPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.util.Arrays; + +/** + * general class to handle a PGP public key object. + */ +public class PGPPublicKey + implements PublicKeyAlgorithmTags +{ + private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] { PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION }; + + PublicKeyPacket publicPk; + TrustPacket trustPk; + List keySigs = new ArrayList(); + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + + List subSigs = null; + + private long keyID; + private byte[] fingerprint; + private int keyStrength; + + private void init(KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + BCPGKey key = publicPk.getKey(); + + this.fingerprint = fingerPrintCalculator.calculateFingerprint(publicPk); + + if (publicPk.getVersion() <= 3) + { + RSAPublicBCPGKey rK = (RSAPublicBCPGKey)key; + + this.keyID = rK.getModulus().longValue(); + this.keyStrength = rK.getModulus().bitLength(); + } + else + { + this.keyID = ((long)(fingerprint[fingerprint.length - 8] & 0xff) << 56) + | ((long)(fingerprint[fingerprint.length - 7] & 0xff) << 48) + | ((long)(fingerprint[fingerprint.length - 6] & 0xff) << 40) + | ((long)(fingerprint[fingerprint.length - 5] & 0xff) << 32) + | ((long)(fingerprint[fingerprint.length - 4] & 0xff) << 24) + | ((long)(fingerprint[fingerprint.length - 3] & 0xff) << 16) + | ((long)(fingerprint[fingerprint.length - 2] & 0xff) << 8) + | ((fingerprint[fingerprint.length - 1] & 0xff)); + + if (key instanceof RSAPublicBCPGKey) + { + this.keyStrength = ((RSAPublicBCPGKey)key).getModulus().bitLength(); + } + else if (key instanceof DSAPublicBCPGKey) + { + this.keyStrength = ((DSAPublicBCPGKey)key).getP().bitLength(); + } + else if (key instanceof ElGamalPublicBCPGKey) + { + this.keyStrength = ((ElGamalPublicBCPGKey)key).getP().bitLength(); + } + } + } + + /** + * Create a PGP public key from a packet descriptor using the passed in fingerPrintCalculator to do calculate + * the fingerprint and keyID. + * + * @param publicKeyPacket packet describing the public key. + * @param fingerPrintCalculator calculator providing the digest support ot create the key fingerprint. + * @throws PGPException if the packet is faulty, or the required calculations fail. + */ + public PGPPublicKey(PublicKeyPacket publicKeyPacket, KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicKeyPacket; + this.ids = new ArrayList(); + this.idSigs = new ArrayList(); + + init(fingerPrintCalculator); + } + + /* + * Constructor for a sub-key. + */ + PGPPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + List sigs, + KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.subSigs = sigs; + + init(fingerPrintCalculator); + } + + PGPPublicKey( + PGPPublicKey key, + TrustPacket trust, + List subSigs) + { + this.publicPk = key.publicPk; + this.trustPk = trust; + this.subSigs = subSigs; + + this.fingerprint = key.fingerprint; + this.keyID = key.keyID; + this.keyStrength = key.keyStrength; + } + + /** + * Copy constructor. + * @param pubKey the public key to copy. + */ + PGPPublicKey( + PGPPublicKey pubKey) + { + this.publicPk = pubKey.publicPk; + + this.keySigs = new ArrayList(pubKey.keySigs); + this.ids = new ArrayList(pubKey.ids); + this.idTrusts = new ArrayList(pubKey.idTrusts); + this.idSigs = new ArrayList(pubKey.idSigs.size()); + for (int i = 0; i != pubKey.idSigs.size(); i++) + { + this.idSigs.add(new ArrayList((ArrayList)pubKey.idSigs.get(i))); + } + + if (pubKey.subSigs != null) + { + this.subSigs = new ArrayList(pubKey.subSigs.size()); + for (int i = 0; i != pubKey.subSigs.size(); i++) + { + this.subSigs.add(pubKey.subSigs.get(i)); + } + } + + this.fingerprint = pubKey.fingerprint; + this.keyID = pubKey.keyID; + this.keyStrength = pubKey.keyStrength; + } + + PGPPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + List keySigs, + List ids, + List idTrusts, + List idSigs, + KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + + init(fingerPrintCalculator); + } + + /** + * @return the version of this key. + */ + public int getVersion() + { + return publicPk.getVersion(); + } + + /** + * @return creation time of key. + */ + public Date getCreationTime() + { + return publicPk.getTime(); + } + + /** + * @return number of valid days from creation time - zero means no + * expiry. + */ + public int getValidDays() + { + if (publicPk.getVersion() > 3) + { + return (int)(this.getValidSeconds() / (24 * 60 * 60)); + } + else + { + return publicPk.getValidDays(); + } + } + + /** + * Return the trust data associated with the public key, if present. + * @return a byte array with trust data, null otherwise. + */ + public byte[] getTrustData() + { + if (trustPk == null) + { + return null; + } + + return Arrays.clone(trustPk.getLevelAndTrustAmount()); + } + + /** + * @return number of valid seconds from creation time - zero means no + * expiry. + */ + public long getValidSeconds() + { + if (publicPk.getVersion() > 3) + { + if (this.isMasterKey()) + { + for (int i = 0; i != MASTER_KEY_CERTIFICATION_TYPES.length; i++) + { + long seconds = getExpirationTimeFromSig(true, MASTER_KEY_CERTIFICATION_TYPES[i]); + + if (seconds >= 0) + { + return seconds; + } + } + } + else + { + long seconds = getExpirationTimeFromSig(false, PGPSignature.SUBKEY_BINDING); + + if (seconds >= 0) + { + return seconds; + } + } + + return 0; + } + else + { + return (long)publicPk.getValidDays() * 24 * 60 * 60; + } + } + + private long getExpirationTimeFromSig( + boolean selfSigned, + int signatureType) + { + Iterator signatures = this.getSignaturesOfType(signatureType); + long expiryTime = -1; + + while (signatures.hasNext()) + { + PGPSignature sig = (PGPSignature)signatures.next(); + + if (!selfSigned || sig.getKeyID() == this.getKeyID()) + { + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null) + { + long current = hashed.getKeyExpirationTime(); + + if (current == 0 || current > expiryTime) + { + expiryTime = current; + } + } + else + { + return 0; + } + } + } + + return expiryTime; + } + + /** + * Return the keyID associated with the public key. + * + * @return long + */ + public long getKeyID() + { + return keyID; + } + + /** + * Return the fingerprint of the key. + * + * @return key fingerprint. + */ + public byte[] getFingerprint() + { + byte[] tmp = new byte[fingerprint.length]; + + System.arraycopy(fingerprint, 0, tmp, 0, tmp.length); + + return tmp; + } + + /** + * Return true if this key has an algorithm type that makes it suitable to use for encryption. + * <p> + * Note: with version 4 keys KeyFlags subpackets should also be considered when present for + * determining the preferred use of the key. + * + * @return true if the key algorithm is suitable for encryption. + */ + public boolean isEncryptionKey() + { + int algorithm = publicPk.getAlgorithm(); + + return ((algorithm == RSA_GENERAL) || (algorithm == RSA_ENCRYPT) + || (algorithm == ELGAMAL_ENCRYPT) || (algorithm == ELGAMAL_GENERAL)); + } + + /** + * Return true if this is a master key. + * @return true if a master key. + */ + public boolean isMasterKey() + { + return (subSigs == null); + } + + /** + * Return the algorithm code associated with the public key. + * + * @return int + */ + public int getAlgorithm() + { + return publicPk.getAlgorithm(); + } + + /** + * Return the strength of the key in bits. + * + * @return bit strenght of key. + */ + public int getBitStrength() + { + return keyStrength; + } + + /** + * Return any userIDs associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserIDs() + { + List temp = new ArrayList(); + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof String) + { + temp.add(ids.get(i)); + } + } + + return temp.iterator(); + } + + /** + * Return any user attribute vectors associated with the key. + * + * @return an iterator of PGPUserAttributeSubpacketVector objects. + */ + public Iterator getUserAttributes() + { + List temp = new ArrayList(); + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof PGPUserAttributeSubpacketVector) + { + temp.add(ids.get(i)); + } + } + + return temp.iterator(); + } + + /** + * Return any signatures associated with the passed in id. + * + * @param id the id to be matched. + * @return an iterator of PGPSignature objects. + */ + public Iterator getSignaturesForID( + String id) + { + for (int i = 0; i != ids.size(); i++) + { + if (id.equals(ids.get(i))) + { + return ((ArrayList)idSigs.get(i)).iterator(); + } + } + + return null; + } + + /** + * Return an iterator of signatures associated with the passed in user attributes. + * + * @param userAttributes the vector of user attributes to be matched. + * @return an iterator of PGPSignature objects. + */ + public Iterator getSignaturesForUserAttribute( + PGPUserAttributeSubpacketVector userAttributes) + { + for (int i = 0; i != ids.size(); i++) + { + if (userAttributes.equals(ids.get(i))) + { + return ((ArrayList)idSigs.get(i)).iterator(); + } + } + + return null; + } + + /** + * Return signatures of the passed in type that are on this key. + * + * @param signatureType the type of the signature to be returned. + * @return an iterator (possibly empty) of signatures of the given type. + */ + public Iterator getSignaturesOfType( + int signatureType) + { + List l = new ArrayList(); + Iterator it = this.getSignatures(); + + while (it.hasNext()) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == signatureType) + { + l.add(sig); + } + } + + return l.iterator(); + } + + /** + * Return all signatures/certifications associated with this key. + * + * @return an iterator (possibly empty) with all signatures/certifications. + */ + public Iterator getSignatures() + { + if (subSigs == null) + { + List sigs = new ArrayList(); + + sigs.addAll(keySigs); + + for (int i = 0; i != idSigs.size(); i++) + { + sigs.addAll((Collection)idSigs.get(i)); + } + + return sigs.iterator(); + } + else + { + return subSigs.iterator(); + } + } + + public PublicKeyPacket getPublicKeyPacket() + { + return publicPk; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(publicPk); + if (trustPk != null) + { + out.writePacket(trustPk); + } + + if (subSigs == null) // not a sub-key + { + for (int i = 0; i != keySigs.size(); i++) + { + ((PGPSignature)keySigs.get(i)).encode(out); + } + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof String) + { + String id = (String)ids.get(i); + + out.writePacket(new UserIDPacket(id)); + } + else + { + PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)ids.get(i); + + out.writePacket(new UserAttributePacket(v.toSubpacketArray())); + } + + if (idTrusts.get(i) != null) + { + out.writePacket((ContainedPacket)idTrusts.get(i)); + } + + List sigs = (List)idSigs.get(i); + for (int j = 0; j != sigs.size(); j++) + { + ((PGPSignature)sigs.get(j)).encode(out); + } + } + } + else + { + for (int j = 0; j != subSigs.size(); j++) + { + ((PGPSignature)subSigs.get(j)).encode(out); + } + } + } + + /** + * Check whether this (sub)key has a revocation signature on it. + * + * @return boolean indicating whether this (sub)key has been revoked. + */ + public boolean isRevoked() + { + int ns = 0; + boolean revoked = false; + + if (this.isMasterKey()) // Master key + { + while (!revoked && (ns < keySigs.size())) + { + if (((PGPSignature)keySigs.get(ns++)).getSignatureType() == PGPSignature.KEY_REVOCATION) + { + revoked = true; + } + } + } + else // Sub-key + { + while (!revoked && (ns < subSigs.size())) + { + if (((PGPSignature)subSigs.get(ns++)).getSignatureType() == PGPSignature.SUBKEY_REVOCATION) + { + revoked = true; + } + } + } + + return revoked; + } + + + /** + * Add a certification for an id to the given public key. + * + * @param key the key the certification is to be added to. + * @param id the id the certification is associated with. + * @param certification the new certification. + * @return the re-certified key. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + String id, + PGPSignature certification) + { + return addCert(key, id, certification); + } + + /** + * Add a certification for the given UserAttributeSubpackets to the given public key. + * + * @param key the key the certification is to be added to. + * @param userAttributes the attributes the certification is associated with. + * @param certification the new certification. + * @return the re-certified key. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes, + PGPSignature certification) + { + return addCert(key, userAttributes, certification); + } + + private static PGPPublicKey addCert( + PGPPublicKey key, + Object id, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + List sigList = null; + + for (int i = 0; i != returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + sigList = (List)returnKey.idSigs.get(i); + } + } + + if (sigList != null) + { + sigList.add(certification); + } + else + { + sigList = new ArrayList(); + + sigList.add(certification); + returnKey.ids.add(id); + returnKey.idTrusts.add(null); + returnKey.idSigs.add(sigList); + } + + return returnKey; + } + + /** + * Remove any certifications associated with a given user attribute subpacket + * on a key. + * + * @param key the key the certifications are to be removed from. + * @param userAttributes the attributes to be removed. + * @return the re-certified key, null if the user attribute subpacket was not found on the key. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes) + { + return removeCert(key, userAttributes); + } + + /** + * Remove any certifications associated with a given id on a key. + * + * @param key the key the certifications are to be removed from. + * @param id the id that is to be removed. + * @return the re-certified key, null if the id was not found on the key. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + String id) + { + return removeCert(key, id); + } + + private static PGPPublicKey removeCert( + PGPPublicKey key, + Object id) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found = false; + + for (int i = 0; i < returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + found = true; + returnKey.ids.remove(i); + returnKey.idTrusts.remove(i); + returnKey.idSigs.remove(i); + } + } + + if (!found) + { + return null; + } + + return returnKey; + } + + /** + * Remove a certification associated with a given id on a key. + * + * @param key the key the certifications are to be removed from. + * @param id the id that the certification is to be removed from. + * @param certification the certification to be removed. + * @return the re-certified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + String id, + PGPSignature certification) + { + return removeCert(key, id, certification); + } + + /** + * Remove a certification associated with a given user attributes on a key. + * + * @param key the key the certifications are to be removed from. + * @param userAttributes the user attributes that the certification is to be removed from. + * @param certification the certification to be removed. + * @return the re-certified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes, + PGPSignature certification) + { + return removeCert(key, userAttributes, certification); + } + + private static PGPPublicKey removeCert( + PGPPublicKey key, + Object id, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found = false; + + for (int i = 0; i < returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + found = ((List)returnKey.idSigs.get(i)).remove(certification); + } + } + + if (!found) + { + return null; + } + + return returnKey; + } + + /** + * Add a revocation or some other key certification to a key. + * + * @param key the key the revocation is to be added to. + * @param certification the key signature to be added. + * @return the new changed public key object. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + PGPSignature certification) + { + if (key.isMasterKey()) + { + if (certification.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) + { + throw new IllegalArgumentException("signature type incorrect for master key revocation."); + } + } + else + { + if (certification.getSignatureType() == PGPSignature.KEY_REVOCATION) + { + throw new IllegalArgumentException("signature type incorrect for sub-key revocation."); + } + } + + PGPPublicKey returnKey = new PGPPublicKey(key); + + if (returnKey.subSigs != null) + { + returnKey.subSigs.add(certification); + } + else + { + returnKey.keySigs.add(certification); + } + + return returnKey; + } + + /** + * Remove a certification from the key. + * + * @param key the key the certifications are to be removed from. + * @param certification the certification to be removed. + * @return the modified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found; + + if (returnKey.subSigs != null) + { + found = returnKey.subSigs.remove(certification); + } + else + { + found = returnKey.keySigs.remove(certification); + } + + if (!found) + { + for (Iterator it = key.getUserIDs(); it.hasNext();) + { + String id = (String)it.next(); + for (Iterator sIt = key.getSignaturesForID(id); sIt.hasNext();) + { + if (certification == sIt.next()) + { + found = true; + returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); + } + } + } + + if (!found) + { + for (Iterator it = key.getUserAttributes(); it.hasNext();) + { + PGPUserAttributeSubpacketVector id = (PGPUserAttributeSubpacketVector)it.next(); + for (Iterator sIt = key.getSignaturesForUserAttribute(id); sIt.hasNext();) + { + if (certification == sIt.next()) + { + found = true; + returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); + } + } + } + } + } + + return returnKey; + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java new file mode 100644 index 000000000..e3e71cfac --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java @@ -0,0 +1,167 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.InputStream; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.PublicKeyEncSessionPacket; +import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.util.io.TeeInputStream; + +/** + * A public key encrypted data object. + */ +public class PGPPublicKeyEncryptedData + extends PGPEncryptedData +{ + PublicKeyEncSessionPacket keyData; + + PGPPublicKeyEncryptedData( + PublicKeyEncSessionPacket keyData, + InputStreamPacket encData) + { + super(encData); + + this.keyData = keyData; + } + + private boolean confirmCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + return (sessionInfo[sessionInfo.length - 2] == (byte)(check >> 8)) + && (sessionInfo[sessionInfo.length - 1] == (byte)(check)); + } + + /** + * Return the keyID for the key used to encrypt the data. + * + * @return long + */ + public long getKeyID() + { + return keyData.getKeyID(); + } + + /** + * Return the symmetric key algorithm required to decrypt the data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data. + * @return the integer encryption algorithm code. + * @throws PGPException if the session data cannot be recovered. + */ + public int getSymmetricAlgorithm( + PublicKeyDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] plain = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey()); + + return plain[0]; + } + + /** + * Open an input stream which will provide the decrypted data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data and provide the stream. + * @return the resulting input stream + * @throws PGPException if the session data cannot be recovered or the stream cannot be created. + */ + public InputStream getDataStream( + PublicKeyDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey()); + + if (!confirmCheckSum(sessionData)) + { + throw new PGPKeyValidationException("key checksum failed"); + } + + if (sessionData[0] != SymmetricKeyAlgorithmTags.NULL) + { + try + { + boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket; + byte[] sessionKey = new byte[sessionData.length - 3]; + + System.arraycopy(sessionData, 1, sessionKey, 0, sessionKey.length); + + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionData[0] & 0xff, sessionKey); + + encStream = new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); + + if (withIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + integrityCalculator = dataDecryptor.getIntegrityCalculator(); + + encStream = new TeeInputStream(truncStream, integrityCalculator.getOutputStream()); + } + + byte[] iv = new byte[dataDecryptor.getBlockSize()]; + + for (int i = 0; i != iv.length; i++) + { + int ch = encStream.read(); + + if (ch < 0) + { + throw new EOFException("unexpected end of stream."); + } + + iv[i] = (byte)ch; + } + + int v1 = encStream.read(); + int v2 = encStream.read(); + + if (v1 < 0 || v2 < 0) + { + throw new EOFException("unexpected end of stream."); + } + + // + // some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + // + /* + * Commented out in the light of the oracle attack. + if (iv[iv.length - 2] != (byte)v1 && v1 != 0) + { + throw new PGPDataValidationException("data check failed."); + } + + if (iv[iv.length - 1] != (byte)v2 && v2 != 0) + { + throw new PGPDataValidationException("data check failed."); + } + */ + + return encStream; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception starting decryption", e); + } + } + else + { + return encData.getInputStream(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyRing.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyRing.java new file mode 100644 index 000000000..35620ff24 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPPublicKeyRing.java @@ -0,0 +1,252 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; + +/** + * Class to hold a single master public key and its subkeys. + * <p> + * Often PGP keyring files consist of multiple master keys, if you are trying to process + * or construct one of these you should use the PGPPublicKeyRingCollection class. + */ +public class PGPPublicKeyRing + extends PGPKeyRing +{ + List keys; + + public PGPPublicKeyRing( + byte[] encoding, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException + { + this(new ByteArrayInputStream(encoding), fingerPrintCalculator); + } + + /** + * @param pubKeys + */ + PGPPublicKeyRing( + List pubKeys) + { + this.keys = pubKeys; + } + + public PGPPublicKeyRing( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException + { + this.keys = new ArrayList(); + + BCPGInputStream pIn = wrap(in); + + int initialTag = pIn.nextPacketTag(); + if (initialTag != PacketTags.PUBLIC_KEY && initialTag != PacketTags.PUBLIC_SUBKEY) + { + throw new IOException( + "public key ring doesn't start with public key tag: " + + "tag 0x" + Integer.toHexString(initialTag)); + } + + PublicKeyPacket pubPk = (PublicKeyPacket)pIn.readPacket(); + TrustPacket trustPk = readOptionalTrustPacket(pIn); + + // direct signatures and revocations + List keySigs = readSignaturesAndTrust(pIn); + + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + readUserIDs(pIn, ids, idTrusts, idSigs); + + try + { + keys.add(new PGPPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs, fingerPrintCalculator)); + + // Read subkeys + while (pIn.nextPacketTag() == PacketTags.PUBLIC_SUBKEY) + { + keys.add(readSubkey(pIn, fingerPrintCalculator)); + } + } + catch (PGPException e) + { + throw new IOException("processing exception: " + e.toString()); + } + } + + /** + * Return the first public key in the ring. + * + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey() + { + return (PGPPublicKey)keys.get(0); + } + + /** + * Return the public key referred to by the passed in keyID if it + * is present. + * + * @param keyID + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey( + long keyID) + { + for (int i = 0; i != keys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + if (keyID == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator containing all the public keys. + * + * @return Iterator + */ + public Iterator getPublicKeys() + { + return Collections.unmodifiableList(keys).iterator(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + for (int i = 0; i != keys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + k.encode(outStream); + } + } + + /** + * Returns a new key ring with the public key passed in + * either added or replacing an existing one. + * + * @param pubRing the public key ring to be modified + * @param pubKey the public key to be inserted. + * @return a new keyRing + */ + public static PGPPublicKeyRing insertPublicKey( + PGPPublicKeyRing pubRing, + PGPPublicKey pubKey) + { + List keys = new ArrayList(pubRing.keys); + boolean found = false; + boolean masterFound = false; + + for (int i = 0; i != keys.size();i++) + { + PGPPublicKey key = (PGPPublicKey)keys.get(i); + + if (key.getKeyID() == pubKey.getKeyID()) + { + found = true; + keys.set(i, pubKey); + } + if (key.isMasterKey()) + { + masterFound = true; + } + } + + if (!found) + { + if (pubKey.isMasterKey()) + { + if (masterFound) + { + throw new IllegalArgumentException("cannot add a master key to a ring that already has one"); + } + + keys.add(0, pubKey); + } + else + { + keys.add(pubKey); + } + } + + return new PGPPublicKeyRing(keys); + } + + /** + * Returns a new key ring with the public key passed in + * removed from the key ring. + * + * @param pubRing the public key ring to be modified + * @param pubKey the public key to be removed. + * @return a new keyRing, null if pubKey is not found. + */ + public static PGPPublicKeyRing removePublicKey( + PGPPublicKeyRing pubRing, + PGPPublicKey pubKey) + { + List keys = new ArrayList(pubRing.keys); + boolean found = false; + + for (int i = 0; i < keys.size();i++) + { + PGPPublicKey key = (PGPPublicKey)keys.get(i); + + if (key.getKeyID() == pubKey.getKeyID()) + { + found = true; + keys.remove(i); + } + } + + if (!found) + { + return null; + } + + return new PGPPublicKeyRing(keys); + } + + static PGPPublicKey readSubkey(BCPGInputStream in, KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + PublicKeyPacket pk = (PublicKeyPacket)in.readPacket(); + TrustPacket kTrust = readOptionalTrustPacket(in); + + // PGP 8 actually leaves out the signature. + List sigList = readSignaturesAndTrust(in); + + return new PGPPublicKey(pk, kTrust, sigList, fingerPrintCalculator); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java new file mode 100644 index 000000000..2554be8f1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java @@ -0,0 +1,701 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGObject; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SecretKeyPacket; +import org.spongycastle.bcpg.SecretSubkeyPacket; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserIDPacket; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +/** + * general class to handle a PGP secret key object. + */ +public class PGPSecretKey +{ + SecretKeyPacket secret; + PGPPublicKey pub; + + PGPSecretKey( + SecretKeyPacket secret, + PGPPublicKey pub) + { + this.secret = secret; + this.pub = pub; + } + + PGPSecretKey( + PGPPrivateKey privKey, + PGPPublicKey pubKey, + PGPDigestCalculator checksumCalculator, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(privKey, pubKey, checksumCalculator, false, keyEncryptor); + } + + PGPSecretKey( + PGPPrivateKey privKey, + PGPPublicKey pubKey, + PGPDigestCalculator checksumCalculator, + boolean isMasterKey, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this.pub = pubKey; + + BCPGObject secKey = (BCPGObject)privKey.getPrivateKeyDataPacket(); + + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.writeObject(secKey); + + byte[] keyData = bOut.toByteArray(); + + pOut.write(checksum(checksumCalculator, keyData, keyData.length)); + + int encAlgorithm = keyEncryptor.getAlgorithm(); + + if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL) + { + keyData = bOut.toByteArray(); // include checksum + + byte[] encData = keyEncryptor.encryptKeyData(keyData, 0, keyData.length); + byte[] iv = keyEncryptor.getCipherIV(); + + S2K s2k = keyEncryptor.getS2K(); + + int s2kUsage; + + if (checksumCalculator != null) + { + if (checksumCalculator.getAlgorithm() != HashAlgorithmTags.SHA1) + { + throw new PGPException("only SHA1 supported for key checksum calculations."); + } + s2kUsage = SecretKeyPacket.USAGE_SHA1; + } + else + { + s2kUsage = SecretKeyPacket.USAGE_CHECKSUM; + } + + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + } + else + { + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray()); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray()); + } + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception encrypting key", e); + } + } + + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(certificationLevel, keyPair, id, null, hashedPcks, unhashedPcks, certificationSignerBuilder, keyEncryptor); + } + + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPDigestCalculator checksumCalculator, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(keyPair.getPrivateKey(), certifiedPublicKey(certificationLevel, keyPair, id, hashedPcks, unhashedPcks, certificationSignerBuilder), checksumCalculator, true, keyEncryptor); + } + + private static PGPPublicKey certifiedPublicKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder) + throws PGPException + { + PGPSignatureGenerator sGen; + + try + { + sGen = new PGPSignatureGenerator(certificationSignerBuilder); + } + catch (Exception e) + { + throw new PGPException("creating signature generator: " + e, e); + } + + // + // generate the certification + // + sGen.init(certificationLevel, keyPair.getPrivateKey()); + + sGen.setHashedSubpackets(hashedPcks); + sGen.setUnhashedSubpackets(unhashedPcks); + + try + { + PGPSignature certification = sGen.generateCertification(id, keyPair.getPublicKey()); + + return PGPPublicKey.addCertification(keyPair.getPublicKey(), id, certification); + } + catch (Exception e) + { + throw new PGPException("exception doing certification: " + e, e); + } + } + + /** + * Return true if this key has an algorithm type that makes it suitable to use for signing. + * <p> + * Note: with version 4 keys KeyFlags subpackets should also be considered when present for + * determining the preferred use of the key. + * + * @return true if this key algorithm is suitable for use with signing. + */ + public boolean isSigningKey() + { + int algorithm = pub.getAlgorithm(); + + return ((algorithm == PGPPublicKey.RSA_GENERAL) || (algorithm == PGPPublicKey.RSA_SIGN) + || (algorithm == PGPPublicKey.DSA) || (algorithm == PGPPublicKey.ECDSA) || (algorithm == PGPPublicKey.ELGAMAL_GENERAL)); + } + + /** + * Return true if this is a master key. + * @return true if a master key. + */ + public boolean isMasterKey() + { + return pub.isMasterKey(); + } + + /** + * Detect if the Secret Key's Private Key is empty or not + * + * @return boolean whether or not the private key is empty + */ + public boolean isPrivateKeyEmpty() + { + byte[] secKeyData = secret.getSecretKeyData(); + + return (secKeyData == null || secKeyData.length < 1); + } + + /** + * return the algorithm the key is encrypted with. + * + * @return the algorithm used to encrypt the secret key. + */ + public int getKeyEncryptionAlgorithm() + { + return secret.getEncAlgorithm(); + } + + /** + * Return the keyID of the public key associated with this key. + * + * @return the keyID associated with this key. + */ + public long getKeyID() + { + return pub.getKeyID(); + } + + /** + * Return the public key associated with this key. + * + * @return the public key for this key. + */ + public PGPPublicKey getPublicKey() + { + return pub; + } + + /** + * Return any userIDs associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserIDs() + { + return pub.getUserIDs(); + } + + /** + * Return any user attribute vectors associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserAttributes() + { + return pub.getUserAttributes(); + } + + private byte[] extractKeyData( + PBESecretKeyDecryptor decryptorFactory) + throws PGPException + { + byte[] encData = secret.getSecretKeyData(); + byte[] data = null; + + if (secret.getEncAlgorithm() != SymmetricKeyAlgorithmTags.NULL) + { + try + { + if (secret.getPublicKeyPacket().getVersion() == 4) + { + byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); + + data = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, secret.getIV(), encData, 0, encData.length); + + boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1; + byte[] check = checksum(useSHA1 ? decryptorFactory.getChecksumCalculator(HashAlgorithmTags.SHA1) : null, data, (useSHA1) ? data.length - 20 : data.length - 2); + + for (int i = 0; i != check.length; i++) + { + if (check[i] != data[data.length - check.length + i]) + { + throw new PGPException("checksum mismatch at " + i + " of " + check.length); + } + } + } + else // version 2 or 3, RSA only. + { + byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); + + data = new byte[encData.length]; + + byte[] iv = new byte[secret.getIV().length]; + + System.arraycopy(secret.getIV(), 0, iv, 0, iv.length); + + // + // read in the four numbers + // + int pos = 0; + + for (int i = 0; i != 4; i++) + { + int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + byte[] tmp = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, iv, encData, pos + 2, encLen); + System.arraycopy(tmp, 0, data, pos + 2, tmp.length); + pos += 2 + encLen; + + if (i != 3) + { + System.arraycopy(encData, pos - iv.length, iv, 0, iv.length); + } + } + + // + // verify and copy checksum + // + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); + int calcCs = 0; + for (int j = 0; j < data.length - 2; j++) + { + calcCs += data[j] & 0xff; + } + + calcCs &= 0xffff; + if (calcCs != cs) + { + throw new PGPException("checksum mismatch: passphrase wrong, expected " + + Integer.toHexString(cs) + + " found " + Integer.toHexString(calcCs)); + } + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception decrypting key", e); + } + } + else + { + data = encData; + } + + return data; + } + + /** + * Extract a PGPPrivate key from the SecretKey's encrypted contents. + * + * @param decryptorFactory factory to use to generate a decryptor for the passed in secretKey. + * @return PGPPrivateKey the unencrypted private key. + * @throws PGPException on failure. + */ + public PGPPrivateKey extractPrivateKey( + PBESecretKeyDecryptor decryptorFactory) + throws PGPException + { + if (isPrivateKeyEmpty()) + { + return null; + } + + PublicKeyPacket pubPk = secret.getPublicKeyPacket(); + + try + { + byte[] data = extractKeyData(decryptorFactory); + BCPGInputStream in = new BCPGInputStream(new ByteArrayInputStream(data)); + + + switch (pubPk.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: + RSASecretBCPGKey rsaPriv = new RSASecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, rsaPriv); + case PGPPublicKey.DSA: + DSASecretBCPGKey dsaPriv = new DSASecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, dsaPriv); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalSecretBCPGKey elPriv = new ElGamalSecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, elPriv); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception constructing key", e); + } + } + + private static byte[] checksum(PGPDigestCalculator digCalc, byte[] bytes, int length) + throws PGPException + { + if (digCalc != null) + { + OutputStream dOut = digCalc.getOutputStream(); + + try + { + dOut.write(bytes, 0, length); + + dOut.close(); + } + catch (Exception e) + { + throw new PGPException("checksum digest calculation failed: " + e.getMessage(), e); + } + return digCalc.getDigest(); + } + else + { + int checksum = 0; + + for (int i = 0; i != length; i++) + { + checksum += bytes[i] & 0xff; + } + + byte[] check = new byte[2]; + + check[0] = (byte)(checksum >> 8); + check[1] = (byte)checksum; + + return check; + } + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(secret); + if (pub.trustPk != null) + { + out.writePacket(pub.trustPk); + } + + if (pub.subSigs == null) // is not a sub key + { + for (int i = 0; i != pub.keySigs.size(); i++) + { + ((PGPSignature)pub.keySigs.get(i)).encode(out); + } + + for (int i = 0; i != pub.ids.size(); i++) + { + if (pub.ids.get(i) instanceof String) + { + String id = (String)pub.ids.get(i); + + out.writePacket(new UserIDPacket(id)); + } + else + { + PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)pub.ids.get(i); + + out.writePacket(new UserAttributePacket(v.toSubpacketArray())); + } + + if (pub.idTrusts.get(i) != null) + { + out.writePacket((ContainedPacket)pub.idTrusts.get(i)); + } + + List sigs = (ArrayList)pub.idSigs.get(i); + + for (int j = 0; j != sigs.size(); j++) + { + ((PGPSignature)sigs.get(j)).encode(out); + } + } + } + else + { + for (int j = 0; j != pub.subSigs.size(); j++) + { + ((PGPSignature)pub.subSigs.get(j)).encode(out); + } + } + } + + /** + * Return a copy of the passed in secret key, encrypted using a new + * password and the passed in algorithm. + * + * @param key the PGPSecretKey to be copied. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. + */ + public static PGPSecretKey copyWithNewPassword( + PGPSecretKey key, + PBESecretKeyDecryptor oldKeyDecryptor, + PBESecretKeyEncryptor newKeyEncryptor) + throws PGPException + { + if (key.isPrivateKeyEmpty()) + { + throw new PGPException("no private key in this SecretKey - public key present only."); + } + + byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor); + int s2kUsage = key.secret.getS2KUsage(); + byte[] iv = null; + S2K s2k = null; + byte[] keyData; + int newEncAlgorithm = SymmetricKeyAlgorithmTags.NULL; + + if (newKeyEncryptor == null || newKeyEncryptor.getAlgorithm() == SymmetricKeyAlgorithmTags.NULL) + { + s2kUsage = SecretKeyPacket.USAGE_NONE; + if (key.secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1) // SHA-1 hash, need to rewrite checksum + { + keyData = new byte[rawKeyData.length - 18]; + + System.arraycopy(rawKeyData, 0, keyData, 0, keyData.length - 2); + + byte[] check = checksum(null, keyData, keyData.length - 2); + + keyData[keyData.length - 2] = check[0]; + keyData[keyData.length - 1] = check[1]; + } + else + { + keyData = rawKeyData; + } + } + else + { + if (key.secret.getPublicKeyPacket().getVersion() < 4) + { + // Version 2 or 3 - RSA Keys only + + byte[] encKey = newKeyEncryptor.getKey(); + keyData = new byte[rawKeyData.length]; + + if (newKeyEncryptor.getS2K() != null) + { + throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor."); + } + + // + // process 4 numbers + // + int pos = 0; + for (int i = 0; i != 4; i++) + { + int encLen = (((rawKeyData[pos] << 8) | (rawKeyData[pos + 1] & 0xff)) + 7) / 8; + + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + byte[] tmp; + if (i == 0) + { + tmp = newKeyEncryptor.encryptKeyData(encKey, rawKeyData, pos + 2, encLen); + iv = newKeyEncryptor.getCipherIV(); + + } + else + { + byte[] tmpIv = new byte[iv.length]; + + System.arraycopy(keyData, pos - iv.length, tmpIv, 0, tmpIv.length); + tmp = newKeyEncryptor.encryptKeyData(encKey, tmpIv, rawKeyData, pos + 2, encLen); + } + + System.arraycopy(tmp, 0, keyData, pos + 2, tmp.length); + pos += 2 + encLen; + } + + // + // copy in checksum. + // + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + s2k = newKeyEncryptor.getS2K(); + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } + else + { + keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length); + + iv = newKeyEncryptor.getCipherIV(); + + s2k = newKeyEncryptor.getS2K(); + + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } + } + + SecretKeyPacket secret; + if (key.secret instanceof SecretSubkeyPacket) + { + secret = new SecretSubkeyPacket(key.secret.getPublicKeyPacket(), + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + else + { + secret = new SecretKeyPacket(key.secret.getPublicKeyPacket(), + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + + return new PGPSecretKey(secret, key.pub); + } + + /** + * Replace the passed the public key on the passed in secret key. + * + * @param secretKey secret key to change + * @param publicKey new public key. + * @return a new secret key. + * @throws IllegalArgumentException if keyIDs do not match. + */ + public static PGPSecretKey replacePublicKey(PGPSecretKey secretKey, PGPPublicKey publicKey) + { + if (publicKey.getKeyID() != secretKey.getKeyID()) + { + throw new IllegalArgumentException("keyIDs do not match"); + } + + return new PGPSecretKey(secretKey.secret, publicKey); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKeyRing.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKeyRing.java new file mode 100644 index 000000000..b54f34084 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKeyRing.java @@ -0,0 +1,402 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.PublicSubkeyPacket; +import org.spongycastle.bcpg.SecretKeyPacket; +import org.spongycastle.bcpg.SecretSubkeyPacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; + +/** + * Class to hold a single master secret key and its subkeys. + * <p> + * Often PGP keyring files consist of multiple master keys, if you are trying to process + * or construct one of these you should use the PGPSecretKeyRingCollection class. + */ +public class PGPSecretKeyRing + extends PGPKeyRing +{ + List keys; + List extraPubKeys; + + PGPSecretKeyRing(List keys) + { + this(keys, new ArrayList()); + } + + private PGPSecretKeyRing(List keys, List extraPubKeys) + { + this.keys = keys; + this.extraPubKeys = extraPubKeys; + } + + public PGPSecretKeyRing( + byte[] encoding, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + this(new ByteArrayInputStream(encoding), fingerPrintCalculator); + } + + public PGPSecretKeyRing( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + this.keys = new ArrayList(); + this.extraPubKeys = new ArrayList(); + + BCPGInputStream pIn = wrap(in); + + int initialTag = pIn.nextPacketTag(); + if (initialTag != PacketTags.SECRET_KEY && initialTag != PacketTags.SECRET_SUBKEY) + { + throw new IOException( + "secret key ring doesn't start with secret key tag: " + + "tag 0x" + Integer.toHexString(initialTag)); + } + + SecretKeyPacket secret = (SecretKeyPacket)pIn.readPacket(); + + // + // ignore GPG comment packets if found. + // + while (pIn.nextPacketTag() == PacketTags.EXPERIMENTAL_2) + { + pIn.readPacket(); + } + + TrustPacket trust = readOptionalTrustPacket(pIn); + + // revocation and direct signatures + List keySigs = readSignaturesAndTrust(pIn); + + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + readUserIDs(pIn, ids, idTrusts, idSigs); + + keys.add(new PGPSecretKey(secret, new PGPPublicKey(secret.getPublicKeyPacket(), trust, keySigs, ids, idTrusts, idSigs, fingerPrintCalculator))); + + + // Read subkeys + while (pIn.nextPacketTag() == PacketTags.SECRET_SUBKEY + || pIn.nextPacketTag() == PacketTags.PUBLIC_SUBKEY) + { + if (pIn.nextPacketTag() == PacketTags.SECRET_SUBKEY) + { + SecretSubkeyPacket sub = (SecretSubkeyPacket)pIn.readPacket(); + + // + // ignore GPG comment packets if found. + // + while (pIn.nextPacketTag() == PacketTags.EXPERIMENTAL_2) + { + pIn.readPacket(); + } + + TrustPacket subTrust = readOptionalTrustPacket(pIn); + List sigList = readSignaturesAndTrust(pIn); + + keys.add(new PGPSecretKey(sub, new PGPPublicKey(sub.getPublicKeyPacket(), subTrust, sigList, fingerPrintCalculator))); + } + else + { + PublicSubkeyPacket sub = (PublicSubkeyPacket)pIn.readPacket(); + + TrustPacket subTrust = readOptionalTrustPacket(pIn); + List sigList = readSignaturesAndTrust(pIn); + + extraPubKeys.add(new PGPPublicKey(sub, subTrust, sigList, fingerPrintCalculator)); + } + } + } + + /** + * Return the public key for the master key. + * + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey() + { + return ((PGPSecretKey)keys.get(0)).getPublicKey(); + } + + /** + * Return the public key referred to by the passed in keyID if it + * is present. + * + * @param keyID + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey( + long keyID) + { + PGPSecretKey key = getSecretKey(keyID); + if (key != null) + { + return key.getPublicKey(); + } + + for (int i = 0; i != extraPubKeys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + if (keyID == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator containing all the public keys. + * + * @return Iterator + */ + public Iterator getPublicKeys() + { + List pubKeys = new ArrayList(); + + for (Iterator it = getSecretKeys(); it.hasNext();) + { + pubKeys.add(((PGPSecretKey)it.next()).getPublicKey()); + } + + pubKeys.addAll(extraPubKeys); + + return Collections.unmodifiableList(pubKeys).iterator(); + } + + /** + * Return the master private key. + * + * @return PGPSecretKey + */ + public PGPSecretKey getSecretKey() + { + return ((PGPSecretKey)keys.get(0)); + } + + /** + * Return an iterator containing all the secret keys. + * + * @return Iterator + */ + public Iterator getSecretKeys() + { + return Collections.unmodifiableList(keys).iterator(); + } + + public PGPSecretKey getSecretKey( + long keyId) + { + for (int i = 0; i != keys.size(); i++) + { + PGPSecretKey k = (PGPSecretKey)keys.get(i); + + if (keyId == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator of the public keys in the secret key ring that + * have no matching private key. At the moment only personal certificate data + * appears in this fashion. + * + * @return iterator of unattached, or extra, public keys. + */ + public Iterator getExtraPublicKeys() + { + return extraPubKeys.iterator(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + for (int i = 0; i != keys.size(); i++) + { + PGPSecretKey k = (PGPSecretKey)keys.get(i); + + k.encode(outStream); + } + for (int i = 0; i != extraPubKeys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)extraPubKeys.get(i); + + k.encode(outStream); + } + } + + /** + * Replace the public key set on the secret ring with the corresponding key off the public ring. + * + * @param secretRing secret ring to be changed. + * @param publicRing public ring containing the new public key set. + */ + public static PGPSecretKeyRing replacePublicKeys(PGPSecretKeyRing secretRing, PGPPublicKeyRing publicRing) + { + List newList = new ArrayList(secretRing.keys.size()); + + for (Iterator it = secretRing.keys.iterator(); it.hasNext();) + { + PGPSecretKey sk = (PGPSecretKey)it.next(); + PGPPublicKey pk = publicRing.getPublicKey(sk.getKeyID()); + + newList.add(PGPSecretKey.replacePublicKey(sk, pk)); + } + + return new PGPSecretKeyRing(newList); + } + + /** + * Return a copy of the passed in secret key ring, with the private keys (where present) associated with the master key and sub keys + * are encrypted using a new password and the passed in algorithm. + * + * @param ring the PGPSecretKeyRing to be copied. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. + * @return the updated key ring. + */ + public static PGPSecretKeyRing copyWithNewPassword( + PGPSecretKeyRing ring, + PBESecretKeyDecryptor oldKeyDecryptor, + PBESecretKeyEncryptor newKeyEncryptor) + throws PGPException + { + List newKeys = new ArrayList(ring.keys.size()); + + for (Iterator keys = ring.getSecretKeys(); keys.hasNext();) + { + PGPSecretKey key = (PGPSecretKey)keys.next(); + + if (key.isPrivateKeyEmpty()) + { + newKeys.add(key); + } + else + { + newKeys.add(PGPSecretKey.copyWithNewPassword(key, oldKeyDecryptor, newKeyEncryptor)); + } + } + + return new PGPSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /** + * Returns a new key ring with the secret key passed in either added or + * replacing an existing one with the same key ID. + * + * @param secRing the secret key ring to be modified. + * @param secKey the secret key to be added. + * @return a new secret key ring. + */ + public static PGPSecretKeyRing insertSecretKey( + PGPSecretKeyRing secRing, + PGPSecretKey secKey) + { + List keys = new ArrayList(secRing.keys); + boolean found = false; + boolean masterFound = false; + + for (int i = 0; i != keys.size();i++) + { + PGPSecretKey key = (PGPSecretKey)keys.get(i); + + if (key.getKeyID() == secKey.getKeyID()) + { + found = true; + keys.set(i, secKey); + } + if (key.isMasterKey()) + { + masterFound = true; + } + } + + if (!found) + { + if (secKey.isMasterKey()) + { + if (masterFound) + { + throw new IllegalArgumentException("cannot add a master key to a ring that already has one"); + } + + keys.add(0, secKey); + } + else + { + keys.add(secKey); + } + } + + return new PGPSecretKeyRing(keys, secRing.extraPubKeys); + } + + /** + * Returns a new key ring with the secret key passed in removed from the + * key ring. + * + * @param secRing the secret key ring to be modified. + * @param secKey the secret key to be removed. + * @return a new secret key ring, or null if secKey is not found. + */ + public static PGPSecretKeyRing removeSecretKey( + PGPSecretKeyRing secRing, + PGPSecretKey secKey) + { + List keys = new ArrayList(secRing.keys); + boolean found = false; + + for (int i = 0; i < keys.size();i++) + { + PGPSecretKey key = (PGPSecretKey)keys.get(i); + + if (key.getKeyID() == secKey.getKeyID()) + { + found = true; + keys.remove(i); + } + } + + if (!found) + { + return null; + } + + return new PGPSecretKeyRing(keys, secRing.extraPubKeys); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignature.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignature.java new file mode 100644 index 000000000..8fae6c3b0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignature.java @@ -0,0 +1,534 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.DERInteger; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.Strings; + +/** + *A PGP signature object. + */ +public class PGPSignature +{ + public static final int BINARY_DOCUMENT = 0x00; + public static final int CANONICAL_TEXT_DOCUMENT = 0x01; + public static final int STAND_ALONE = 0x02; + + public static final int DEFAULT_CERTIFICATION = 0x10; + public static final int NO_CERTIFICATION = 0x11; + public static final int CASUAL_CERTIFICATION = 0x12; + public static final int POSITIVE_CERTIFICATION = 0x13; + + public static final int SUBKEY_BINDING = 0x18; + public static final int PRIMARYKEY_BINDING = 0x19; + public static final int DIRECT_KEY = 0x1f; + public static final int KEY_REVOCATION = 0x20; + public static final int SUBKEY_REVOCATION = 0x28; + public static final int CERTIFICATION_REVOCATION = 0x30; + public static final int TIMESTAMP = 0x40; + + private SignaturePacket sigPck; + private int signatureType; + private TrustPacket trustPck; + private PGPContentVerifier verifier; + private byte lastb; + private OutputStream sigOut; + + PGPSignature( + BCPGInputStream pIn) + throws IOException, PGPException + { + this((SignaturePacket)pIn.readPacket()); + } + + PGPSignature( + SignaturePacket sigPacket) + throws PGPException + { + sigPck = sigPacket; + signatureType = sigPck.getSignatureType(); + trustPck = null; + } + + PGPSignature( + SignaturePacket sigPacket, + TrustPacket trustPacket) + throws PGPException + { + this(sigPacket); + + this.trustPck = trustPacket; + } + + /** + * Return the OpenPGP version number for this signature. + * + * @return signature version number. + */ + public int getVersion() + { + return sigPck.getVersion(); + } + + /** + * Return the key algorithm associated with this signature. + * @return signature key algorithm. + */ + public int getKeyAlgorithm() + { + return sigPck.getKeyAlgorithm(); + } + + /** + * Return the hash algorithm associated with this signature. + * @return signature hash algorithm. + */ + public int getHashAlgorithm() + { + return sigPck.getHashAlgorithm(); + } + + public void init(PGPContentVerifierBuilderProvider verifierBuilderProvider, PGPPublicKey pubKey) + throws PGPException + { + PGPContentVerifierBuilder verifierBuilder = verifierBuilderProvider.get(sigPck.getKeyAlgorithm(), sigPck.getHashAlgorithm()); + + verifier = verifierBuilder.build(pubKey); + + lastb = 0; + sigOut = verifier.getOutputStream(); + } + + public void update( + byte b) + throws PGPSignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] bytes) + throws PGPSignatureException + { + this.update(bytes, 0, bytes.length); + } + + public void update( + byte[] bytes, + int off, + int length) + throws PGPSignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, off, length); + } + } + + private void byteUpdate(byte b) + throws PGPSignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws PGPSignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + public boolean verify() + throws PGPException + { + try + { + sigOut.write(this.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + + return verifier.verify(this.getSignature()); + } + + + private void updateWithIdData(int header, byte[] idBytes) + throws PGPException + { + this.update((byte)header); + this.update((byte)(idBytes.length >> 24)); + this.update((byte)(idBytes.length >> 16)); + this.update((byte)(idBytes.length >> 8)); + this.update((byte)(idBytes.length)); + this.update(idBytes); + } + + private void updateWithPublicKey(PGPPublicKey key) + throws PGPException + { + byte[] keyBytes = getEncodedPublicKey(key); + + this.update((byte)0x99); + this.update((byte)(keyBytes.length >> 8)); + this.update((byte)(keyBytes.length)); + this.update(keyBytes); + } + + /** + * Verify the signature as certifying the passed in public key as associated + * with the passed in user attributes. + * + * @param userAttributes user attributes the key was stored under + * @param key the key to be verified. + * @return true if the signature matches, false otherwise. + * @throws PGPException + */ + public boolean verifyCertification( + PGPUserAttributeSubpacketVector userAttributes, + PGPPublicKey key) + throws PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(key); + + // + // hash in the userAttributes + // + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + UserAttributeSubpacket[] packets = userAttributes.toSubpacketArray(); + for (int i = 0; i != packets.length; i++) + { + packets[i].encode(bOut); + } + updateWithIdData(0xd1, bOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("cannot encode subpacket array", e); + } + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + /** + * Verify the signature as certifying the passed in public key as associated + * with the passed in id. + * + * @param id id the key was stored under + * @param key the key to be verified. + * @return true if the signature matches, false otherwise. + * @throws PGPException + */ + public boolean verifyCertification( + String id, + PGPPublicKey key) + throws PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(key); + + // + // hash in the id + // + updateWithIdData(0xb4, Strings.toUTF8ByteArray(id)); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + /** + * Verify a certification for the passed in key against the passed in + * master key. + * + * @param masterKey the key we are verifying against. + * @param pubKey the key we are verifying. + * @return true if the certification is valid, false otherwise. + * @throws PGPException + */ + public boolean verifyCertification( + PGPPublicKey masterKey, + PGPPublicKey pubKey) + throws PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(masterKey); + updateWithPublicKey(pubKey); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + private void addTrailer() + throws PGPSignatureException + { + try + { + sigOut.write(sigPck.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + /** + * Verify a key certification, such as a revocation, for the passed in key. + * + * @param pubKey the key we are checking. + * @return true if the certification is valid, false otherwise. + * @throws PGPException + */ + public boolean verifyCertification( + PGPPublicKey pubKey) + throws PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + if (this.getSignatureType() != KEY_REVOCATION + && this.getSignatureType() != SUBKEY_REVOCATION) + { + throw new PGPException("signature is not a key signature"); + } + + updateWithPublicKey(pubKey); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + public int getSignatureType() + { + return sigPck.getSignatureType(); + } + + /** + * Return the id of the key that created the signature. + * @return keyID of the signatures corresponding key. + */ + public long getKeyID() + { + return sigPck.getKeyID(); + } + + /** + * Return the creation time of the signature. + * + * @return the signature creation time. + */ + public Date getCreationTime() + { + return new Date(sigPck.getCreationTime()); + } + + public byte[] getSignatureTrailer() + { + return sigPck.getSignatureTrailer(); + } + + /** + * Return true if the signature has either hashed or unhashed subpackets. + * + * @return true if either hashed or unhashed subpackets are present, false otherwise. + */ + public boolean hasSubpackets() + { + return sigPck.getHashedSubPackets() != null || sigPck.getUnhashedSubPackets() != null; + } + + public PGPSignatureSubpacketVector getHashedSubPackets() + { + return createSubpacketVector(sigPck.getHashedSubPackets()); + } + + public PGPSignatureSubpacketVector getUnhashedSubPackets() + { + return createSubpacketVector(sigPck.getUnhashedSubPackets()); + } + + private PGPSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks) + { + if (pcks != null) + { + return new PGPSignatureSubpacketVector(pcks); + } + + return null; + } + + public byte[] getSignature() + throws PGPException + { + MPInteger[] sigValues = sigPck.getSignature(); + byte[] signature; + + if (sigValues != null) + { + if (sigValues.length == 1) // an RSA signature + { + signature = BigIntegers.asUnsignedByteArray(sigValues[0].getValue()); + } + else + { + try + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new DERInteger(sigValues[0].getValue())); + v.add(new DERInteger(sigValues[1].getValue())); + + signature = new DERSequence(v).getEncoded(); + } + catch (IOException e) + { + throw new PGPException("exception encoding DSA sig.", e); + } + } + } + else + { + signature = sigPck.getSignatureBytes(); + } + + return signature; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(sigPck); + if (trustPck != null) + { + out.writePacket(trustPck); + } + } + + private byte[] getEncodedPublicKey( + PGPPublicKey pubKey) + throws PGPException + { + byte[] keyBytes; + + try + { + keyBytes = pubKey.publicPk.getEncodedContents(); + } + catch (IOException e) + { + throw new PGPException("exception preparing key.", e); + } + + return keyBytes; + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureException.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureException.java new file mode 100644 index 000000000..a90cab886 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureException.java @@ -0,0 +1,15 @@ +package org.spongycastle.openpgp; + +public class PGPSignatureException + extends PGPException +{ + public PGPSignatureException(String message) + { + super(message); + } + + public PGPSignatureException(String message, Exception cause) + { + super(message, cause); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java new file mode 100644 index 000000000..c0940855d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSignatureGenerator.java @@ -0,0 +1,487 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; + +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.util.Strings; + +/** + * Generator for PGP Signatures. + */ +public class PGPSignatureGenerator +{ + private SignatureSubpacket[] unhashed = new SignatureSubpacket[0]; + private SignatureSubpacket[] hashed = new SignatureSubpacket[0]; + private OutputStream sigOut; + private PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSigner contentSigner; + private int sigType; + private byte lastb; + private int providedKeyAlgorithm = -1; + + /** + * Create a signature generator built on the passed in contentSignerBuilder. + * + * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures. + */ + public PGPSignatureGenerator( + PGPContentSignerBuilder contentSignerBuilder) + { + this.contentSignerBuilder = contentSignerBuilder; + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + * @deprecated use init() method + */ + public void initSign( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + */ + public void init( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @param random + * @throws PGPException + * @deprecated random parameter now ignored. + */ + public void initSign( + int signatureType, + PGPPrivateKey key, + SecureRandom random) + throws PGPException + { + initSign(signatureType, key); + } + + public void update( + byte b) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] b) + throws PGPSignatureException + { + this.update(b, 0, b.length); + } + + public void update( + byte[] b, + int off, + int len) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + this.update(b[i]); + } + } + else + { + blockUpdate(b, off, len); + } + } + + private void byteUpdate(byte b) + throws PGPSignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws PGPSignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new PGPSignatureException(e.getMessage(), e); + } + } + + public void setHashedSubpackets( + PGPSignatureSubpacketVector hashedPcks) + { + if (hashedPcks == null) + { + hashed = new SignatureSubpacket[0]; + return; + } + + hashed = hashedPcks.toSubpacketArray(); + } + + public void setUnhashedSubpackets( + PGPSignatureSubpacketVector unhashedPcks) + { + if (unhashedPcks == null) + { + unhashed = new SignatureSubpacket[0]; + return; + } + + unhashed = unhashedPcks.toSubpacketArray(); + } + + /** + * Return the one pass header associated with the current signature. + * + * @param isNested + * @return PGPOnePassSignature + * @throws PGPException + */ + public PGPOnePassSignature generateOnePassVersion( + boolean isNested) + throws PGPException + { + return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested)); + } + + /** + * Return a signature object containing the current signature state. + * + * @return PGPSignature + * @throws PGPException + */ + public PGPSignature generate() + throws PGPException + { + MPInteger[] sigValues; + int version = 4; + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + SignatureSubpacket[] hPkts, unhPkts; + + if (!packetPresent(hashed, SignatureSubpacketTags.CREATION_TIME)) + { + hPkts = insertSubpacket(hashed, new SignatureCreationTime(false, new Date())); + } + else + { + hPkts = hashed; + } + + if (!packetPresent(hashed, SignatureSubpacketTags.ISSUER_KEY_ID) && !packetPresent(unhashed, SignatureSubpacketTags.ISSUER_KEY_ID)) + { + unhPkts = insertSubpacket(unhashed, new IssuerKeyID(false, contentSigner.getKeyID())); + } + else + { + unhPkts = unhashed; + } + + try + { + sOut.write((byte)version); + sOut.write((byte)sigType); + sOut.write((byte)contentSigner.getKeyAlgorithm()); + sOut.write((byte)contentSigner.getHashAlgorithm()); + + ByteArrayOutputStream hOut = new ByteArrayOutputStream(); + + for (int i = 0; i != hPkts.length; i++) + { + hPkts[i].encode(hOut); + } + + byte[] data = hOut.toByteArray(); + + sOut.write((byte)(data.length >> 8)); + sOut.write((byte)data.length); + sOut.write(data); + } + catch (IOException e) + { + throw new PGPException("exception encoding hashed data.", e); + } + + byte[] hData = sOut.toByteArray(); + + sOut.write((byte)version); + sOut.write((byte)0xff); + sOut.write((byte)(hData.length >> 24)); + sOut.write((byte)(hData.length >> 16)); + sOut.write((byte)(hData.length >> 8)); + sOut.write((byte)(hData.length)); + + byte[] trailer = sOut.toByteArray(); + + blockUpdate(trailer, 0, trailer.length); + + if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN + || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature + { + sigValues = new MPInteger[1]; + sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature())); + } + else + { + sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature()); + } + + byte[] digest = contentSigner.getDigest(); + byte[] fingerPrint = new byte[2]; + + fingerPrint[0] = digest[0]; + fingerPrint[1] = digest[1]; + + return new PGPSignature(new SignaturePacket(sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues)); + } + + /** + * Generate a certification for the passed in id and key. + * + * @param id the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + String id, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the id + // + updateWithIdData(0xb4, Strings.toUTF8ByteArray(id)); + + return this.generate(); + } + + /** + * Generate a certification for the passed in userAttributes + * @param userAttributes the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPUserAttributeSubpacketVector userAttributes, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the attributes + // + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + UserAttributeSubpacket[] packets = userAttributes.toSubpacketArray(); + for (int i = 0; i != packets.length; i++) + { + packets[i].encode(bOut); + } + updateWithIdData(0xd1, bOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("cannot encode subpacket array", e); + } + + return this.generate(); + } + + /** + * Generate a certification for the passed in key against the passed in + * master key. + * + * @param masterKey the key we are certifying against. + * @param pubKey the key we are certifying. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey masterKey, + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(masterKey); + updateWithPublicKey(pubKey); + + return this.generate(); + } + + /** + * Generate a certification, such as a revocation, for the passed in key. + * + * @param pubKey the key we are certifying. + * @return the certification. + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey pubKey) + throws PGPException + { + updateWithPublicKey(pubKey); + + return this.generate(); + } + + private byte[] getEncodedPublicKey( + PGPPublicKey pubKey) + throws PGPException + { + byte[] keyBytes; + + try + { + keyBytes = pubKey.publicPk.getEncodedContents(); + } + catch (IOException e) + { + throw new PGPException("exception preparing key.", e); + } + + return keyBytes; + } + + private boolean packetPresent( + SignatureSubpacket[] packets, + int type) + { + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + return true; + } + } + + return false; + } + + private SignatureSubpacket[] insertSubpacket( + SignatureSubpacket[] packets, + SignatureSubpacket subpacket) + { + SignatureSubpacket[] tmp = new SignatureSubpacket[packets.length + 1]; + + tmp[0] = subpacket; + System.arraycopy(packets, 0, tmp, 1, packets.length); + + return tmp; + } + + private void updateWithIdData(int header, byte[] idBytes) + throws PGPSignatureException + { + this.update((byte)header); + this.update((byte)(idBytes.length >> 24)); + this.update((byte)(idBytes.length >> 16)); + this.update((byte)(idBytes.length >> 8)); + this.update((byte)(idBytes.length)); + this.update(idBytes); + } + + private void updateWithPublicKey(PGPPublicKey key) + throws PGPException + { + byte[] keyBytes = getEncodedPublicKey(key); + + this.update((byte)0x99); + this.update((byte)(keyBytes.length >> 8)); + this.update((byte)(keyBytes.length)); + this.update(keyBytes); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPUtil.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPUtil.java new file mode 100644 index 000000000..03bf905b7 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPUtil.java @@ -0,0 +1,152 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.security.SecureRandom; + +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERInteger; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; + +/** + * Basic utility class + */ +public class PGPUtil + implements HashAlgorithmTags +{ + static MPInteger[] dsaSigToMpi( + byte[] encoding) + throws PGPException + { + ASN1InputStream aIn = new ASN1InputStream(encoding); + + DERInteger i1; + DERInteger i2; + + try + { + ASN1Sequence s = (ASN1Sequence)aIn.readObject(); + + i1 = (DERInteger)s.getObjectAt(0); + i2 = (DERInteger)s.getObjectAt(1); + } + catch (IOException e) + { + throw new PGPException("exception encoding signature", e); + } + + MPInteger[] values = new MPInteger[2]; + + values[0] = new MPInteger(i1.getValue()); + values[1] = new MPInteger(i2.getValue()); + + return values; + } + + static String getDigestName( + int hashAlgorithm) + throws PGPException + { + switch (hashAlgorithm) + { + case HashAlgorithmTags.SHA1: + return "SHA1"; + case HashAlgorithmTags.MD2: + return "MD2"; + case HashAlgorithmTags.MD5: + return "MD5"; + case HashAlgorithmTags.RIPEMD160: + return "RIPEMD160"; + case HashAlgorithmTags.SHA256: + return "SHA256"; + case HashAlgorithmTags.SHA384: + return "SHA384"; + case HashAlgorithmTags.SHA512: + return "SHA512"; + case HashAlgorithmTags.SHA224: + return "SHA224"; + default: + throw new PGPException("unknown hash algorithm tag in getDigestName: " + hashAlgorithm); + } + } + + static String getSignatureName( + int keyAlgorithm, + int hashAlgorithm) + throws PGPException + { + String encAlg; + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTags.DSA: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases. + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + encAlg = "ElGamal"; + break; + default: + throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return getDigestName(hashAlgorithm) + "with" + encAlg; + } + + public static byte[] makeRandomKey( + int algorithm, + SecureRandom random) + throws PGPException + { + int keySize = 0; + + switch (algorithm) + { + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + keySize = 192; + break; + case SymmetricKeyAlgorithmTags.IDEA: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.CAST5: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.BLOWFISH: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.SAFER: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.DES: + keySize = 64; + break; + case SymmetricKeyAlgorithmTags.AES_128: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.AES_192: + keySize = 192; + break; + case SymmetricKeyAlgorithmTags.AES_256: + keySize = 256; + break; + case SymmetricKeyAlgorithmTags.TWOFISH: + keySize = 256; + break; + default: + throw new PGPException("unknown symmetric algorithm: " + algorithm); + } + + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + random.nextBytes(keyBytes); + + return keyBytes; + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPV3SignatureGenerator.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPV3SignatureGenerator.java new file mode 100644 index 000000000..d89603bb1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPV3SignatureGenerator.java @@ -0,0 +1,241 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; + +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; + +/** + * Generator for old style PGP V3 Signatures. + */ +public class PGPV3SignatureGenerator +{ + private byte lastb; + private OutputStream sigOut; + private PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSigner contentSigner; + private int sigType; + private int providedKeyAlgorithm = -1; + + /** + * Create a signature generator built on the passed in contentSignerBuilder. + * + * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures. + */ + public PGPV3SignatureGenerator( + PGPContentSignerBuilder contentSignerBuilder) + { + this.contentSignerBuilder = contentSignerBuilder; + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + */ + public void init( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @param random + * @throws PGPException + * @deprecated random now ignored - set random in PGPContentSignerBuilder + */ + public void initSign( + int signatureType, + PGPPrivateKey key, + SecureRandom random) + throws PGPException + { + init(signatureType, key); + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + * @deprecated use init() + */ + public void initSign( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + init(signatureType, key); + } + + public void update( + byte b) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] b) + throws PGPSignatureException + { + this.update(b, 0, b.length); + } + + public void update( + byte[] b, + int off, + int len) + throws PGPSignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + this.update(b[i]); + } + } + else + { + blockUpdate(b, off, len); + } + } + + private void byteUpdate(byte b) + throws PGPSignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new PGPSignatureException("unable to update signature", e); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws PGPSignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new PGPSignatureException("unable to update signature", e); + } + } + + /** + * Return the one pass header associated with the current signature. + * + * @param isNested + * @return PGPOnePassSignature + * @throws PGPException + */ + public PGPOnePassSignature generateOnePassVersion( + boolean isNested) + throws PGPException + { + return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested)); + } + + /** + * Return a V3 signature object containing the current signature state. + * + * @return PGPSignature + * @throws PGPException + */ + public PGPSignature generate() + throws PGPException + { + long creationTime = new Date().getTime() / 1000; + + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + + sOut.write(sigType); + sOut.write((byte)(creationTime >> 24)); + sOut.write((byte)(creationTime >> 16)); + sOut.write((byte)(creationTime >> 8)); + sOut.write((byte)creationTime); + + byte[] hData = sOut.toByteArray(); + + blockUpdate(hData, 0, hData.length); + + MPInteger[] sigValues; + if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN + || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) + // an RSA signature + { + sigValues = new MPInteger[1]; + sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature())); + } + else + { + sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature()); + } + + byte[] digest = contentSigner.getDigest(); + byte[] fingerPrint = new byte[2]; + + fingerPrint[0] = digest[0]; + fingerPrint[1] = digest[1]; + + return new PGPSignature(new SignaturePacket(3, contentSigner.getType(), contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), creationTime * 1000, fingerPrint, sigValues)); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java new file mode 100644 index 000000000..1842bbfcf --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java @@ -0,0 +1,469 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.generators.ElGamalKeyPairGenerator; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSAElGamalTest + extends SimpleTest +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + public void performTest() + throws Exception + { + try + { + PGPPublicKey pubKey; + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + if (pubKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bOut); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key suitable for encryption + // + long pgpKeyID = 0; + AsymmetricKeyParameter pKey = null; + BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = keyConverter.getPublicKey(pgpKey); + pgpKeyID = pgpKey.getKeyID(); + if (pgpKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // verify the key + // + + } + } + + AsymmetricBlockCipher c = new PKCS1Encoding(new ElGamalEngine()); + + c.init(true, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.processBlock(in, 0, in.length); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + c.init(false, keyConverter.getPrivateKey(pgpPrivKey)); + + out = c.processBlock(out, 0, out.length); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + /* No compressed data support + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + */ + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); +// compressed data not supported +// while ((ch = clear.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// out = bOut.toByteArray(); +// +// if (!areEqual(out, text)) +// { +// fail("wrong plain text in generated packet"); +// } + + // + // use of PGPKeyPair + // + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalKeyPairGenerator kpg = new ElGamalKeyPairGenerator(); + + ElGamalParameters elParams = new ElGamalParameters(p, g); + + kpg.init(new ElGamalKeyGenerationParameters(new SecureRandom(), elParams)); + + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new BcPGPKeyPair(PGPPublicKey.ELGAMAL_GENERAL , kp, new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + // check sub key encoding + + it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (!pgpKey.isMasterKey()) + { + byte[] kEnc = pgpKey.getEncoded(); + + PGPObjectFactory objF = new PGPObjectFactory(kEnc); + + PGPPublicKey k = (PGPPublicKey)objF.nextObject(); + + pKey = keyConverter.getPublicKey(k); + pgpKeyID = k.getKeyID(); + if (k.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + if (objF.nextObject() != null) + { + fail("failed - stream not fully parsed."); + } + } + } + + } + catch (PGPException e) + { + fail("exception: " + e.getMessage(), e.getUnderlyingException()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main( + String[] args) + { + runTest(new BcPGPDSAElGamalTest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSATest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSATest.java new file mode 100644 index 000000000..f3087ee0b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPDSATest.java @@ -0,0 +1,609 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.RSAKeyPairGenerator; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.RSAKeyGenerationParameters; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mQGiBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbLQzRXJpYyBFY2hp" + + "ZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExEC" + + "ABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j9enEyjRDAlwAn2rrom0s" + + "MhufWK5vIRwg7gj5qsLEAJ4vnT5dPBVblofsG+pDoCVeJXGGng=="); + + byte[] testPrivKey = + Base64.decode( + "lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC" + + "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu" + + "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh" + + "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j" + + "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD" + + "4nXkHg=="); + + byte[] testPrivKey2 = + Base64.decode( + "lQHhBEAnoewRBADRvKgDhbV6pMzqYfUgBsLxSHzmycpuxGbjMrpyKHDOEemj" + + "iQb6TyyBKUoR28/pfshFP9R5urtKIT7wjVrDuOkxYkgRhNm+xmPXW2Lw3D++" + + "MQrC5VWe8ywBltz6T9msmChsaKo2hDhIiRI/mg9Q6rH9pJKtVGi4R7CgGxM2" + + "STQ5fwCgub38qGS1W2O4hUsa+3gva5gaNZUEAItegda4/H4t88XdWxW3D8pv" + + "RnFz26/ADdImVaQlBoumD15VmcgYoT1Djizey7X8vfV+pntudESzLbn3GHlI" + + "6C09seH4e8eYP63t7KU/qbUCDomlSswd1OgQ/RxfN86q765K2t3K1i3wDSxe" + + "EgSRyGKee0VNvOBFOFhuWt+patXaBADE1riNkUxg2P4lBNWwu8tEZRmsl/Ys" + + "DBIzXBshoMzZCvS5PnNXMW4G3SAaC9OC9jvKSx9IEWhKjfjs3QcWzXR28mcm" + + "5na0bTxeOMlaPPhBdkTCmFl0IITWlH/pFlR2ah9WYoWYhZEL2tqB82wByzxH" + + "SkSeD9V5oeSCdCcqiqkEmv4DAwLeNsQ2XGJVRmA4lld+CR5vRxpT/+/2xklp" + + "lxVf/nx0+thrHDpro3u/nINIIObk0gh59+zaEEe3APlHqbQVYWFhIGJiYiA8" + + "Y2NjQGRkZC5lZWU+iFoEExECABoFAkAnoewFCwcDAgEDFQIDAxYCAQIeAQIX" + + "gAAKCRA5nBpCS63az85BAKCbPfU8ATrFvkXhzGNGlc1BJo6DWQCgnK125xVK" + + "lWLpt6ZJJ7TXcx3nkm6wAgAAnQFXBEAnoe0QBACsQxPvaeBcv2TkbgU/5Wc/" + + "tO222dPE1mxFbXjGTKfb+6ge96iyD8kTRLrKCkEEeVBa8AZqMSoXUVN6tV8j" + + "/zD8Bc76o5iJ6wgpg3Mmy2GxInVfsfZN6/G3Y2ukmouz+CDNvQdUw8cTguIb" + + "QoV3XhQ03MLbfVmNcHsku9F4CuKNWwADBQP0DSSe8v5PXF9CSCXOIxBDcQ5x" + + "RKjyYOveqoH/4lbOV0YNUbIDZq4RaUdotpADuPREFmWf0zTB6KV/WIiag8XU" + + "WU9zdDvLKR483Bo6Do5pDBcN+NqfQ+ntGY9WJ7BSFnhQ3+07i1K+NsfFTRfv" + + "hf9X3MP75rCf7MxAIWHTabEmUf4DAwLeNsQ2XGJVRmA8DssBUCghogG9n8T3" + + "qfBeKsplGyCcF+JjPeQXkKQaoYGJ0aJz36qFP9d8DuWtT9soQcqIxVf6mTa8" + + "kN1594hGBBgRAgAGBQJAJ6HtAAoJEDmcGkJLrdrPpMkAnRyjQSKugz0YJqOB" + + "yGasMLQLxd2OAKCEIlhtCarlufVQNGZsuWxHVbU8crACAAA="); + + byte[] sig1 = + Base64.decode( + "owGbwMvMwCR4VvnryyOnTJwZ10gncZSkFpfolVSU2Ltz78hIzcnJVyjPL8pJUeTq" + + "sGdmZQCJwpQLMq3ayTA/0Fj3xf4jbwPfK/H3zj55Z9L1n2k/GOapKJrvMZ4tLiCW" + + "GtP/XeDqX4fORDUA"); + + byte[] sig1crc = Base64.decode("OZa/"); + + byte[] testPubWithUserAttr = + Base64.decode( + "mQGiBD2Rqv0RBADqKCkhVEtB/lEEr/9CubuHEy2oN/yU5j+2GXSdcNdVnRI/rwFy" + + "fHEQIk3uU7zHSUKFrC59yDm0sODYyjEdE3BVb0xvEJ5LE/OdndcIMXT1DungZ1vB" + + "zIK/3lr33W/PHixYxv9jduH3WrTehBpiKkgMZp8XloSFj2Cnw9LDyfqB7QCg/8K1" + + "o2k75NkOd9ZjnA9ye7Ri3bEEAKyr61Mo7viPWBK1joWAEsxG0OBWM+iSlG7kwh31" + + "8efgC/7Os6x4Y0jzs8mpcbBjeZtZjS9lRbfp7RinhF269xL0TZ3JxIdtaAV/6yDQ" + + "9NXfZY9dskN++HIR/5GCEEgq/qTJZt6ti5k7aV19ZFfO6wiK3NUy08wOrVsdOkVE" + + "w9IcBADaplhpcel3201uU3OCboogJtw81R5MJMZ4Y9cKL/ca2jGISn0nA7KrAw9v" + + "ShheSixGO4BV9JECkLEbtg7i+W/j/De6S+x2GLNcphuTP3UmgtKbhs0ItRqzW561" + + "s6gLkqi6aWmgaFLd8E1pMJcd9DSY95P13EYB9VJIUxFNUopzo7QcUmFsZiBIYXVz" + + "ZXIgPGhhdXNlckBhY20ub3JnPokAWAQQEQIAGAUCPZGq/QgLAwkIBwIBCgIZAQUb" + + "AwAAAAAKCRAqIBiOh4JvOKg4AJ9j14yygOqqzqiLKeaasIzqT8LCIgCggx14WuLO" + + "wOUTUswTaVKMFnU7tseJAJwEEAECAAYFAj2Rqx8ACgkQ9aWTKMpUDFV+9QP/RiWT" + + "5FAF5Rgb7beaApsgXsME+Pw7HEYFtqGa6VcXEpbcUXO6rjaXsgMgY90klWlWCF1T" + + "HOyKITvj2FdhE+0j8NQn4vaGpiTwORW/zMf/BZ0abdSWQybp10Yjs8gXw30UheO+" + + "F1E524MC+s2AeUi2hwHMiS+AVYd4WhxWHmWuBpTRypP/AAALTgEQAAEBAAAAAQAA" + + "AAABAAAA/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQ" + + "Dg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/" + + "2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7" + + "Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozv/wAARCABqAF0DASIAAhEBAxEB/8QAHwAAAQUB" + + "AQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQID" + + "AAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0" + + "NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKT" + + "lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl" + + "5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL" + + "/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB" + + "CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpj" + + "ZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3" + + "uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIR" + + "AxEAPwD2aiiq9xcxWsRllcKqjOT06E/0oAsVm6jrmm6VGXvLuOPGflz8x+grzXxV" + + "8U51u5LXRgBGowZHXknnkc9OQcV51caneXdw9xPOXlckl2AJHY4J6cD1oA9J1z4p" + + "TRkrYQhRyQ0hIY5/2QRx7k9ulczN8SvEEshdZkX0UorDrznI759a5Mksckkknqec" + + "mkoA7WD4oavEoEttbTepYEZ+mCMVv6H8SLTULhbe/gFozAYkD5Unp3Ax/kV5XRQB" + + "9EAhgCDkHkEcgilryTwd4zn0m4WzvpTJZSMBuY5MfbueletKyugZWDKwyCOc/j3o" + + "AduyWLDeWB5Ynj8jSUUUAdFXn/xU15dO0RbGGYC5uWwUB6L1Jx+n413F1cJa2stz" + + "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" + + "7DGrzw=="); + + byte[] aesSecretKey = Base64.decode( + "lQHpBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyv4JAwKyWlhdblPudWBFXNkW5ydKn0AV2f51wEtj" + + "Zy0aLIeutVMSJf1ytLqjFqrnFe6pdJrHO3G00TE8OuFhftWosLGLbEGytDtF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gQUVTMjU2KSA8ZXJpY0Bib3Vu" + + "Y3ljYXN0bGUub3JnPohZBBMRAgAZBQJAUnSGBAsHAwIDFQIDAxYCAQIeAQIX" + + "gAAKCRBYt1NnUiCgeFKaAKCiqtOO+NQES1gJW6XuOGmSkXt8bQCfcuW7SXZH" + + "zxK1FfdcG2HEDs3YEVawAgAA"); + + byte[] aesPublicKey = Base64.decode( + "mQGiBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyrQ7RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IEFFUzI1NikgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ0" + + "hgQLBwMCAxUCAwMWAgECHgECF4AACgkQWLdTZ1IgoHhSmgCfU83BLBF2nCua" + + "zk2dXB9zO1l6XS8AnA07U4cq5W0GrKM6/kP9HWtPhgOFsAIAAA=="); + + byte[] twofishSecretKey = Base64.decode( + "lQHpBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV/4KAwLcGrxT3X0qR2CZyZYSVBdjXeNYKXuGBtOf" + + "ood26WOtwLw4+l9sHVoiXNv0LomkO58ndJRPGCeZWZEDMVrfkS7rcOlktDxF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gdHdvZmlzaCkgPGVyaWNAYm91" + + "bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ20gQLBwMCAxUCAwMWAgECHgEC" + + "F4AACgkQaCCMaHh9zR2+RQCghcQwlt4B4YmNxp2b3v6rP3E8M0kAn2Gspi4u" + + "A/ynoqnC1O8HNlbjPdlVsAIAAA=="); + + byte[] twofishPublicKey = Base64.decode( + "mQGiBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV7Q8RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IHR3b2Zpc2gpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkFAkBS" + + "dtIECwcDAgMVAgMDFgIBAh4BAheAAAoJEGggjGh4fc0dvkUAn2QGdNk8Wrrd" + + "+DvKECrO5+yoPRx3AJ91DhCMme6uMrQorKSDYxHlgc7iT7ACAAA="); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Generated signature test + * + * @param sKey + * @param pgpPrivKey + */ + public void generateTest( + PGPSecretKeyRing sKey, + PGPPublicKey pgpPubKey, + PGPPrivateKey pgpPrivKey) + throws Exception + { + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + Iterator it = sKey.getSecretKey().getPublicKey().getUserIDs(); + String primaryUserID = (String)it.next(); + + spGen.setSignerUserID(true, primaryUserID); + + sGen.setHashedSubpackets(spGen.generate()); + + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bOut); + + PGPObjectFactory pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + } + + public void performTest() + throws Exception + { + String file = null; + PGPPublicKey pubKey = null; + + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // test signature message + // + PGPOnePassSignatureList p1; + + PGPOnePassSignature ops; + + PGPLiteralData p2; + + InputStream dIn; + int ch; + + + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1); +// compressed data not supported +// PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); +// +// PGPOnePassSignature ops = p1.get(0); +// +// PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); +// +// InputStream dIn = p2.getInputStream(); +// int ch; +// +// ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); +// +// while ((ch = dIn.read()) >= 0) +// { +// ops.update((byte)ch); +// } +// +// PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); +// +// if (!ops.verify(p3.get(0))) +// { +// fail("Failed signature check"); +// } + + // + // signature generation + // + generateTest(sKey, pubKey, pgpPrivKey); + + // + // signature generation - canonical text + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.TEXT, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bOut); + + // + // verify generated signature - canconical text + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // Read the public key with user attributes + // + pgpPub = new PGPPublicKeyRing(testPubWithUserAttr, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + sigs.next(); + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("Failed user attributes check"); + } + + byte[] pgpPubBytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(pgpPubBytes, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + it = pubKey.getUserAttributes(); + count = 0; + while (it.hasNext()) + { + it.next(); + count++; + } + + if (count != 1) + { + fail("Failed user attributes reread"); + } + + // + // reading test extra data - key with edge condition for DSA key password. + // + char [] passPhrase = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + + sKey = new PGPSecretKeyRing(testPrivKey2, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + AsymmetricKeyParameter bytes = new BcPGPKeyConverter().getPrivateKey(pgpPrivKey); + + // + // reading test - aes256 encrypted passphrase. + // + sKey = new PGPSecretKeyRing(aesSecretKey, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + bytes = new BcPGPKeyConverter().getPrivateKey(pgpPrivKey); + + // + // reading test - twofish encrypted passphrase. + // + sKey = new PGPSecretKeyRing(twofishSecretKey, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + bytes = new BcPGPKeyConverter().getPrivateKey(pgpPrivKey); + + // + // use of PGPKeyPair + // + RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); + + kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 512, 25)); + + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL , kp, new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + } + + public String getName() + { + return "BcPGPDSATest"; + } + + public static void main( + String[] args) + { + runTest(new BcPGPDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java new file mode 100644 index 000000000..b587b9d60 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java @@ -0,0 +1,2379 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.DSAKeyPairGenerator; +import org.spongycastle.crypto.generators.DSAParametersGenerator; +import org.spongycastle.crypto.generators.ElGamalKeyPairGenerator; +import org.spongycastle.crypto.generators.RSAKeyPairGenerator; +import org.spongycastle.crypto.params.DSAKeyGenerationParameters; +import org.spongycastle.crypto.params.ElGamalKeyGenerationParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.RSAKeyGenerationParameters; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; + +public class BcPGPKeyRingTest + extends SimpleTest +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + char[] sec5pass1 = "12345678".toCharArray(); + + // + // Werner Koch "odd keys" + // + byte[] pub6 = Base64.decode( + "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" + + "L4Kb7TWkd/OHQScVBO8sTUz0+2g="); + + byte[] pub6check = Base64.decode("62O9"); + + // + // revoked sub key + // + byte[] pub7 = Base64.decode( + "mQGiBEFOsIwRBADcjRx7nAs4RaWsQU6p8/ECLZD9sSeYc6CN6UDI96RKj0/hCzMs" + + "qlA0+9fzGZ7ZEJ34nuvDKlhKGC7co5eOiE0a9EijxgcrZU/LClZWa4YfyNg/ri6I" + + "yTyfOfrPQ33GNQt2iImDf3FKp7XKuY9nIxicGQEaW0kkuAmbV3oh0+9q8QCg/+fS" + + "epDEqEE/+nKONULGizKUjMED/RtL6RThRftZ9DOSdBytGYd48z35pca/qZ6HA36K" + + "PVQwi7V77VKQyKFLTOXPLnVyO85hyYB/Nv4DFHN+vcC7/49lfoyYMZlN+LarckHi" + + "NL154wmmzygB/KKysvWBLgkErEBCD0xBDd89iTQNlDtVQAWGORVffl6WWjOAkliG" + + "3dL6A/9A288HfFRnywqi3xddriV6wCPmStC3dkCS4vHk2ofS8uw4ZNoRlp1iEPna" + + "ai2Xa9DX1tkhaGk2k96MqqbBdGpbW8sMA9otJ9xdMjWEm/CgJUFUFQf3zaVy3mkM" + + "S2Lvb6P4Wc2l/diEEIyK8+PqJItSh0OVU3K9oM7ngHwVcalKILQVUkV2b2tlZCA8" + + "UmV2b2tlZEB0ZWQ+iQBOBBARAgAOBQJBTrCMBAsDAgECGQEACgkQvglkcFA/c63+" + + "QgCguh8rsJbPTtbhZcrqBi5Mo1bntLEAoPZQ0Kjmu2knRUpHBeUemHDB6zQeuQIN" + + "BEFOsIwQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz" + + "0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRP" + + "xfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvN" + + "ILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dD" + + "ox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMI" + + "PWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/93zriSvSHqsi1FeEmUBo431Jkh" + + "VerIzb6Plb1j6FIq+s3vyvx9K+dMvjotZqylWZj4GXpH+2xLJTjWkrGSfUZVI2Nk" + + "nyOFxUCKLLqaqVBFAQIjULfvQfGEWiGQKk9aRLkdG+D+8Y2N9zYoBXoQ9arvvS/t" + + "4mlOsiuaTe+BZ4x+BXTpF4b9sKZl7V8QP/TkoJWUdydkvxciHdWp7ssqyiKOFRhG" + + "818knDfFQ3cn2w/RnOb+7AF9wDncXDPYLfpPv9b2qZoLrXcyvlLffGDUdWs553ut" + + "1F5AprMURs8BGmY9BnjggfVubHdhTUoA4gVvrdaf+D9NwZAl0xK/5Y/oPuMZiQBG" + + "BBgRAgAGBQJBTrCMAAoJEL4JZHBQP3Ot09gAoMmLKloVDP+WhDXnsM5VikxysZ4+" + + "AKCrJAUO+lYAyPYwEwgK+bKmUGeKrIkARgQoEQIABgUCQU6wpQAKCRC+CWRwUD9z" + + "rQK4AJ98kKFxGU6yhHPr6jYBJPWemTNOXgCfeGB3ox4PXeS4DJDuLy9yllytOjo="); + + byte[] pub7check = Base64.decode("f/YQ"); + + byte[] pub8 = Base64.decode( + "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" + + "9Uqz3fUvGoewAWA="); + + byte[] sec8 = Base64.decode( + "lQHpBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ/4JAwLXyWhb4pf4" + + "nmCmD0lDwoYvatLiR7UQVM2MamxClIiT0lCPN9C2AYIFgRWAJNS215Tjx7P/dh7e" + + "8sYfh5XEHErT3dMbsAGHtCFKaWEgWWl5dSA8eXlqaWFAbm93bWVkaWF0ZWNoLmNv" + + "bT6wAwP//4kAXQQQEQIAHQUCQRytpgcLCQgHAwIKAhkBBRsDAAAABR4BAAAAAAoJ" + + "EPT+Vvgr/2IUmWEAoJEyJ9B7CJfmjwVTT30UbdCctVRmAJ9akw6we6UYd82vF0FK" + + "QTlSuqHXKbABZ50CawRBHK2mEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlL" + + "OCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N" + + "286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/" + + "RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2O" + + "u1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqV" + + "DNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/crbApFLO" + + "QikN9lHbn62dDxuu/dNigNmjf/A3cX+vo8aPHTXnRDyDwQALRuqbitdbM6p1LFjG" + + "8g84Aabk/jL7zzjJLqPe8pxeK1Pu+P44NzalXAYMsOOsxGuixIrz3Jw8IrpH/mkP" + + "tG/iX0NaLZ+Au9eokdLb+6NR/a51r2qL3E0ycFciJ61HzZKKHi6d0VQ7CHozCz+j" + + "d5530Mh1qq/ZjYDDjfP+snpyNIZXcLtGR/3HzkBqNgDcvClLx3322AWKDa7pX8DX" + + "qiLr8znWhPRpH9kCTnSpRzhYGT25FRcvdj4/q/oUIET/Tp+BWW3BxAc7WgpgqEfn" + + "fa0Mv72Zbn91gf4JAwITijME9IlFBGAwH6YmBtWIlnDiRbsq/Pxozuhbnes831il" + + "KmdpUKXkiIfHY0MqrEWl3Dfn6PMJGTnhgqXMrDxx3uHrq0Jl2swRnAWIIO8gID7j" + + "uPetUqEviPiwAYeJAEwEGBECAAwFAkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg" + + "+JW8m5nF3R/oZGuG87bXQBszkjMAoLhGPncuGKowJXMRVc70/8qwXQJLsAFn"); + + char[] sec8pass = "qwertyui".toCharArray(); + + byte[] sec9 = Base64.decode( + "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" + + "6neEEX/i1Q=="); + + public char[] sec9pass = "foo".toCharArray(); + + // version 4 keys with expiry dates + byte[] pub10 = Base64.decode( + "mQGiBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHLQgdGVzdCBrZXkg" + + "KHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUCQqqJrQIbAwUJACTqAAYL" + + "CQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzLAJ42AeCRIBBjv8r8qw9y" + + "laNj2GZ1sACgiWYHVXMA6B1H9I1kS3YsCd3Oq7qwAgAAuM0EQqqJrhADAKWkix8l" + + "pJN7MMTXob4xFF1TvGll0UD1bDGOMMbes6aeXSbT9QXee/fH3GnijLY7wB+qTPv9" + + "ohubrSpnv3yen3CEBW6Q2YK+NlCskma42Py8YMV2idmYjtJi1ckvHFWt5wADBQL/" + + "fkB5Q5xSGgspMaTZmtmX3zG7ZDeZ0avP8e8mRL8UszCTpqs6vMZrXwyQLZPbtMYv" + + "PQpuRGEeKj0ysimwYRA5rrLQjnRER3nyuuEUUgc4j+aeRxPf9WVsJ/a1FCHtaAP1" + + "iE8EGBECAA8FAkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCfd66H7DL7kFGd" + + "IoS+NIp8JO+noxAAn25si4QAF7og8+4T5YQUuhIhx/NesAIAAA=="); + + byte[] sec10 = Base64.decode( + "lQHhBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHP4DAwLssmOjVC+d" + + "mWB783Lpzjb9evKzsxisTdx8/jHpUSS+r//6/Guyx3aA/zUw5bbftItW57mhuNNb" + + "JTu7WrQgdGVzdCBrZXkgKHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUC" + + "QqqJrQIbAwUJACTqAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzL" + + "AJ0cYPwKeoSReY14LqJtAjnkX7URHACgsRZWfpbalrSyDnq3TtZeGPUqGX+wAgAA" + + "nQEUBEKqia4QAwClpIsfJaSTezDE16G+MRRdU7xpZdFA9WwxjjDG3rOmnl0m0/UF" + + "3nv3x9xp4oy2O8Afqkz7/aIbm60qZ798np9whAVukNmCvjZQrJJmuNj8vGDFdonZ" + + "mI7SYtXJLxxVrecAAwUC/35AeUOcUhoLKTGk2ZrZl98xu2Q3mdGrz/HvJkS/FLMw" + + "k6arOrzGa18MkC2T27TGLz0KbkRhHio9MrIpsGEQOa6y0I50REd58rrhFFIHOI/m" + + "nkcT3/VlbCf2tRQh7WgD9f4DAwLssmOjVC+dmWDXVLRopzxbBGOvodp/LZoSDb56" + + "gNJjDMJ1aXqWW9qTAg1CFjBq73J3oFpVzInXZ8+Q8inxv7bnWiHbiE8EGBECAA8F" + + "AkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCgl2jw5hfk/JsyjulQqe1Nps1q" + + "Lx0AoMdnFMZmTMLHn8scUW2j9XO312tmsAIAAA=="); + + public char[] sec10pass = "test".toCharArray(); + + public byte[] subKeyBindingKey = Base64.decode( + "mQGiBDWagYwRBAD7UcH4TAIp7tmUoHBNxVxCVz2ZrNo79M6fV63riOiH2uDxfIpr" + + "IrL0cM4ehEKoqlhngjDhX60eJrOw1nC5BpYZRnDnyDYT4wTWRguxObzGq9pqA1dM" + + "oPTJhkFZVIBgFY99/ULRqaUYIhFGgBtnwS70J8/L/PGVc3DmWRLMkTDjSQCg/5Nh" + + "MCjMK++MdYMcMl/ziaKRT6EEAOtw6PnU9afdohbpx9CK4UvCCEagfbnUtkSCQKSk" + + "6cUp6VsqyzY0pai/BwJ3h4apFMMMpVrtBAtchVgqo4xTr0Sve2j0k+ase6FSImiB" + + "g+AR7hvTUTcBjwtIExBc8TuCTqmn4GG8F7UMdl5Z0AZYj/FfAQYaRVZYP/pRVFNx" + + "Lw65BAC/Fi3qgiGCJFvXnHIckTfcAmZnKSEXWY9NJ4YQb4+/nH7Vsw0wR/ZObUHR" + + "bWgTc9Vw1uZIMe0XVj6Yk1dhGRehUnrm3mE7UJxu7pgkBCbFECFSlSSqP4MEJwZV" + + "09YP/msu50kjoxyoTpt+16uX/8B4at24GF1aTHBxwDLd8X0QWrQsTWVycmlsbCBM" + + "eW5jaCBDTEVBUiBzeXN0ZW0gREggPGNsZWFyQG1sLmNvbT6JAEsEEBECAAsFAjWa" + + "gYwECwMBAgAKCRDyAGjiP47/XanfAKCs6BPURWVQlGh635VgL+pdkUVNUwCdFcNa" + + "1isw+eAcopXPMj6ACOapepu5Ag0ENZqBlBAIAPZCV7cIfwgXcqK61qlC8wXo+VMR" + + "OU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf" + + "3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2g" + + "pXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPA" + + "Q/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQD" + + "GcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH" + + "/RYtVo+HROZ6jrNjrATEwQm1fUQrk6n5+2dniN881lF0CNkB4NkHw1Xxz4Ejnu/0" + + "iLg8fkOAsmanOsKpOkRtqUnVpsVL5mLJpFEyCY5jbcfj+KY9/25bs0ga7kLHNZia" + + "zbCxJdF+W179z3nudQxRaXG/0XISIH7ziZbSVni69sKc1osk1+OoOMbSuZ86z535" + + "Pln4fXclkFE927HxfbWoO+60hkOLKh7x+8fC82b3x9vCETujEaxrscO2xS7/MYXP" + + "8t1ffriTDmhuIuQS2q4fLgeWdqrODrMhrD8Dq7e558gzp30ZCqpiS7EmKGczL7B8" + + "gXxbBCVSTxYMJheXt2xMXsuJAD8DBRg1moGU8gBo4j+O/10RAgWdAKCPhaFIXuC8" + + "/cdiNMxTDw9ug3De5QCfYXmDzRSFUu/nrCi8yz/l09wsnxo="); + + public byte[] subKeyBindingCheckSum = Base64.decode("3HU+"); + + // + // PGP8 with SHA1 checksum. + // + public byte[] rewrapKey = Base64.decode( + "lQOWBEUPOQgBCADdjPTtl8oOwqJFA5WU8p7oDK5KRWfmXeXUZr+ZJipemY5RSvAM" + + "rxqsM47LKYbmXOJznXCQ8+PPa+VxXAsI1CXFHIFqrXSwvB/DUmb4Ec9EuvNd18Zl" + + "hJAybzmV2KMkaUp9oG/DUvxZJqkpUddNfwqZu0KKKZWF5gwW5Oy05VCpaJxQVXFS" + + "whdbRfwEENJiNx4RB3OlWhIjY2p+TgZfgQjiGB9i15R+37sV7TqzBUZF4WWcnIRQ" + + "DnpUfxHgxQ0wO/h/aooyRHSpIx5i4oNpMYq9FNIyakEx/Bomdbs5hW9dFxhrE8Es" + + "UViAYITgTsyROxmgGatGG09dcmVDJVYF4i7JAAYpAAf/VnVyUDs8HrxYTOIt4rYY" + + "jIHToBsV0IiLpA8fEA7k078L1MwSwERVVe6oHVTjeR4A9OxE52Vroh2eOLnF3ftf" + + "6QThVVZr+gr5qeG3yvQ36N7PXNEVOlkyBzGmFQNe4oCA+NR2iqnAIspnekVmwJV6" + + "xVvPCjWw/A7ZArDARpfthspwNcJAp4SWfoa2eKzvUTznTyqFu2PSS5fwQZUgOB0P" + + "Y2FNaKeqV8vEZu4SUWwLOqXBQIZXiaLvdKNgwFvUe3kSHdCNsrVzW7SYxFwaEog2" + + "o6YLKPVPqjlGX1cMOponGp+7n9nDYkQjtEsGSSMQkQRDAcBdSVJmLO07kFOQSOhL" + + "WQQA49BcgTZyhyH6TnDBMBHsGCYj43FnBigypGT9FrQHoWybfX47yZaZFROAaaMa" + + "U6man50YcYZPwzDzXHrK2MoGALY+DzB3mGeXVB45D/KYtlMHPLgntV9T5b14Scbc" + + "w1ES2OUtsSIUs0zelkoXqjLuKnSIYK3mMb67Au7AEp6LXM8EAPj2NypvC86VEnn+" + + "FH0QHvUwBpmDw0EZe25xQs0brvAG00uIbiZnTH66qsIfRhXV/gbKK9J5DTGIqQ15" + + "DuPpz7lcxg/n2+SmjQLNfXCnG8hmtBjhTe+udXAUrmIcfafXyu68SAtebgm1ga56" + + "zUfqsgN3FFuMUffLl3myjyGsg5DnA/oCFWL4WCNClOgL6A5VkNIUait8QtSdCACT" + + "Y7jdSOguSNXfln0QT5lTv+q1AjU7zjRl/LsFNmIJ5g2qdDyK937FOXM44FEEjZty" + + "/4P2dzYpThUI4QUohIj8Qi9f2pZQueC5ztH6rpqANv9geZKcciAeAbZ8Md0K2TEU" + + "RD3Lh+RSBzILtBtUZXN0IEtleSA8dGVzdEBleGFtcGxlLmNvbT6JATYEEwECACAF" + + "AkUPOQgCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDYpknHeQaskD9NB/9W" + + "EbFuLaqZAl3yjLU5+vb75BdvcfL1lUs44LZVwobNp3/0XbZdY76xVPNZURtU4u3L" + + "sJfGlaF+EqZDE0Mqc+vs5SIb0OnCzNJ00KaUFraUtkByRV32T5ECHK0gMBjCs5RT" + + "I0vVv+Qmzl4+X1Y2bJ2mlpBejHIrOzrBD5NTJimTAzyfnNfipmbqL8p/cxXKKzS+" + + "OM++ZFNACj6lRM1W9GioXnivBRC88gFSQ4/GXc8yjcrMlKA27JxV+SZ9kRWwKH2f" + + "6o6mojUQxnHr+ZFKUpo6ocvTgBDlC57d8IpwJeZ2TvqD6EdA8rZ0YriVjxGMDrX1" + + "8esfw+iLchfEwXtBIRwS"); + + char[] rewrapPass = "voltage123".toCharArray(); + + byte[] pubWithX509 = Base64.decode( + "mQENBERabjABCACtmfyo6Nph9MQjv4nmCWjZrRYnhXbivomAdIwYkLZUj1bjqE+j"+ + "uaLzjZV8xSI59odZvrmOiqlzOc4txitQ1OX7nRgbOJ7qku0dvwjtIn46+HQ+cAFn"+ + "2mTi81RyXEpO2uiZXfsNTxUtMi+ZuFLufiMc2kdk27GZYWEuasdAPOaPJnA+wW6i"+ + "ZHlt0NfXIGNz864gRwhD07fmBIr1dMFfATWxCbgMd/rH7Z/j4rvceHD2n9yrhPze"+ + "YN7W4Nuhsr2w/Ft5Cm9xO7vXT/cpto45uxn8f7jERep6bnUwNOhH8G+6xLQgTLD0"+ + "qFBGVSIneK3lobs6+xn6VaGN8W0tH3UOaxA1ABEBAAG0D0NOPXFhLWRlZXBzaWdo"+ + "dIkFDgQQZAIFAQUCRFpuMAUDCWdU0gMF/3gCGwPELGQBAQQwggTkMIIDzKADAgEC"+ + "AhBVUMV/M6rIiE+IzmnPheQWMA0GCSqGSIb3DQEBBQUAMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDAeFw0wNjA1MDQyMTEyMTZaFw0xMTA1MDQyMTIwMDJaMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2Z/Kjo2mH0xCO/ieYJ"+ + "aNmtFieFduK+iYB0jBiQtlSPVuOoT6O5ovONlXzFIjn2h1m+uY6KqXM5zi3GK1DU"+ + "5fudGBs4nuqS7R2/CO0ifjr4dD5wAWfaZOLzVHJcSk7a6Jld+w1PFS0yL5m4Uu5+"+ + "IxzaR2TbsZlhYS5qx0A85o8mcD7BbqJkeW3Q19cgY3PzriBHCEPTt+YEivV0wV8B"+ + "NbEJuAx3+sftn+Piu9x4cPaf3KuE/N5g3tbg26GyvbD8W3kKb3E7u9dP9ym2jjm7"+ + "Gfx/uMRF6npudTA06Efwb7rEtCBMsPSoUEZVIid4reWhuzr7GfpVoY3xbS0fdQ5r"+ + "EDUCAwEAAaOCAXwwggF4MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G"+ + "A1UdDgQWBBSmFTRv5y65DHtTYae48zl0ExNWZzCCASUGA1UdHwSCARwwggEYMIIB"+ + "FKCCARCgggEMhoHFbGRhcDovLy9DTj1xYS1kZWVwc2lnaHQsQ049cWEtd3VtYW4x"+ + "LWRjLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNl"+ + "cyxDTj1Db25maWd1cmF0aW9uLERDPVdlYmZlLERDPXRtczAxLERDPXFhLERDPWNv"+ + "bT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM"+ + "RGlzdHJpYnV0aW9uUG9pbnSGQmh0dHA6Ly9xYS13dW1hbjEtZGMud2ViZmUudG1z"+ + "MDEucWEuY29tL0NlcnRFbnJvbGwvcWEtZGVlcHNpZ2h0LmNybDAQBgkrBgEEAYI3"+ + "FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAfuZCW3XlB7Eok35zQbvYt9rhAndT"+ + "DNw3wPNI4ZzD1nXoYWnwhNNvWRpsOt4ExOSNdaHErfgDXAMyyg66Sro0TkAx8eAj"+ + "fPQsyRAh0nm0glzFmJN6TdOZbj7hqGZjc4opQ6nZo8h/ULnaEwMIUW4gcSkZt0ww"+ + "CuErl5NUrN3DpkREeCG/fVvQZ8ays3ibQ5ZCZnYBkLYq/i0r3NLW34WfYhjDY48J"+ + "oQWtvFSAxvRfz2NGmqnrCHPQZxqlfdta97kDa4VQ0zSeBaC70gZkLmD1GJMxWoXW"+ + "6tmEcgPY5SghInUf+L2u52V55MjyAFzVp7kTK2KY+p7qw35vzckrWkwu8AAAAAAA"+ + "AQE="); + + private static byte[] secWithPersonalCertificate = Base64.decode( + "lQOYBEjGLGsBCACp1I1dZKsK4N/I0/4g02hDVNLdQkDZfefduJgyJUyBGo/I" + + "/ZBpc4vT1YwVIdic4ADjtGB4+7WohN4v8siGzwRSeXardSdZVIw2va0JDsQC" + + "yeoTnwVkUgn+w/MDgpL0BBhTpr9o3QYoo28/qKMni3eA8JevloZqlAbQ/sYq" + + "rToMAqn0EIdeVVh6n2lRQhUJaNkH/kA5qWBpI+eI8ot/Gm9kAy3i4e0Xqr3J" + + "Ff1lkGlZuV5H5p/ItZui9BDIRn4IDaeR511NQnKlxFalM/gP9R9yDVI1aXfy" + + "STcp3ZcsTOTGNzACtpvMvl6LZyL42DyhlOKlJQJS81wp4dg0LNrhMFOtABEB" + + "AAEAB/0QIH5UEg0pTqAG4r/3v1uKmUbKJVJ3KhJB5xeSG3dKWIqy3AaXR5ZN" + + "mrJfXK7EfC5ZcSAqx5br1mzVl3PHVBKQVQxvIlmG4r/LKvPVhQYZUFyJWckZ" + + "9QMR+EA0Dcran9Ds5fa4hH84jgcwalkj64XWRAKDdVh098g17HDw+IYnQanl" + + "7IXbYvh+1Lr2HyPo//vHX8DxXIJBv+E4skvqGoNfCIfwcMeLsrI5EKo+D2pu" + + "kAuBYI0VBiZkrJHFXWmQLW71Mc/Bj7wTG8Q1pCpu7YQ7acFSv+/IOCsB9l9S" + + "vdB7pNhB3lEjYFGoTgr03VfeixA7/x8uDuSXjnBdTZqmGqkZBADNwCqlzdaQ" + + "X6CjS5jc3vzwDSPgM7ovieypEL6NU3QDEUhuP6fVvD2NYOgVnAEbJzgOleZS" + + "W2AFXKAf5NDxfqHnBmo/jlYb5yZV5Y+8/poLLj/m8t7sAfAmcZqGXfYMbSbe" + + "tr6TGTUXcXgbRyU5oH1e4iq691LOwZ39QjL8lNQQywQA006XYEr/PS9uJkyM" + + "Cg+M+nmm40goW4hU/HboFh9Ru6ataHj+CLF42O9sfMAV02UcD3Agj6w4kb5L" + + "VswuwfmY+17IryT81d+dSmDLhpo6ufKoAp4qrdP+bzdlbfIim4Rdrw5vF/Yk" + + "rC/Nfm3CLJxTimHJhqFx4MG7yEC89lxgdmcD/iJ3m41fwS+bPN2rrCAf7j1u" + + "JNr/V/8GAnoXR8VV9150BcOneijftIIYKKyKkV5TGwcTfjaxRKp87LTeC3MV" + + "szFDw04MhlIKRA6nBdU0Ay8Yu+EjXHK2VSpLG/Ny+KGuNiFzhqgBxM8KJwYA" + + "ISa1UEqWjXoLU3qu1aD7cCvANPVCOASwAYe0GlBHUCBEZXNrdG9wIDxpbmZv" + + "QHBncC5jb20+sAMD//+JAW4EEAECAFgFAkjGLGswFIAAAAAAIAAHcHJlZmVy" + + "cmVkLWVtYWlsLWVuY29kaW5nQHBncC5jb21wZ3BtaW1lBwsJCAcDAgoCGQEF" + + "GwMAAAADFgECBR4BAAAABRUCCAkKAAoJEHHHqp2m1tlWsx8H/icpHl1Nw17A" + + "D6MJN6zJm+aGja+5BOFxOsntW+IV6JI+l5WwiIVE8xTDhoXW4zdH3IZTqoyY" + + "frtkqLGpvsPtAQmV6eiPgE3+25ahL+MmjXKsceyhbZeCPDtM2M382VCHYCZK" + + "DZ4vrHVgK/BpyTeP/mqoWra9+F5xErhody71/cLyIdImLqXgoAny6YywjuAD" + + "2TrFnzPEBmZrkISHVEso+V9sge/8HsuDqSI03BAVWnxcg6aipHtxm907sdVo" + + "jzl2yFbxCCCaDIKR7XVbmdX7VZgCYDvNSxX3WEOgFq9CYl4ZlXhyik6Vr4XP" + + "7EgqadtfwfMcf4XrYoImSQs0gPOd4QqwAWedA5gESMYsawEIALiazFREqBfi" + + "WouTjIdLuY09Ks7PCkn0eo/i40/8lEj1R6JKFQ5RlHNnabh+TLvjvb3nOSU0" + + "sDg+IKK/JUc8/Fo7TBdZvARX6BmltEGakqToDC3eaF9EQgHLEhyE/4xXiE4H" + + "EeIQeCHdC7k0pggEuWUn5lt6oeeiPUWhqdlUOvzjG+jqMPJL0bk9STbImHUR" + + "EiugCPTekC0X0Zn0yrwyqlJQMWnh7wbSl/uo4q45K7qOhxcijo+hNNrkRAMi" + + "fdNqD4s5qDERqqHdAAgpWqydo7zV5tx0YSz5fjh59Z7FxkUXpcu1WltT6uVn" + + "hubiMTWpXzXOQI8wZL2fb12JmRY47BEAEQEAAQAH+wZBeanj4zne+fBHrWAS" + + "2vx8LYiRV9EKg8I/PzKBVdGUnUs0vTqtXU1dXGXsAsPtu2r1bFh0TQH06gR1" + + "24iq2obgwkr6x54yj+sZlE6SU0SbF/mQc0NCNAXtSKV2hNXvy+7P+sVJR1bn" + + "b5ukuvkj1tgEln/0W4r20qJ60F+M5QxXg6kGh8GAlo2tetKEv1NunAyWY6iv" + + "FTnSaIJ/YaKQNcudNvOJjeIakkIzfzBL+trUiI5n1LTBB6+u3CF/BdZBTxOy" + + "QwjAh6epZr+GnQqeaomFxBc3mU00sjrsB1Loso84UIs6OKfjMkPoZWkQrQQW" + + "+xvQ78D33YwqNfXk/5zQAxkEANZxJGNKaAeDpN2GST/tFZg0R5GPC7uWYC7T" + + "pG100mir9ugRpdeIFvfAa7IX2jujxo9AJWo/b8hq0q0koUBdNAX3xxUaWy+q" + + "KVCRxBifpYVBfEViD3lsbMy+vLYUrXde9087YD0c0/XUrj+oowWJavblmZtS" + + "V9OjkQW9zoCigpf5BADcYV+6bkmJtstxJopJG4kD/lr1o35vOEgLkNsMLayc" + + "NuzES084qP+8yXPehkzSsDB83kc7rKfQCQMZ54V7KCCz+Rr4wVG7FCrFAw4e" + + "4YghfGVU/5whvbJohl/sXXCYGtVljvY/BSQrojRdP+/iZxFbeD4IKiTjV+XL" + + "WKSS56Fq2QQAzeoKBJFUq8nqc8/OCmc52WHSOLnB4AuHL5tNfdE9tjqfzZAE" + + "tx3QB7YGGP57tPQxPFDFJVRJDqw0YxI2tG9Pum8iriKGjHg+oEfFhxvCmPxf" + + "zDKaGibkLeD7I6ATpXq9If+Nqb5QjzPjFbXBIz/q2nGjamZmp4pujKt/aZxF" + + "+YRCebABh4kCQQQYAQIBKwUCSMYsbAUbDAAAAMBdIAQZAQgABgUCSMYsawAK" + + "CRCrkqZshpdZSNAiB/9+5nAny2O9/lp2K2z5KVXqlNAHUmd4S/dpqtsZCbAo" + + "8Lcr/VYayrNojga1U7cyhsvFky3N9wczzPHq3r9Z+R4WnRM1gpRWl+9+xxtd" + + "ZxGfGzMRlxX1n5rCqltKKk6IKuBAr2DtTnxThaQiISO2hEw+P1MT2HnSzMXt" + + "zse5CZ5OiOd/bm/rdvTRD/JmLqhXmOFaIwzdVP0dR9Ld4Dug2onOlIelIntC" + + "cywY6AmnL0DThaTy5J8MiMSPamSmATl4Bicm8YRbHHz58gCYxI5UMLwtwR1+" + + "rSEmrB6GwVHZt0/BzOpuGpvFZI5ZmC5yO/waR1hV+VYj025cIz+SNuDPyjy4" + + "AAoJEHHHqp2m1tlW/w0H/3w38SkB5n9D9JL3chp+8fex03t7CQowVMdsBYNY" + + "qI4QoVQkakkxzCz5eF7rijXt5eC3NE/quWhlMigT8LARiwBROBWgDRFW4WuX" + + "6MwYtjKKUkZSkBKxP3lmaqZrJpF6jfhPEN76zr/NxWPC/nHRNldUdqkzSu/r" + + "PeJyePMofJevzMkUzw7EVtbtWhZavCz+EZXRTZXub9M4mDMj64BG6JHMbVZI" + + "1iDF2yka5RmhXz9tOhYgq80m7UQUb1ttNn86v1zVbe5lmB8NG4Ndv+JaaSuq" + + "SBZOYQ0ZxtMAB3vVVLZCWxma1P5HdXloegh+hosqeu/bl0Wh90z5Bspt6eI4" + + "imqwAWeVAdgESMYtmwEEAM9ZeMFxor7oSoXnhQAXD9lXLLfBky6IcIWISY4F" + + "JWc8sK8+XiVzpOrefKro0QvmEGSYcDFQMHdScBLOTsiVJiqenA7fg1bkBr/M" + + "bnD7vTKMJe0DARlU27tE5hsWCDYTluxIFjGcAcecY2UqHkqpctYKY0WY9EIm" + + "dBA5TYaw3c0PABEBAAEAA/0Zg6318nC57cWLIp5dZiO/dRhTPZD0hI+BWZrg" + + "zJtPT8rXVY+qK3Jwquig8z29/r+nppEE+xQWVWDlv4M28BDJAbGE+qWKAZqT" + + "67lyKgc0c50W/lfbGvvs+F7ldCcNpFvlk79GODKxcEeTGDQKb9R6FnHFee/K" + + "cZum71O3Ku3vUQIA3B3PNM+tKocIUNDHnInuLyqLORwQBNGfjU/pLMM0MkpP" + + "lWeIfgUmn2zL/e0JrRoO0LQqX1LN/TlfcurDM0SEtwIA8Sba9OpDq99Yz360" + + "FiePJiGNNlbj9EZsuGJyMVXL1mTLA6WHnz5XZOfYqJXHlmKvaKDbARW4+0U7" + + "0/vPdYWSaQIAwYeo2Ce+b7M5ifbGMDWYBisEvGISg5xfvbe6qApmHS4QVQzE" + + "Ym81rdJJ8OfvgSbHcgn37S3OBXIQvNdejF4BWqM9sAGHtCBIeW5lay1JbnRy" + + "YW5ldCA8aHluZWtAYWxzb2Z0LmN6PrADA///iQDrBBABAgBVBQJIxi2bBQkB" + + "mgKAMBSAAAAAACAAB3ByZWZlcnJlZC1lbWFpbC1lbmNvZGluZ0BwZ3AuY29t" + + "cGdwbWltZQULBwgJAgIZAQUbAQAAAAUeAQAAAAIVAgAKCRDlTa3BE84gWVKW" + + "BACcoCFKvph9r9QiHT1Z3N4wZH36Uxqu/059EFALnBkEdVudX/p6S9mynGRk" + + "EfhmWFC1O6dMpnt+ZBEed/4XyFWVSLPwirML+6dxfXogdUsdFF1NCRHc3QGc" + + "txnNUT/zcZ9IRIQjUhp6RkIvJPHcyfTXKSbLviI+PxzHU2Padq8pV7ABZ7kA" + + "jQRIfg8tAQQAutJR/aRnfZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr" + + "5dg50wq3I4HOamRxUwHpdPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO" + + "8LUJ2VTbfPxoLFp539SQ0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0Ft" + + "JycAEQEAAbABj4kEzQQYAQIENwUCSMYtnAUJAeEzgMLFFAAAAAAAFwNleDUw" + + "OWNlcnRpZmljYXRlQHBncC5jb20wggNhMIICyqADAgECAgkA1AoCoRKJCgsw" + + "DQYJKoZIhvcNAQEFBQAwgakxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj" + + "aCBSZXB1YmxpYzESMBAGA1UEChQJQSYmTCBzb2Z0MSAwHgYDVQQLExdJbnRl" + + "cm5hbCBEZXZlbG9wbWVudCBDQTEqMCgGA1UEAxQhQSYmTCBzb2Z0IEludGVy" + + "bmFsIERldmVsb3BtZW50IENBMR8wHQYJKoZIhvcNAQkBFhBrYWRsZWNAYWxz" + + "b2Z0LmN6MB4XDTA4MDcxNjE1MDkzM1oXDTA5MDcxNjE1MDkzM1owaTELMAkG" + + "A1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMRIwEAYDVQQKFAlB" + + "JiZMIHNvZnQxFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5IeW5l" + + "ay1JbnRyYW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAutJR/aRn" + + "fZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr5dg50wq3I4HOamRxUwHp" + + "dPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO8LUJ2VTbfPxoLFp539SQ" + + "0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0FtJycCAwEAAaOBzzCBzDAJ" + + "BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD" + + "ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNaw7A6r10PtYZzAvr9CrSKeRYJgwHwYD" + + "VR0jBBgwFoAUmqSRM8rN3+T1+tkGiqef8S5suYgwGgYDVR0RBBMwEYEPaHlu" + + "ZWtAYWxzb2Z0LmN6MCgGA1UdHwQhMB8wHaAboBmGF2h0dHA6Ly9wZXRyazIv" + + "Y2EvY2EuY3JsMAsGA1UdDwQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQCUdOWd" + + "7mBLWj1/GSiYgfwgdTrgk/VZOJvMKBiiFyy1iFEzldz6Xx+mAexnFJKfZXZb" + + "EMEGWHfWPmgJzAtuTT0Jz6tUwDmeLH3MP4m8uOZtmyUJ2aq41kciV3rGxF0G" + + "BVlZ/bWTaOzHdm6cjylt6xxLt6MJzpPBA/9ZfybSBh1DaAUbDgAAAJ0gBBkB" + + "AgAGBQJIxi2bAAoJEAdYkEWLb2R2fJED/RK+JErZ98uGo3Z81cHkdP3rk8is" + + "DUL/PR3odBPFH2SIA5wrzklteLK/ZXmBUzcvxqHEgI1F7goXbsBgeTuGgZdx" + + "pINErxkNpcMl9FTldWKGiapKrhkZ+G8knDizF/Y7Lg6uGd2nKVxzutLXdHJZ" + + "pU89Q5nzq6aJFAZo5TBIcchQAAoJEOVNrcETziBZXvQD/1mvFqBfWqwXxoj3" + + "8fHUuFrE2pcp32y3ciO2i+uNVEkNDoaVVNw5eHQaXXWpllI/Pe6LnBl4vkyc" + + "n3pjONa4PKrePkEsCUhRbIySqXIHuNwZumDOlKzZHDpCUw72LaC6S6zwuoEf" + + "ucOcxTeGIUViANWXyTIKkHfo7HfigixJIL8nsAFn"); + + private static final byte[] umlautKeySig = Base64.decode( + "mI0ETdvOgQEEALoI2a39TRk1HReEB6DP9Bu3ShZUce+/Oeg9RIL9aUFuCsNdhu02" + + "REEHjO29Jz8daPgrnJDfFepNLD6iKKru2m9P30qnhsHMIAshO2Ozfh6wKwuHRqR3" + + "L4gBDu7cCB6SLwPoD8AYG0yQSM+Do10Td87RlStxCgxpMK6R3TsRkxcFABEBAAG0" + + "OlVNTEFVVFNUQVJUOsOEw6TDlsO2w5zDvMOfOlVNTEFURU5ERSA8YXNkbGFrc2Rs" + + "QGFrc2RqLmNvbT6IuAQTAQIAIgUCTdvOgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC" + + "HgECF4AACgkQP8kDwm8AOFiArAP/ZXrlZJB1jFEjyBb04ckpE6F/aJuSYIXf0Yx5" + + "T2eS+lA69vYuqKRC1qNROBrAn/WGNOQBFNEgGoy3F3gV5NgpIphnyIEZdZWGY2rv" + + "yjunKWlioZjWc/xbSbvpvJ3Q8RyfDXBOkDEB6uF1ksimw2eJSOUTkF9AQfS5f4rT" + + "5gs013G4jQRN286BAQQApVbjd8UhsQLB4TpeKn9+dDXAfikGgxDOb19XisjRiWxA" + + "+bKFxu5tRt6fxXl6BGSGT7DhoVbNkcJGVQFYcbR31UGKCVYcWSL3yfz+PiVuf1UB" + + "Rp44cXxxqxrLqKp1rk3dGvV4Ayy8lkk3ncDGPez6lIKvj3832yVtAzUOX1QOg9EA" + + "EQEAAYifBBgBAgAJBQJN286BAhsMAAoJED/JA8JvADhYQ80D/R3TX0FBMHs/xqEh" + + "tiS86XP/8pW6eMm2eaAYINxoDY3jmDMv2HFQ+YgrYXgqGr6eVGqDMNPj4W8VBoOt" + + "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + public void test1() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + Iterator sIt = pubKey.getSignatures(); + while (sIt.hasNext()) + { + ((PGPSignature)sIt.next()).getSignatureType(); + } + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + // + // exact match + // + rIt = pubRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = pubRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on partial match 1"); + } + + // + // partial match 0 expected + // + rIt = pubRings.getKeyRings("XXX", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of public keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = pubRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on case-insensitive partial match"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + pk.getSignatures(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + + // + // exact match + // + rIt = secretRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = secretRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on partial match 1"); + } + + // + // exact match 0 expected + // + rIt = secretRings.getKeyRings("test", false); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of secret keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = secretRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on case-insensitive partial match"); + } + } + + public void test2() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + + keyCount++; + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + if (pk.getKeyID() == -1413891222336124627L) + { + int sCount = 0; + Iterator sIt = pk.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + while (sIt.hasNext()) + { + int type = ((PGPSignature)sIt.next()).getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING) + { + fail("failed to return correct signature type"); + } + sCount++; + } + + if (sCount != 1) + { + fail("failed to find binding signature"); + } + } + + pk.getSignatures(); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass1)); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass2)); + } + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 2) + { + fail("wrong number of secret keyrings"); + } + } + + public void test3() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubK = (PGPPublicKey)it.next(); + + pubK.getSignatures(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test4() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test5() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + if (noIDEA()) + { + return; + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec5pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + private boolean noIDEA() + { + return true; + } + + public void test6() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub6); + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.getKeyID() == 0x5ce086b5b5a18ff4L) + { + int count = 0; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test6."); + } + } + } + } + + byte[] encRing = pubRings.getEncoded(); + } + + public void test7() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(pub7, new BcKeyFingerprintCalculator()); + Iterator it = pgpPub.getPublicKeys(); + PGPPublicKey masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + + int count = 0; + PGPSignature sig = null; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + + while (sIt.hasNext()) + { + sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test7."); + } + + sig.init(new BcPGPContentVerifierBuilderProvider(), masterKey); + + if (!sig.verifyCertification(k)) + { + fail("failed to verify revocation certification"); + } + } + } + + public void test8() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub8); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec8); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec8pass)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test9() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec9); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + PGPPrivateKey pKey = k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec9pass)); + if (keyCount == 1 && pKey != null) + { + fail("primary secret key found, null expected"); + } + } + + if (keyCount != 3) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test10() + throws Exception + { + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new BcKeyFingerprintCalculator()); + Iterator secretKeys = secretRing.getSecretKeys(); + + while (secretKeys.hasNext()) + { + PGPPublicKey pubKey = ((PGPSecretKey)secretKeys.next()).getPublicKey(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on secret key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on secret key ring"); + } + } + + PGPPublicKeyRing publicRing = new PGPPublicKeyRing(pub10, new BcKeyFingerprintCalculator()); + Iterator publicKeys = publicRing.getPublicKeys(); + + while (publicKeys.hasNext()) + { + PGPPublicKey pubKey = (PGPPublicKey)publicKeys.next(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on public key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on public key ring"); + } + } + } + + public void generateTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + DSAParametersGenerator dsaPGen = new DSAParametersGenerator(); + + dsaPGen.init(512, 10, new SecureRandom()); + + DSAKeyPairGenerator dsaKpg = new DSAKeyPairGenerator(); + + dsaKpg.init(new DSAKeyGenerationParameters(new SecureRandom(), dsaPGen.generateParameters())); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + AsymmetricCipherKeyPair dsaKp = dsaKpg.generateKeyPair(); + + ElGamalKeyPairGenerator elgKpg = new ElGamalKeyPairGenerator(); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameters elParams = new ElGamalParameters(p, g); + + elgKpg.init(new ElGamalKeyGenerationParameters(new SecureRandom(), elParams)); + + // + // this is quicker because we are using pregenerated parameters. + // + AsymmetricCipherKeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new BcPGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new BcPGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", null, null, null, new BcPGPContentSignerBuilder(PGPPublicKey.DSA, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void insertMasterTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + RSAKeyPairGenerator rsaKpg = new RSAKeyPairGenerator(); + + rsaKpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 512, 25)); + + // + // this is quicker because we are using pregenerated parameters. + // + AsymmetricCipherKeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + PGPDigestCalculator chkSumCalc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + "test", chkSumCalc, null, null, new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + PGPSecretKeyRing secRing1 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair2, + "test", chkSumCalc, null, null, new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + PGPSecretKeyRing secRing2 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing2 = keyRingGen.generatePublicKeyRing(); + + try + { + PGPPublicKeyRing.insertPublicKey(pubRing1, pubRing2.getPublicKey()); + fail("adding second master key (public) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in public test"); + } + } + + try + { + PGPSecretKeyRing.insertSecretKey(secRing1, secRing2.getSecretKey()); + fail("adding second master key (secret) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in secret test"); + } + } + } + + public void generateSha1Test() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + DSAParametersGenerator dsaPGen = new DSAParametersGenerator(); + + dsaPGen.init(512, 10, new SecureRandom()); + + DSAKeyPairGenerator dsaKpg = new DSAKeyPairGenerator(); + + dsaKpg.init(new DSAKeyGenerationParameters(new SecureRandom(), dsaPGen.generateParameters())); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + AsymmetricCipherKeyPair dsaKp = dsaKpg.generateKeyPair(); + + ElGamalKeyPairGenerator elgKpg = new ElGamalKeyPairGenerator(); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameters elParams = new ElGamalParameters(p, g); + + elgKpg.init(new ElGamalKeyGenerationParameters(new SecureRandom(), elParams)); + + // + // this is quicker because we are using pregenerated parameters. + // + AsymmetricCipherKeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new BcPGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new BcPGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + PGPDigestCalculator chkSumCalc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", chkSumCalc, null, null, new BcPGPContentSignerBuilder(PGPPublicKey.DSA, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void test11() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(subKeyBindingKey, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + while (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + if (key.getValidSeconds() != 0) + { + fail("expiration time non-zero"); + } + } + } + + private void rewrapTest() + throws Exception + { + SecureRandom rand = new SecureRandom(); + + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + } + } + } + + private void testPublicKeyRingWithX509() + throws Exception + { + checkPublicKeyRingWithX509(pubWithX509); + + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(pubWithX509, new BcKeyFingerprintCalculator()); + + checkPublicKeyRingWithX509(pubRing.getEncoded()); + } + + private void testSecretKeyRingWithPersonalCertificate() + throws Exception + { + checkSecretKeyRingWithPersonalCertificate(secWithPersonalCertificate); + PGPSecretKeyRingCollection secRing = new PGPSecretKeyRingCollection(secWithPersonalCertificate); + checkSecretKeyRingWithPersonalCertificate(secRing.getEncoded()); + } + + private void testUmlaut() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(umlautKeySig, new BcKeyFingerprintCalculator()); + + PGPPublicKey pub = pubRing.getPublicKey(); + String userID = (String)pub.getUserIDs().next(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID test"); + } + } + } + + // + // this is quicker because we are using pregenerated parameters. + // + RSAKeyPairGenerator rsaKpg = new RSAKeyPairGenerator(); + + rsaKpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 512, 25)); + + AsymmetricCipherKeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + char[] passPhrase = "passwd".toCharArray(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + userID, null, null, null, new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + + pub = pubRing1.getPublicKey(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID creation test"); + } + } + } + } + + private void checkSecretKeyRingWithPersonalCertificate(byte[] keyRing) + throws Exception + { + PGPSecretKeyRingCollection secCol = new PGPSecretKeyRingCollection(keyRing); + + + int count = 0; + + for (Iterator rIt = secCol.getKeyRings(); rIt.hasNext();) + { + PGPSecretKeyRing ring = (PGPSecretKeyRing)rIt.next(); + + for (Iterator it = ring.getExtraPublicKeys(); it.hasNext();) + { + it.next(); + count++; + } + } + + if (count != 1) + { + fail("personal certificate data subkey not found - count = " + count); + } + } + + private void checkPublicKeyRingWithX509(byte[] keyRing) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(keyRing, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + if (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + Iterator sIt = key.getSignatures(); + + if (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + if (sig.getKeyAlgorithm() != 100) + { + fail("experimental signature not found"); + } + if (!areEqual(sig.getSignature(), Hex.decode("000101"))) + { + fail("experimental encoding check failed"); + } + } + else + { + fail("no signature found"); + } + } + else + { + fail("no key found"); + } + } + + public void performTest() + throws Exception + { + try + { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + // test7(); + test8(); + test9(); + test10(); + test11(); + generateTest(); + generateSha1Test(); + rewrapTest(); + testPublicKeyRingWithX509(); + testSecretKeyRingWithPersonalCertificate(); + insertMasterTest(); + testUmlaut(); + } + catch (PGPException e) + { + if (e.getUnderlyingException() != null) + { + Exception ex = e.getUnderlyingException(); + fail("exception: " + ex, ex); + } + else + { + fail("exception: " + e, e); + } + } + } + + public String getName() + { + return "PGPKeyRingTest"; + } + + public static void main( + String[] args) + { + runTest(new BcPGPKeyRingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPPBETest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPPBETest.java new file mode 100644 index 000000000..b6ada0f49 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPPBETest.java @@ -0,0 +1,382 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.Date; + +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPPBETest + extends SimpleTest +{ + private static final Date TEST_DATE = new Date(1062200111000L); + + byte[] enc1 = Base64.decode( + "jA0EAwMC5M5wWBP2HBZgySvUwWFAmMRLn7dWiZN6AkQMvpE3b6qwN3SSun7zInw2" + + "hxxdgFzVGfbjuB8w"); + + byte[] enc1crc = Base64.decode("H66L"); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Message with both PBE and symmetric + */ + byte[] testPBEAsym = Base64.decode( + "hQIOA/ZlQEFWB5vuEAf/covEUaBve7NlWWdiO5NZubdtTHGElEXzG9hyBycp9At8" + + "nZGi27xOZtEGFQo7pfz4JySRc3O0s6w7PpjJSonFJyNSxuze2LuqRwFWBYYcbS8/" + + "7YcjB6PqutrT939OWsozfNqivI9/QyZCjBvFU89pp7dtUngiZ6MVv81ds2I+vcvk" + + "GlIFcxcE1XoCIB3EvbqWNaoOotgEPT60unnB2BeDV1KD3lDRouMIYHfZ3SzBwOOI" + + "6aK39sWnY5sAK7JjFvnDAMBdueOiI0Fy+gxbFD/zFDt4cWAVSAGTC4w371iqppmT" + + "25TM7zAtCgpiq5IsELPlUZZnXKmnYQ7OCeysF0eeVwf+OFB9fyvCEv/zVQocJCg8" + + "fWxfCBlIVFNeNQpeGygn/ZmRaILvB7IXDWP0oOw7/F2Ym66IdYYIp2HeEZv+jFwa" + + "l41w5W4BH/gtbwGjFQ6CvF/m+lfUv6ZZdzsMIeEOwhP5g7rXBxrbcnGBaU+PXbho" + + "gjDqaYzAWGlrmAd6aPSj51AGeYXkb2T1T/yoJ++M3GvhH4C4hvitamDkksh/qRnM" + + "M/s8Nku6z1+RXO3M6p5QC1nlAVqieU8esT43945eSoC77K8WyujDNbysDyUCUTzt" + + "p/aoQwe/HgkeOTJNelKR9y2W3xinZLFzep0SqpNI/e468yB/2/LGsykIyQa7JX6r" + + "BYwuBAIDAkOKfv5rK8v0YDfnN+eFqwhTcrfBj5rDH7hER6nW3lNWcMataUiHEaMg" + + "o6Q0OO1vptIGxW8jClTD4N1sCNwNu9vKny8dKYDDHbCjE06DNTv7XYVW3+JqTL5E" + + "BnidvGgOmA=="); + + /** + * decrypt the passed in message stream + */ + private byte[] decryptMessage( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(pass, new BcPGPDigestCalculatorProvider())); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + int ch; + + while ((ch = unc.read()) >= 0) + { + bOut.write(ch); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + private byte[] decryptMessageBuffered( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(pass, new BcPGPDigestCalculatorProvider())); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear);; + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + byte[] buf = new byte[1024]; + int len; + + while ((len = unc.read(buf)) >= 0) + { + bOut.write(buf, 0, len); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + public void performTest() + throws Exception + { + // compressed data not supported +// byte[] out = decryptMessage(enc1, TEST_DATE); +// +// if (out[0] != 'h' || out[1] != 'e' || out[2] != 'l') +// { +// fail("wrong plain text in packet"); +// } +// + // + // create a PBE encrypted message and read it back. + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // encryption step - convert to literal data, compress, encode. + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + Date cDate = new Date((System.currentTimeMillis() / 1000) * 1000); + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + OutputStream comOut = bOut; + OutputStream ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + text.length, + cDate); + + ldOut.write(text); + + ldOut.close(); + + comOut.close(); + + // + // encrypt - with stream close + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(new SecureRandom())); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + byte[] out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - with generator close + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(new SecureRandom())); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cPk.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - partial packet style. + // + SecureRandom rand = new SecureRandom(); + byte[] test = new byte[1233]; + + rand.nextBytes(test); + + bOut = new ByteArrayOutputStream(); + + comOut = bOut; + lData = new PGPLiteralDataGenerator(); + + ldOut = lData.open(new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, TEST_DATE, + new byte[16]); + + + ldOut.write(test); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // with integrity packet + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(true).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in buffer generated packet"); + } + + // + // sample message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPBEAsym); + + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpFact.nextObject(); + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(1); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory("password".toCharArray(), new BcPGPDigestCalculatorProvider())); + + pgpFact = new PGPObjectFactory(clear); + + // Compressed data not supported +// PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); +// +// bOut = new ByteArrayOutputStream(); +// InputStream unc = ld.getInputStream(); +// int ch; +// +// while ((ch = unc.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// if (!areEqual(bOut.toByteArray(), Hex.decode("5361742031302e30322e30370d0a"))) +// { +// fail("data mismatch on combined PBE"); +// } + + // + // with integrity packet - one byte message + // + byte[] msg = new byte[1]; + bOut = new ByteArrayOutputStream(); + + lData = new PGPLiteralDataGenerator(); + comOut = bOut; + ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + msg.length, + cDate); + + ldOut.write(msg); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(true).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in buffer generated packet"); + } + } + + public String getName() + { + return "BcPGPPBETest"; + } + + public static void main( + String[] args) + { + runTest(new BcPGPPBETest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPRSATest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPRSATest.java new file mode 100644 index 000000000..e6d790d3f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/BcPGPRSATest.java @@ -0,0 +1,1354 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.attr.ImageAttribute; +import org.spongycastle.bcpg.sig.Features; +import org.spongycastle.bcpg.sig.KeyFlags; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.BufferedAsymmetricBlockCipher; +import org.spongycastle.crypto.KeyGenerationParameters; +import org.spongycastle.crypto.engines.RSAEngine; +import org.spongycastle.crypto.generators.RSAKeyPairGenerator; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.RSAKeyGenerationParameters; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.PGPV3SignatureGenerator; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPRSATest + extends SimpleTest +{ + byte[] testPubKey = Base64.decode( + "mIsEPz2nJAEEAOTVqWMvqYE693qTgzKv/TJpIj3hI8LlYPC6m1dk0z3bDLwVVk9F" + + "FAB+CWS8RdFOWt/FG3tEv2nzcoNdRvjv9WALyIGNawtae4Ml6oAT06/511yUzXHO" + + "k+9xK3wkXN5jdzUhf4cA2oGpLSV/pZlocsIDL+jCUQtumUPwFodmSHhzAAYptC9F" + + "cmljIEVjaGlkbmEgKHRlc3Qga2V5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPoi4" + + "BBMBAgAiBQI/PackAhsDBQkAg9YABAsHAwIDFQIDAxYCAQIeAQIXgAAKCRA1WGFG" + + "/fPzc8WMA/9BbjuB8E48QAlxoiVf9U8SfNelrz/ONJA/bMvWr/JnOGA9PPmFD5Uc" + + "+kV/q+i94dEMjsC5CQ1moUHWSP2xlQhbOzBP2+oPXw3z2fBs9XJgnTH6QWMAAvLs" + + "3ug9po0loNHLobT/D/XdXvcrb3wvwvPT2FptZqrtonH/OdzT9JdfrA=="); + + byte[] testPrivKey = Base64.decode( + "lQH8BD89pyQBBADk1aljL6mBOvd6k4Myr/0yaSI94SPC5WDwuptXZNM92wy8FVZP" + + "RRQAfglkvEXRTlrfxRt7RL9p83KDXUb47/VgC8iBjWsLWnuDJeqAE9Ov+ddclM1x" + + "zpPvcSt8JFzeY3c1IX+HANqBqS0lf6WZaHLCAy/owlELbplD8BaHZkh4cwAGKf4D" + + "AwKbLeIOVYTEdWD5v/YgW8ERs0pDsSIfBTvsJp2qA798KeFuED6jGsHUzdi1M990" + + "6PRtplQgnoYmYQrzEc6DXAiAtBR4Kuxi4XHx0ZR2wpVlVxm2Ypgz7pbBNWcWqzvw" + + "33inl7tR4IDsRdJOY8cFlN+1tSCf16sDidtKXUVjRjZNYJytH18VfSPlGXMeYgtw" + + "3cSGNTERwKaq5E/SozT2MKTiORO0g0Mtyz+9MEB6XVXFavMun/mXURqbZN/k9BFb" + + "z+TadpkihrLD1xw3Hp+tpe4CwPQ2GdWKI9KNo5gEnbkJgLrSMGgWalPhknlNHRyY" + + "bSq6lbIMJEE3LoOwvYWwweR1+GrV9farJESdunl1mDr5/d6rKru+FFDwZM3na1IF" + + "4Ei4FpqhivZ4zG6pN5XqLy+AK85EiW4XH0yAKX1O4YlbmDU4BjxhiwTdwuVMCjLO" + + "5++jkz5BBQWdFX8CCMA4FJl36G70IbGzuFfOj07ly7QvRXJpYyBFY2hpZG5hICh0" + + "ZXN0IGtleSkgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IuAQTAQIAIgUCPz2nJAIb" + + "AwUJAIPWAAQLBwMCAxUCAwMWAgECHgECF4AACgkQNVhhRv3z83PFjAP/QW47gfBO" + + "PEAJcaIlX/VPEnzXpa8/zjSQP2zL1q/yZzhgPTz5hQ+VHPpFf6voveHRDI7AuQkN" + + "ZqFB1kj9sZUIWzswT9vqD18N89nwbPVyYJ0x+kFjAALy7N7oPaaNJaDRy6G0/w/1" + + "3V73K298L8Lz09habWaq7aJx/znc0/SXX6w="); + + byte[] testPubKeyV3 = Base64.decode( + "mQCNAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURtApGSVhDSVRZX1FBiQCVAwUQP7O+UZ6Fwdi2fBUJAQFMwwQA" + + "qRnFsdg4xQnB8Y5d4cOpXkIn9AZgYS3cxtuSJB84vG2CgC39nfv4c+nlLkWP" + + "4puG+mZuJNgVoE84cuAF4I//1anKjlU7q1M6rFQnt5S4uxPyG3dFXmgyU1b4" + + "PBOnA0tIxjPzlIhJAMsPCGGA5+5M2JP0ad6RnzqzE3EENMX+GqY="); + + byte[] testPrivKeyV3 = Base64.decode( + "lQHfAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURAXWwRBZQHNikA/f0ScLLjrXi4s0hgQecg+dkpDow94eu5+AR" + + "0DzZnfurpgfUJCNiDi5W/5c3Zj/xyrfMAgkbCgJ1m6FZqAQh7Mq73l7Kfu4/" + + "XIkyDF3tDgRuZNezB+JuElX10tV03xumHepp6M6CfhXqNJ15F33F99TA5hXY" + + "CPYD7SiSOpIhQkCOAgDAA63imxbpuKE2W7Y4I1BUHB7WQi8ZdkZd04njNTv+" + + "rFUuOPapQVfbWG0Vq8ld3YmJB4QWsa2mmqn+qToXbwufAgBpXkjvqK5yPiHF" + + "Px2QbFc1VqoCJB6PO5JRIqEiUZBFGdDlLxt3VSyqz7IZ/zEnxZq+tPCGGGSm" + + "/sAGiMvENcHVAfy0kTXU42TxEAYJyyNyqjXOobDJpEV1mKhFskRXt7tbMfOS" + + "Yf91oX8f6xw6O2Nal+hU8dS0Bmfmk5/enHmvRLHQocO0CkZJWENJVFlfUUE="); + + byte[] sig1 = Base64.decode( + "owGbwMvMwMRoGpHo9vfz52LGNTJJnBmpOTn5eiUVJfb23JvAHIXy/KKcFEWuToap" + + "zKwMIGG4Bqav0SwMy3yParsEKi2LMGI9xhh65sBxb05n5++ZLcWNJ/eLFKdWbm95" + + "tHbDV7GMwj/tUctUpFUXWPYFCLdNsDiVNuXbQvZtdXV/5xzY+9w1nCnijH9JoNiJ" + + "22n2jo0zo30/TZLo+jDl2vTzIvPeLEsPM3ZUE/1Ytqs4SG2TxIQbH7xf3uzcYXq2" + + "5Fw9AA=="); + + byte[] sig1crc = Base64.decode("+3i0"); + + byte[] subKey = Base64.decode( + "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" + + "CphiUYWnsC0mQ+J15B4="); + + byte[] enc1 = Base64.decode( + "hIwDKwfQexPJboABA/4/7prhYYMORTiQ5avQKx0XYpCLujzGefYjnyuWZnx3Iev8" + + "Pmsguumm+OLLvtXhhkXQmkJRXbIg6Otj2ubPYWflRPgpJSgOrNOreOl5jeABOrtw" + + "bV6TJb9OTtZuB7cTQSCq2gmYiSZkluIiDjNs3R3mEanILbYzOQ3zKSggKpzlv9JQ" + + "AZUqTyDyJ6/OUbJF5fI5uiv76DCsw1zyMWotUIu5/X01q+AVP5Ly3STzI7xkWg/J" + + "APz4zUHism7kSYz2viAQaJx9/bNnH3AM6qm1Fuyikl4="); + + byte[] enc1crc = Base64.decode("lv4o"); + + byte[] enc2 = Base64.decode( + "hIwDKwfQexPJboABBAC62jcJH8xKnKb1neDVmiovYON04+7VQ2v4BmeHwJrdag1g" + + "Ya++6PeBlQ2Q9lSGBwLobVuJmQ7cOnPUJP727JeSGWlMyFtMbBSHekOaTenT5lj7" + + "Zk7oRHxMp/hByzlMacIDzOn8LPSh515RHM57eDLCOwqnAxGQwk67GRl8f5dFH9JQ" + + "Aa7xx8rjCqPbiIQW6t5LqCNvPZOiSCmftll6+se1XJhFEuq8WS4nXtPfTiJ3vib4" + + "3soJdHzGB6AOs+BQ6aKmmNTVAxa5owhtSt1Z/6dfSSk="); + + byte[] subPubKey = Base64.decode( + "mIsEPz2nJAEEAOTVqWMvqYE693qTgzKv/TJpIj3hI8LlYPC6m1dk0z3bDLwVVk9F" + + "FAB+CWS8RdFOWt/FG3tEv2nzcoNdRvjv9WALyIGNawtae4Ml6oAT06/511yUzXHO" + + "k+9xK3wkXN5jdzUhf4cA2oGpLSV/pZlocsIDL+jCUQtumUPwFodmSHhzAAYptC9F" + + "cmljIEVjaGlkbmEgKHRlc3Qga2V5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPoi4" + + "BBMBAgAiBQI/PackAhsDBQkAg9YABAsHAwIDFQIDAxYCAQIeAQIXgAAKCRA1WGFG" + + "/fPzc8WMA/9BbjuB8E48QAlxoiVf9U8SfNelrz/ONJA/bMvWr/JnOGA9PPmFD5Uc" + + "+kV/q+i94dEMjsC5CQ1moUHWSP2xlQhbOzBP2+oPXw3z2fBs9XJgnTH6QWMAAvLs" + + "3ug9po0loNHLobT/D/XdXvcrb3wvwvPT2FptZqrtonH/OdzT9JdfrIhMBBARAgAM" + + "BQI/RxooBYMAemL8AAoJEM0j9enEyjRDiBgAn3RcLK+gq90PvnQFTw2DNqdq7KA0" + + "AKCS0EEIXCzbV1tfTdCUJ3hVh3btF7QkRXJpYyBFY2hpZG5hIDxlcmljQGJvdW5j" + + "eWNhc3RsZS5vcmc+iLgEEwECACIFAj9HFA0CGwMFCQCD1gAECwcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEDVYYUb98/Nzc7oD/j21cUKKI/y+Dvs5e+eZufM8EDUbqMJFk0/X" + + "Ihe6w6OkerIs3kr2HDqWr+jik2IK6KrfaiyoZFfeW/+cN24lTWSfY7D4jZVyL6iM" + + "xd0IZ0So/Bl+/juMcvBGshQnbY46haw5G2C9J5FR3gjODyOu5oUzu5+vmGuMSXVy" + + "4tbUevwuiEwEEBECAAwFAj9HGigFgwB6YvwACgkQzSP16cTKNEPwBQCdHm0Amwza" + + "NmVmDHm3rmqI7rp2oQ0An2YbiP/H/kmBNnmTeH55kd253QOhuIsEP0ccUAEEANCd" + + "gSHam/CbQxio72Ma47tLqyVVc8iNQpwowKewrS6B0CVEFOz2o51eCXddrpH3KCuz" + + "Ea2mMnUBI8YdjKj6bkICUt3YEFa8Tj2qw0GARa2TXdrWcTo2GpIRK71JkaOslhWs" + + "QocKjIJfF1roATYNQ72vv2HKBkPpcviYOEVmaB/PAAYpiJ8EGAECAAkFAj9HHFAC" + + "GwwACgkQNVhhRv3z83M5hQP8DIOR4TeTZKpo5x1BX3lzV3R85npMsN/EZ7gjCpn3" + + "ONwIg7PhbSetoKUOjxLGReKPUuX8XI1ZN1UoNNFzA7UP9AbCgXORsYSU6uxvv0qH" + + "jON91htfbqEtPuS3/99cDaE56b+p6TmmKXE0Oy/Swp88ABeB9ISTxOm4ZltgjHLB" + + "PyGZAaIEP0cHNREEALOTFEJUYmDn4t30NThemd3gsIqAyrtg5jsaLQSAr3aBtprr" + + "7NChAWrPW23dBSanPdhWSb/MsRIEG0LUAIhcaCBR0UxSvkjzv7UJvq/N58C0a3w3" + + "lVXUX8xe/WAnPgG7ofLEv+dx97vWOw2Fq/SLjICN/Z61YWPnfMrpH46ODkfrAKCk" + + "v3+1dV8Y/xKoW/Ks3/jfsyUVJQQAg3dXqP8xSWXKtmCWu2vb5FYjBGsb8AKasXqF" + + "O6A6XhxDFoNg1ye1duGVOUmz0zGNMffHHPKXKKsLKrsfMYnFnNK3oVN6HUMVjOEn" + + "tC5hHXGtJ0DpjSZS/SLEPkDic8eO6lDlAKocZ5F8UP+gfMPjOoGcmn8Encu9Drps" + + "REmYqXkD/i6PcehXGlvS+/IQ19GooyYYEhLJxjgygfm5Z9vbcV9+xP2v0ovqcniz" + + "IvNpFHSC9xtyORz1nd6kjuCveNOtvh3xY/GwNtP9bW2/1sb8QdrCiYtyKG25RTrx" + + "R6Pr/Bmi3Vcmq7adCWS7nv4z9XZfXLbpUqyKmiU9+Nri7IE47K9stDNFcmljIEVj" + + "aGlkbmEgKERTQSBUZXN0IEtleSkgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IWQQT" + + "EQIAGQUCP0cHNQQLBwMCAxUCAwMWAgECHgECF4AACgkQzSP16cTKNEMCXACfauui" + + "bSwyG59Yrm8hHCDuCPmqwsQAni+dPl08FVuWh+wb6kOgJV4lcYae"); + + byte[] subPubCrc = Base64.decode("rikt"); + + byte[] pgp8Key = Base64.decode( + "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF" + + "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd" + + "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy" + + "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y" + + "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7" + + "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO" + + "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP" + + "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY" + + "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb" + + "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4" + + "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj" + + "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I" + + "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH" + + "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt" + + "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j" + + "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw" + + "AgAA"); + + char[] pgp8Pass = "2002 Buffalo Sabres".toCharArray(); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + byte[] fingerprintKey = Base64.decode( + "mQEPA0CiJdUAAAEIAMI+znDlPd2kQoEcnxqxLcRz56Z7ttFKHpnYp0UkljZdquVc" + + "By1jMfXGVV64xN1IvMcyenLXUE0IUeUBCQs6tHunFRAPSeCxJ3FdFe1B5MpqQG8A" + + "BnEpAds/hAUfRDZD5y/lolk1hjvFMrRh6WXckaA/QQ2t00NmTrJ1pYUpkw9tnVQb" + + "LUjWJhfZDBBcN0ADtATzgkugxMtcDxR6I5x8Ndn+IilqIm23kxGIcmMd/BHOec4c" + + "jRwJXXDb7u8tl+2knAf9cwhPHp3+Zy4uGSQPdzQnXOhBlA+4WDa0RROOevWgq8uq" + + "8/9Xp/OlTVL+OoIzjsI6mJP1Joa4qmqAnaHAmXcAEQEAAbQoQk9BM1JTS1kgPEJP" + + "QSBNb25pdG9yaW5nIEAgODg4LTI2OS01MjY2PokBFQMFEECiJdWqaoCdocCZdwEB" + + "0RsH/3HPxoUZ3G3K7T3jgOnJUckTSHWU3XspHzMVgqOxjTrcexi5IsAM5M+BulfW" + + "T2aO+Kqf5w8cKTKgW02DNpHUiPjHx0nzDE+Do95zbIErGeK+Twkc4O/aVsvU9GGO" + + "81VFI6WMvDQ4CUAUnAdk03MRrzI2nAuhn4NJ5LQS+uJrnqUJ4HmFAz6CQZQKd/kS" + + "Xgq+A6i7aI1LG80YxWa9ooQgaCrb9dwY/kPQ+yC22zQ3FExtv+Fv3VtAKTilO3vn" + + "BA4Y9uTHuObHfI+1yxUS2PrlRUX0m48ZjpIX+cEN3QblGBJudI/A1QSd6P0LZeBr" + + "7F1Z1aF7ZDo0KzgiAIBvgXkeTpw="); + + byte[] fingerprintCheck = Base64.decode("CTv2"); + + byte[] expiry60and30daysSig13Key = Base64.decode( + "mQGiBENZt/URBAC5JccXiwe4g6MuviEC8NI/x0NaVkGFAOY04d5E4jeIycBP" + + "SrpOPrjETuigqhrj8oqed2+2yUqfnK4nhTsTAjyeJ3PpWC1pGAKzJgYmJk+K" + + "9aTLq0BQWiXDdv5RG6fDmeq1umvOfcXBqGFAguLPZC+U872bSLnfe3lqGNA8" + + "jvmY7wCgjhzVQVm10NN5ST8nemPEcSjnBrED/R494gHL6+r5OgUgXnNCDejA" + + "4InoDImQCF+g7epp5E1MB6CMYSg2WSY2jHFuHpwnUb7AiOO0ZZ3UBqM9rYnK" + + "kDvxkFCxba7Ms+aFj9blRNmy3vG4FewDcTdxzCtjUk6dRfu6UoARpqlTE/q7" + + "Xo6EQP1ncwJ+UTlcHkTBvg/usI/yBACGjBqX8glb5VfNaZgNHMeS/UIiUiuV" + + "SVFojiSDOHcnCe/6y4M2gVm38zz1W9qhoLfLpiAOFeL0yj6wzXvsjjXQiKQ8" + + "nBE4Mf+oeH2qiQ/LfzQrGpI5eNcMXrzK9nigmz2htYO2GjQfupEnu1RHBTH8" + + "NjofD2AShL9IO73plRuExrQgVGVzdCBLZXkgPHRlc3RAYm91bmN5Y2FzdGxl" + + "Lm9yZz6IZAQTEQIAJAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAUCQ1m4DgUJ" + + "AE8aGQAKCRD8QP1QuU7Kqw+eAJ0dZ3ZAqr73X61VmCkbyPoszLQMAQCfdFs2" + + "YMDeUvX34Q/8Ba0KgO5f3RSwAgADuM0EQ1m39hADAIHpVGcLqS9UkmQaWBvH" + + "WP6TnN7Y1Ha0TJOuxpbFjBW+CmVh/FjcsnavFXDXpo2zc742WT+vrHBSa/0D" + + "1QEBsnCaX5SRRVp7Mqs8q+aDhjcHMIP8Sdxf7GozXDORkrRaJwADBQL9HLYm" + + "7Rr5iYWDcvs+Pi6O1zUyb1tjkxEGaV/rcozl2MMmr2mzJ6x/Bz8SuhZEJS0m" + + "bB2CvAA39aQi9jHlV7q0SV73NOkd2L/Vt2UZhzlUdvrJ37PgYDv+Wd9Ufz6g" + + "MzLSiE8EGBECAA8FAkNZt/YCGwwFCQAnjQAACgkQ/ED9ULlOyqsTqQCcDnAZ" + + "7YymCfhm1yJiuFQg3qiX6Z4An19OSEgeSKugVcH49g1sxUB0zNdIsAIAAw=="); + + byte[] jpegImage = Base64.decode( + "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"); + + byte[] embeddedJPEGKey = Base64.decode( + "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" + + "Qvmtc8X7lqK/zLRVkSSBItik2IIhsAIAAw=="); + + + private void fingerPrintTest() + throws Exception + { + // + // version 3 + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(fingerprintKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("4FFB9F0884266C715D1CEAC804A3BBFA"))) + { + fail("version 3 fingerprint test failed"); + } + + // + // version 4 + // + pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("3062363c1046a01a751946bb35586146fdf3f373"))) + { + fail("version 4 fingerprint test failed"); + } + } + + private void mixedTest(PGPPrivateKey pgpPrivKey, PGPPublicKey pgpPubKey) + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // literal data + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, text.length, new Date()); + + lOut.write(text); + + lGen.close(); + + byte[] bytes = bOut.toByteArray(); + + PGPObjectFactory f = new PGPObjectFactory(bytes); + checkLiteralData((PGPLiteralData)f.nextObject(), text); + + ByteArrayOutputStream bcOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_128).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom())); + + encGen.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(pgpPubKey)); + + encGen.addMethod(new BcPBEKeyEncryptionMethodGenerator("password".toCharArray())); + + OutputStream cOut = encGen.open(bcOut, bytes.length); + + cOut.write(bytes); + + cOut.close(); + + byte[] encData = bcOut.toByteArray(); + + // + // asymmetric + // + PGPObjectFactory pgpF = new PGPObjectFactory(encData); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpFact.nextObject(), text); + + // + // PBE + // + pgpF = new PGPObjectFactory(encData); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPBEEncryptedData encPbe = (PGPPBEEncryptedData)encList.get(1); + + clear = encPbe.getDataStream(new BcPBEDataDecryptorFactory("password".toCharArray(), new BcPGPDigestCalculatorProvider())); + + pgpF = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpF.nextObject(), text); + } + + private void checkLiteralData(PGPLiteralData ld, byte[] data) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals(PGPLiteralData.CONSOLE)) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + int ch; + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), data)) + { + fail("wrong plain text in decrypted packet"); + } + } + + private void existingEmbeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(embeddedJPEGKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature sig = (PGPSignature)sigs.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + if (!sig.verifyCertification(attributes, pubKey)) + { + fail("signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find user attributes"); + } + } + + private void embeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + PGPSecretKeyRing pgpSec = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + PGPUserAttributeSubpacketVectorGenerator vGen = new PGPUserAttributeSubpacketVectorGenerator(); + + vGen.setImageAttribute(ImageAttribute.JPEG, jpegImage); + + PGPUserAttributeSubpacketVector uVec = vGen.generate(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, pgpSec.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass))); + + PGPSignature sig = sGen.generateCertification(uVec, pubKey); + + PGPPublicKey nKey = PGPPublicKey.addCertification(pubKey, uVec, sig); + + Iterator it = nKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = nKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature s = (PGPSignature)sigs.next(); + + s.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + if (!s.verifyCertification(attributes, pubKey)) + { + fail("added signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed added user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find added user attributes"); + } + + nKey = PGPPublicKey.removeCertification(nKey, uVec); + count = 0; + for (it = nKey.getUserAttributes(); it.hasNext();) + { + count++; + } + if (count != 0) + { + fail("found attributes where none expected"); + } + } + + private void sigsubpacketTest() + throws Exception + { + char[] passPhrase = "test".toCharArray(); + String identity = "TEST <test@test.org>"; + Date date = new Date(); + + RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); + kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 2048, 25)); + AsymmetricCipherKeyPair kpSgn = kpg.generateKeyPair(); + AsymmetricCipherKeyPair kpEnc = kpg.generateKeyPair(); + + PGPKeyPair sgnKeyPair = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpSgn, date); + PGPKeyPair encKeyPair = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpEnc, date); + + PGPSignatureSubpacketVector unhashedPcks = null; + PGPSignatureSubpacketGenerator svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setPrimaryUserID(true, true); + int[] encAlgs = {SymmetricKeyAlgorithmTags.AES_256, + SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.TRIPLE_DES}; + svg.setPreferredSymmetricAlgorithms(true, encAlgs); + int[] hashAlgs = {HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA512, + HashAlgorithmTags.SHA384, + HashAlgorithmTags.SHA256, + HashAlgorithmTags.RIPEMD160}; + svg.setPreferredHashAlgorithms(true, hashAlgs); + int[] comprAlgs = {CompressionAlgorithmTags.ZLIB, + CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP}; + svg.setPreferredCompressionAlgorithms(true, comprAlgs); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + svg.setKeyFlags(true, KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA); + PGPSignatureSubpacketVector hashedPcks = svg.generate(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + sgnKeyPair, identity, new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1), + hashedPcks, unhashedPcks, new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + + svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setKeyFlags(true, KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); + svg.setPrimaryUserID(true, false); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + hashedPcks = svg.generate(); + + keyRingGen.addSubKey(encKeyPair, hashedPcks, unhashedPcks); + + byte[] encodedKeyRing = keyRingGen.generatePublicKeyRing().getEncoded(); + + PGPPublicKeyRing keyRing = new PGPPublicKeyRing(encodedKeyRing, new BcKeyFingerprintCalculator()); + + for (Iterator it = keyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pKey = (PGPPublicKey)it.next(); + + if (pKey.isEncryptionKey()) + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (v.getKeyExpirationTime() != 86400L * 366 * 2) + { + fail("key expiration time wrong"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.isPrimaryUserID()) + { + fail("primary userID flag wrong"); + } + if (v.getKeyFlags() != KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE) + { + fail("keyFlags wrong"); + } + } + } + else + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (!Arrays.areEqual(v.getPreferredSymmetricAlgorithms(), encAlgs)) + { + fail("preferred encryption algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredHashAlgorithms(), hashAlgs)) + { + fail("preferred hash algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredCompressionAlgorithms(), comprAlgs)) + { + fail("preferred compression algs don't match"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.getKeyFlags() != KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA) + { + fail("keyFlags wrong"); + } + } + } + } + } + + public void performTest() + throws Exception + { + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + AsymmetricKeyParameter pubKey = new BcPGPKeyConverter().getPublicKey(pgpPub.getPublicKey()); + + Iterator it = pgpPub.getPublicKey().getUserIDs(); + + String uid = (String)it.next(); + + it = pgpPub.getPublicKey().getSignaturesForID(uid); + + PGPSignature sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + if (!sig.verifyCertification(uid, pgpPub.getPublicKey())) + { + fail("failed to verify certification"); + } + + // + // write a public key + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pgpPub.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPubKey)) + { + fail("public key rewrite failed"); + } + + // + // Read the public key + // + PGPPublicKeyRing pgpPubV3 = new PGPPublicKeyRing(testPubKeyV3, new BcKeyFingerprintCalculator()); + AsymmetricKeyParameter pubKeyV3 = new BcPGPKeyConverter().getPublicKey(pgpPub.getPublicKey()); + + // + // write a V3 public key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPubV3.encode(pOut); + + // + // Read a v3 private key + // + char[] passP = "FIXCITY_QA".toCharArray(); + + if (!noIDEA()) + { + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKeyV3, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passP)); + + // + // write a v3 private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKeyV3)) + { + fail("private key V3 rewrite failed"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // write a private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKey)) + { + fail("private key rewrite failed"); + } + + + // + // test encryption + // + BufferedAsymmetricBlockCipher c = new BufferedAsymmetricBlockCipher(new RSAEngine()); + + c.init(true, pubKey); + + byte[] in = "hello world".getBytes(); + + c.processBytes(in, 0, in.length); + + byte[] out = c.doFinal(); + + c.init(false, new BcPGPKeyConverter().getPrivateKey(pgpPrivKey)); + + c.processBytes(out, 0, out.length); + + out = c.doFinal(); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // test signature message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1, new BcKeyFingerprintCalculator()); + +// PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + +// PGPOnePassSignature ops = p1.get(0); + + // compression not supported +// PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); +// +// InputStream dIn = p2.getInputStream(); +// int ch; +// +// ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey(ops.getKeyID())); +// +// while ((ch = dIn.read()) >= 0) +// { +// ops.update((byte)ch); +// } +// +// PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); +// +// if (!ops.verify(p3.get(0))) +// { +// fail("Failed signature check"); +// } +// + // + // encrypted message - read subkey + // + pgpPriv = new PGPSecretKeyRing(subKey, new BcKeyFingerprintCalculator()); + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(enc1, new BcKeyFingerprintCalculator()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator()); + + // compressed data not supported +// PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); +// +// bOut = new ByteArrayOutputStream(); +// +// if (!ld.getFileName().equals("test.txt")) +// { +// throw new RuntimeException("wrong filename in packet"); +// } +// +// InputStream inLd = ld.getDataStream(); +// int ch; +// +// while ((ch = inLd.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// if (!areEqual(bOut.toByteArray(), text)) +// { +// fail("wrong plain text in decrypted packet"); +// } + + // + // encrypt - short message + // + byte[] shortText = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o' }; + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), shortText.length); + + cOut.write(shortText); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray(), new BcKeyFingerprintCalculator()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + PublicKeyDataDecryptorFactory dataDecryptorFactory = new BcPublicKeyDataDecryptorFactory(pgpPrivKey); + + if (encP.getSymmetricAlgorithm(dataDecryptorFactory) != SymmetricKeyAlgorithmTags.CAST5) + { + fail("symmetric algorithm mismatch"); + } + + clear = encP.getDataStream(dataDecryptorFactory); + + bOut.reset(); + + int ch; + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, shortText)) + { + fail("wrong plain text in generated short text packet"); + } + + // + // encrypt + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(new SecureRandom())); + puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // read public key with sub key. + // + pgpF = new PGPObjectFactory(subPubKey, new BcKeyFingerprintCalculator()); + Object o; + +// while ((o = pgpFact.nextObject()) != null) +// { +// // System.out.println(o); +// } + + // + // key pair generation - CAST5 encryption + // + char[] passPhrase = "hello".toCharArray(); + + RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); + + kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25)); + + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, new BcPGPKeyPair(PublicKeyAlgorithmTags.RSA_GENERAL, kp, new Date()), "fred", null, null, new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).build(passPhrase)); + + PGPPublicKey key = secretKey.getPublicKey(); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + key = PGPPublicKey.removeCertification(key, uid, sig); + + if (key == null) + { + fail("failed certification removal"); + } + + byte[] keyEnc = key.getEncoded(); + + key = PGPPublicKey.addCertification(key, uid, sig); + + keyEnc = key.getEncoded(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.KEY_REVOCATION, secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase))); + + sig = sGen.generateCertification(key); + + key = PGPPublicKey.addCertification(key, sig); + + keyEnc = key.getEncoded(); + + PGPPublicKeyRing tmpRing = new PGPPublicKeyRing(keyEnc, new BcKeyFingerprintCalculator()); + + key = tmpRing.getPublicKey(); + + Iterator sgIt = key.getSignaturesOfType(PGPSignature.KEY_REVOCATION); + + sig = (PGPSignature)sgIt.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(key)) + { + fail("failed to verify revocation certification"); + } + + // + // use of PGPKeyPair + // + PGPKeyPair pgpKp = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL , kp, new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + k1.getEncoded(); + + mixedTest(k2, k1); + + // + // key pair generation - AES_256 encryption. + // + kp = kpg.generateKeyPair(); + + secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, pgpKp, "fred", null, null, new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256).build(passPhrase)); + + secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + secretKey.encode(new ByteArrayOutputStream()); + + // + // secret key password changing. + // + String newPass = "newPass"; + + secretKey = PGPSecretKey.copyWithNewPassword(secretKey, new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase), new BcPBESecretKeyEncryptorBuilder(secretKey.getKeyEncryptionAlgorithm()).build(newPass.toCharArray())); + + secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(newPass.toCharArray())); + + secretKey.encode(new ByteArrayOutputStream()); + + key = secretKey.getPublicKey(); + + key.encode(new ByteArrayOutputStream()); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(newPass.toCharArray())); + + // + // signature generation + // + String data = "hello world!"; + + bOut = new ByteArrayOutputStream(); + + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + + sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bOut); + + bOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved: " + p2.getModificationTime() + " " + testDate); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), secretKey.getPublicKey()); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // signature generation - version 3 + // + bOut = new ByteArrayOutputStream(); + + testIn = new ByteArrayInputStream(data.getBytes()); + PGPV3SignatureGenerator sGenV3 = new PGPV3SignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + sGen.generateOnePassVersion(false).encode(bOut); + + lGen = new PGPLiteralDataGenerator(); + lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bOut); + + bOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), secretKey.getPublicKey()); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed v3 generated signature check"); + } + + // + // extract PGP 8 private key + // + pgpPriv = new PGPSecretKeyRing(pgp8Key, new BcKeyFingerprintCalculator()); + + secretKey = pgpPriv.getSecretKey(); + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pgp8Pass)); + + // + // expiry + // + testExpiry(expiry60and30daysSig13Key, 60, 30); + + fingerPrintTest(); + existingEmbeddedJpegTest(); + embeddedJpegTest(); + sigsubpacketTest(); + } + + private void testExpiry( + byte[] encodedRing, + int masterDays, + int subKeyDays) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(encodedRing, new BcKeyFingerprintCalculator()); + PGPPublicKey k = pubRing.getPublicKey(); + + if (k.getValidDays() != masterDays) + { + fail("mismatch on master valid days."); + } + + Iterator it = pubRing.getPublicKeys(); + + it.next(); + + k = (PGPPublicKey)it.next(); + + if (k.getValidDays() != subKeyDays) + { + fail("mismatch on subkey valid days."); + } + } + + private boolean noIDEA() + { + return true; + } + + public String getName() + { + return "BcPGPRSATest"; + } + + public static void main( + String[] args) + { + runTest(new BcPGPRSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/RegressionTest.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/RegressionTest.java new file mode 100644 index 000000000..796d12031 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/test/RegressionTest.java @@ -0,0 +1,32 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class RegressionTest +{ + public static Test[] tests = { + new BcPGPDSAElGamalTest(), + new BcPGPDSATest(), + new BcPGPKeyRingTest(), + new BcPGPPBETest(), + new BcPGPRSATest() + }; + + public static void main( + String[] args) + { + for (int i = 0; i != tests.length; i++) + { + TestResult result = tests[i].perform(); + + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + + System.out.println(result); + } + } +} + diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/BZip2Constants.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/BZip2Constants.java new file mode 100644 index 000000000..9bfd74e0f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/BZip2Constants.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.spongycastle.apache.bzip2; + +/** + * Base class for both the compress and decompress classes. + * Holds common arrays, and static data. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public interface BZip2Constants { + + int baseBlockSize = 100000; + int MAX_ALPHA_SIZE = 258; + int MAX_CODE_LEN = 23; + int RUNA = 0; + int RUNB = 1; + int N_GROUPS = 6; + int G_SIZE = 50; + int N_ITERS = 4; + int MAX_SELECTORS = (2 + (900000 / G_SIZE)); + int NUM_OVERSHOOT_BYTES = 20; + + int[] rNums = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + }; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2InputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2InputStream.java new file mode 100644 index 000000000..6609482c9 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2InputStream.java @@ -0,0 +1,848 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ +package org.spongycastle.apache.bzip2; + +import java.io.InputStream; +import java.io.IOException; + +/** + * An input stream that decompresses from the BZip2 format (with the file + * header chars) to be read as any other stream. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * + * <b>NB:</b> note this class has been modified to read the leading BZ from the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ +public class CBZip2InputStream extends InputStream implements BZip2Constants { + private static void cadvise() { + System.out.println("CRC Error"); + //throw new CCoruptionError(); + } + +// private static void badBGLengths() { +// cadvise(); +// } +// +// private static void bitStreamEOF() { +// cadvise(); +// } + + private static void compressedStreamEOF() { + cadvise(); + } + + private void makeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + private int last; + + /* + index in zptr[] of original string after sorting. + */ + private int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + private int blockSize100k; + + private boolean blockRandomised; + + private int bsBuff; + private int bsLive; + private CRC mCrc = new CRC(); + + private boolean[] inUse = new boolean[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[MAX_SELECTORS]; + private char[] selectorMtf = new char[MAX_SELECTORS]; + + private int[] tt; + private char[] ll8; + + /* + freq table collected to save a pass over the data + during decompression. + */ + private int[] unzftab = new int[256]; + + private int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[] minLens = new int[N_GROUPS]; + + private InputStream bsStream; + + private boolean streamEnd = false; + + private int currentChar = -1; + + private static final int START_BLOCK_STATE = 1; + private static final int RAND_PART_A_STATE = 2; + private static final int RAND_PART_B_STATE = 3; + private static final int RAND_PART_C_STATE = 4; + private static final int NO_RAND_PART_A_STATE = 5; + private static final int NO_RAND_PART_B_STATE = 6; + private static final int NO_RAND_PART_C_STATE = 7; + + private int currentState = START_BLOCK_STATE; + + private int storedBlockCRC, storedCombinedCRC; + private int computedBlockCRC, computedCombinedCRC; + + int i2, count, chPrev, ch2; + int i, tPos; + int rNToGo = 0; + int rTPos = 0; + int j2; + char z; + + public CBZip2InputStream(InputStream zStream) + throws IOException + { + ll8 = null; + tt = null; + bsSetStream(zStream); + initialize(); + initBlock(); + setupBlock(); + } + + public int read() { + if (streamEnd) { + return -1; + } else { + int retChar = currentChar; + switch(currentState) { + case START_BLOCK_STATE: + break; + case RAND_PART_A_STATE: + break; + case RAND_PART_B_STATE: + setupRandPartB(); + break; + case RAND_PART_C_STATE: + setupRandPartC(); + break; + case NO_RAND_PART_A_STATE: + break; + case NO_RAND_PART_B_STATE: + setupNoRandPartB(); + break; + case NO_RAND_PART_C_STATE: + setupNoRandPartC(); + break; + default: + break; + } + return retChar; + } + } + + private void initialize() throws IOException { + char magic3, magic4; + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + if (magic3 != 'B' && magic4 != 'Z') + { + throw new IOException("Not a BZIP2 marked stream"); + } + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + if (magic3 != 'h' || magic4 < '1' || magic4 > '9') { + bsFinishedWithStream(); + streamEnd = true; + return; + } + + setDecompressStructureSizes(magic4 - '0'); + computedCombinedCRC = 0; + } + + private void initBlock() { + char magic1, magic2, magic3, magic4; + char magic5, magic6; + magic1 = bsGetUChar(); + magic2 = bsGetUChar(); + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + magic5 = bsGetUChar(); + magic6 = bsGetUChar(); + if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45 + && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) { + complete(); + return; + } + + if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59 + || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) { + badBlockHeader(); + streamEnd = true; + return; + } + + storedBlockCRC = bsGetInt32(); + + if (bsR(1) == 1) { + blockRandomised = true; + } else { + blockRandomised = false; + } + + // currBlockNo++; + getAndMoveToFrontDecode(); + + mCrc.initialiseCRC(); + currentState = START_BLOCK_STATE; + } + + private void endBlock() { + computedBlockCRC = mCrc.getFinalCRC(); + /* A bad CRC is considered a fatal error. */ + if (storedBlockCRC != computedBlockCRC) { + crcError(); + } + + computedCombinedCRC = (computedCombinedCRC << 1) + | (computedCombinedCRC >>> 31); + computedCombinedCRC ^= computedBlockCRC; + } + + private void complete() { + storedCombinedCRC = bsGetInt32(); + if (storedCombinedCRC != computedCombinedCRC) { + crcError(); + } + + bsFinishedWithStream(); + streamEnd = true; + } + + private static void blockOverrun() { + cadvise(); + } + + private static void badBlockHeader() { + cadvise(); + } + + private static void crcError() { + cadvise(); + } + + private void bsFinishedWithStream() { + try { + if (this.bsStream != null) { + if (this.bsStream != System.in) { + this.bsStream.close(); + this.bsStream = null; + } + } + } catch (IOException ioe) { + //ignore + } + } + + private void bsSetStream(InputStream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + } + + private int bsR(int n) { + int v; + while (bsLive < n) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + + v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); + bsLive -= n; + return v; + } + + private char bsGetUChar() { + return (char) bsR(8); + } + + private int bsGetint() { + int u = 0; + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + return u; + } + + private int bsGetIntVS(int numBits) { + return (int) bsR(numBits); + } + + private int bsGetInt32() { + return (int) bsGetint(); + } + + private void hbCreateDecodeTables(int[] limit, int[] base, + int[] perm, char[] length, + int minLen, int maxLen, int alphaSize) { + int pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) { + for (j = 0; j < alphaSize; j++) { + if (length[j] == i) { + perm[pp] = j; + pp++; + } + } + } + + for (i = 0; i < MAX_CODE_LEN; i++) { + base[i] = 0; + } + for (i = 0; i < alphaSize; i++) { + base[length[i] + 1]++; + } + + for (i = 1; i < MAX_CODE_LEN; i++) { + base[i] += base[i - 1]; + } + + for (i = 0; i < MAX_CODE_LEN; i++) { + limit[i] = 0; + } + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i + 1] - base[i]); + limit[i] = vec - 1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) { + base[i] = ((limit[i - 1] + 1) << 1) - base[i]; + } + } + + private void recvDecodingTables() { + char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE]; + int i, j, t, nGroups, nSelectors, alphaSize; + int minLen, maxLen; + boolean[] inUse16 = new boolean[16]; + + /* Receive the mapping table */ + for (i = 0; i < 16; i++) { + if (bsR(1) == 1) { + inUse16[i] = true; + } else { + inUse16[i] = false; + } + } + + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (bsR(1) == 1) { + inUse[i * 16 + j] = true; + } + } + } + } + + makeMaps(); + alphaSize = nInUse + 2; + + /* Now the selectors */ + nGroups = bsR(3); + nSelectors = bsR(15); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (bsR(1) == 1) { + j++; + } + selectorMtf[i] = (char) j; + } + + /* Undo the MTF values for the selectors. */ + { + char[] pos = new char[N_GROUPS]; + char tmp, v; + for (v = 0; v < nGroups; v++) { + pos[v] = v; + } + + for (i = 0; i < nSelectors; i++) { + v = selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { + pos[v] = pos[v - 1]; + v--; + } + pos[0] = tmp; + selector[i] = tmp; + } + } + + /* Now the coding tables */ + for (t = 0; t < nGroups; t++) { + int curr = bsR(5); + for (i = 0; i < alphaSize; i++) { + while (bsR(1) == 1) { + if (bsR(1) == 0) { + curr++; + } else { + curr--; + } + } + len[t][i] = (char) curr; + } + } + + /* Create the Huffman decoding tables */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen, + maxLen, alphaSize); + minLens[t] = minLen; + } + } + + private void getAndMoveToFrontDecode() { + char[] yy = new char[256]; + int i, j, nextSym, limitLast; + int EOB, groupNo, groupPos; + + limitLast = baseBlockSize * blockSize100k; + origPtr = bsGetIntVS(24); + + recvDecodingTables(); + EOB = nInUse + 1; + groupNo = -1; + groupPos = 0; + + /* + Setting up the unzftab entries here is not strictly + necessary, but it does save having to do it later + in a separate pass, and so saves a block's worth of + cache misses. + */ + for (i = 0; i <= 255; i++) { + unzftab[i] = 0; + } + + for (i = 0; i <= 255; i++) { + yy[i] = (char) i; + } + + last = -1; + + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + + while (true) { + + if (nextSym == EOB) { + break; + } + + if (nextSym == RUNA || nextSym == RUNB) { + char ch; + int s = -1; + int N = 1; + do { + if (nextSym == RUNA) { + s = s + (0 + 1) * N; + } else if (nextSym == RUNB) { + s = s + (1 + 1) * N; + } + N = N * 2; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + } while (nextSym == RUNA || nextSym == RUNB); + + s++; + ch = seqToUnseq[yy[0]]; + unzftab[ch] += s; + + while (s > 0) { + last++; + ll8[last] = ch; + s--; + } + + if (last >= limitLast) { + blockOverrun(); + } + continue; + } else { + char tmp; + last++; + if (last >= limitLast) { + blockOverrun(); + } + + tmp = yy[nextSym - 1]; + unzftab[seqToUnseq[tmp]]++; + ll8[last] = seqToUnseq[tmp]; + + /* + This loop is hammered during decompression, + hence the unrolling. + + for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1]; + */ + + j = nextSym - 1; + for (; j > 3; j -= 4) { + yy[j] = yy[j - 1]; + yy[j - 1] = yy[j - 2]; + yy[j - 2] = yy[j - 3]; + yy[j - 3] = yy[j - 4]; + } + for (; j > 0; j--) { + yy[j] = yy[j - 1]; + } + + yy[0] = tmp; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + continue; + } + } + } + + private void setupBlock() { + int[] cftab = new int[257]; + char ch; + + cftab[0] = 0; + for (i = 1; i <= 256; i++) { + cftab[i] = unzftab[i - 1]; + } + for (i = 1; i <= 256; i++) { + cftab[i] += cftab[i - 1]; + } + + for (i = 0; i <= last; i++) { + ch = (char) ll8[i]; + tt[cftab[ch]] = i; + cftab[ch]++; + } + cftab = null; + + tPos = tt[origPtr]; + + count = 0; + i2 = 0; + ch2 = 256; /* not a char and not EOF */ + + if (blockRandomised) { + rNToGo = 0; + rTPos = 0; + setupRandPartA(); + } else { + setupNoRandPartA(); + } + } + + private void setupRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + ch2 ^= (int) ((rNToGo == 1) ? 1 : 0); + i2++; + + currentChar = ch2; + currentState = RAND_PART_B_STATE; + mCrc.updateCRC(ch2); + } else { + endBlock(); + initBlock(); + setupBlock(); + } + } + + private void setupNoRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + i2++; + + currentChar = ch2; + currentState = NO_RAND_PART_B_STATE; + mCrc.updateCRC(ch2); + } else { + endBlock(); + initBlock(); + setupBlock(); + } + } + + private void setupRandPartB() { + if (ch2 != chPrev) { + currentState = RAND_PART_A_STATE; + count = 1; + setupRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + z ^= ((rNToGo == 1) ? 1 : 0); + j2 = 0; + currentState = RAND_PART_C_STATE; + setupRandPartC(); + } else { + currentState = RAND_PART_A_STATE; + setupRandPartA(); + } + } + } + + private void setupRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.updateCRC(ch2); + j2++; + } else { + currentState = RAND_PART_A_STATE; + i2++; + count = 0; + setupRandPartA(); + } + } + + private void setupNoRandPartB() { + if (ch2 != chPrev) { + currentState = NO_RAND_PART_A_STATE; + count = 1; + setupNoRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + currentState = NO_RAND_PART_C_STATE; + j2 = 0; + setupNoRandPartC(); + } else { + currentState = NO_RAND_PART_A_STATE; + setupNoRandPartA(); + } + } + } + + private void setupNoRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.updateCRC(ch2); + j2++; + } else { + currentState = NO_RAND_PART_A_STATE; + i2++; + count = 0; + setupNoRandPartA(); + } + } + + private void setDecompressStructureSizes(int newSize100k) { + if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k + && blockSize100k <= 9)) { + // throw new IOException("Invalid block size"); + } + + blockSize100k = newSize100k; + + if (newSize100k == 0) { + return; + } + + int n = baseBlockSize * newSize100k; + ll8 = new char[n]; + tt = new int[n]; + } +} + diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2OutputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2OutputStream.java new file mode 100644 index 000000000..9648663af --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CBZip2OutputStream.java @@ -0,0 +1,1651 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.spongycastle.apache.bzip2; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * An output stream that compresses into the BZip2 format (with the file + * header chars) into another stream. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * + * TODO: Update to BZip2 1.0.1 + * <b>NB:</b> note this class has been modified to add a leading BZ to the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ +public class CBZip2OutputStream extends OutputStream implements BZip2Constants { + protected static final int SETMASK = (1 << 21); + protected static final int CLEARMASK = (~SETMASK); + protected static final int GREATER_ICOST = 15; + protected static final int LESSER_ICOST = 0; + protected static final int SMALL_THRESH = 20; + protected static final int DEPTH_THRESH = 10; + + /* + If you are ever unlucky/improbable enough + to get a stack overflow whilst sorting, + increase the following constant and try + again. In practice I have never seen the + stack go above 27 elems, so the following + limit seems very generous. + */ + protected static final int QSORT_STACK_SIZE = 1000; + private boolean finished; + + private static void panic() { + System.out.println("panic"); + //throw new CError(); + } + + private void makeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + protected static void hbMakeCodeLengths(char[] len, int[] freq, + int alphaSize, int maxLen) { + /* + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + */ + int nNodes, nHeap, n1, n2, i, j, k; + boolean tooLong; + + int[] heap = new int[MAX_ALPHA_SIZE + 2]; + int[] weight = new int[MAX_ALPHA_SIZE * 2]; + int[] parent = new int[MAX_ALPHA_SIZE * 2]; + + for (i = 0; i < alphaSize; i++) { + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + } + + while (true) { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + { + int zz, tmp; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nHeap < (MAX_ALPHA_SIZE + 2))) { + panic(); + } + + while (nHeap > 1) { + n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + nNodes++; + parent[n1] = parent[n2] = nNodes; + + weight[nNodes] = ((weight[n1] & 0xffffff00) + + (weight[n2] & 0xffffff00)) + | (1 + (((weight[n1] & 0x000000ff) > + (weight[n2] & 0x000000ff)) ? + (weight[n1] & 0x000000ff) : + (weight[n2] & 0x000000ff))); + + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + { + int zz = 0, tmp = 0; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nNodes < (MAX_ALPHA_SIZE * 2))) { + panic(); + } + + tooLong = false; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { + k = parent[k]; + j++; + } + len[i - 1] = (char) j; + if (j > maxLen) { + tooLong = true; + } + } + + if (!tooLong) { + break; + } + + for (i = 1; i < alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + int last; + + /* + index in zptr[] of original string after sorting. + */ + int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + int blockSize100k; + + boolean blockRandomised; + + int bytesOut; + int bsBuff; + int bsLive; + CRC mCrc = new CRC(); + + private boolean[] inUse = new boolean[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[MAX_SELECTORS]; + private char[] selectorMtf = new char[MAX_SELECTORS]; + + private char[] block; + private int[] quadrant; + private int[] zptr; + private short[] szptr; + private int[] ftab; + + private int nMTF; + + private int[] mtfFreq = new int[MAX_ALPHA_SIZE]; + + /* + * Used when sorting. If too many long comparisons + * happen, we stop sorting, randomise the block + * slightly, and try again. + */ + private int workFactor; + private int workDone; + private int workLimit; + private boolean firstAttempt; + private int nBlocksRandomised; + + private int currentChar = -1; + private int runLength = 0; + + public CBZip2OutputStream(OutputStream inStream) throws IOException { + this(inStream, 9); + } + + public CBZip2OutputStream(OutputStream inStream, int inBlockSize) + throws IOException { + block = null; + quadrant = null; + zptr = null; + ftab = null; + + inStream.write('B'); + inStream.write('Z'); + + bsSetStream(inStream); + + workFactor = 50; + if (inBlockSize > 9) { + inBlockSize = 9; + } + if (inBlockSize < 1) { + inBlockSize = 1; + } + blockSize100k = inBlockSize; + allocateCompressStructures(); + initialize(); + initBlock(); + } + + /** + * + * modified by Oliver Merkel, 010128 + * + */ + public void write(int bv) throws IOException { + int b = (256 + bv) % 256; + if (currentChar != -1) { + if (currentChar == b) { + runLength++; + if (runLength > 254) { + writeRun(); + currentChar = -1; + runLength = 0; + } + } else { + writeRun(); + runLength = 1; + currentChar = b; + } + } else { + currentChar = b; + runLength++; + } + } + + private void writeRun() throws IOException { + if (last < allowableBlockSize) { + inUse[currentChar] = true; + for (int i = 0; i < runLength; i++) { + mCrc.updateCRC((char) currentChar); + } + switch (runLength) { + case 1: + last++; + block[last + 1] = (char) currentChar; + break; + case 2: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + case 3: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + default: + inUse[runLength - 4] = true; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) (runLength - 4); + break; + } + } else { + endBlock(); + initBlock(); + writeRun(); + } + } + + boolean closed = false; + + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + public void close() throws IOException { + if (closed) { + return; + } + + finish(); + + closed = true; + super.close(); + bsStream.close(); + } + + public void finish() throws IOException { + if (finished) { + return; + } + + if (runLength > 0) { + writeRun(); + } + currentChar = -1; + endBlock(); + endCompression(); + finished = true; + flush(); + } + + public void flush() throws IOException { + super.flush(); + bsStream.flush(); + } + + private int blockCRC, combinedCRC; + + private void initialize() throws IOException { + bytesOut = 0; + nBlocksRandomised = 0; + + /* Write `magic' bytes h indicating file-format == huffmanised, + followed by a digit indicating blockSize100k. + */ + bsPutUChar('h'); + bsPutUChar('0' + blockSize100k); + + combinedCRC = 0; + } + + private int allowableBlockSize; + + private void initBlock() { + // blockNo++; + mCrc.initialiseCRC(); + last = -1; + // ch = 0; + + for (int i = 0; i < 256; i++) { + inUse[i] = false; + } + + /* 20 is just a paranoia constant */ + allowableBlockSize = baseBlockSize * blockSize100k - 20; + } + + private void endBlock() throws IOException { + blockCRC = mCrc.getFinalCRC(); + combinedCRC = (combinedCRC << 1) | (combinedCRC >>> 31); + combinedCRC ^= blockCRC; + + /* sort the block and establish posn of original string */ + doReversibleTransformation(); + + /* + A 6-byte block header, the value chosen arbitrarily + as 0x314159265359 :-). A 32 bit value does not really + give a strong enough guarantee that the value will not + appear by chance in the compressed datastream. Worst-case + probability of this event, for a 900k block, is about + 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. + For a compressed file of size 100Gb -- about 100000 blocks -- + only a 48-bit marker will do. NB: normal compression/ + decompression do *not* rely on these statistical properties. + They are only important when trying to recover blocks from + damaged files. + */ + bsPutUChar(0x31); + bsPutUChar(0x41); + bsPutUChar(0x59); + bsPutUChar(0x26); + bsPutUChar(0x53); + bsPutUChar(0x59); + + /* Now the block's CRC, so it is in a known place. */ + bsPutint(blockCRC); + + /* Now a single bit indicating randomisation. */ + if (blockRandomised) { + bsW(1, 1); + nBlocksRandomised++; + } else { + bsW(1, 0); + } + + /* Finally, block's contents proper. */ + moveToFrontCodeAndSend(); + } + + private void endCompression() throws IOException { + /* + Now another magic 48-bit number, 0x177245385090, to + indicate the end of the last block. (sqrt(pi), if + you want to know. I did want to use e, but it contains + too much repetition -- 27 18 28 18 28 46 -- for me + to feel statistically comfortable. Call me paranoid.) + */ + bsPutUChar(0x17); + bsPutUChar(0x72); + bsPutUChar(0x45); + bsPutUChar(0x38); + bsPutUChar(0x50); + bsPutUChar(0x90); + + bsPutint(combinedCRC); + + bsFinishedWithStream(); + } + + private void hbAssignCodes (int[] code, char[] length, int minLen, + int maxLen, int alphaSize) { + int n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) { + if (length[i] == n) { + code[i] = vec; + vec++; + } + } + vec <<= 1; + } + } + + private void bsSetStream(OutputStream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + bytesOut = 0; + } + + private void bsFinishedWithStream() throws IOException { + while (bsLive > 0) { + int ch = (bsBuff >> 24); + try { + bsStream.write(ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + } + + private void bsW(int n, int v) throws IOException { + while (bsLive >= 8) { + int ch = (bsBuff >> 24); + try { + bsStream.write(ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + bsBuff |= (v << (32 - bsLive - n)); + bsLive += n; + } + + private void bsPutUChar(int c) throws IOException { + bsW(8, c); + } + + private void bsPutint(int u) throws IOException { + bsW(8, (u >> 24) & 0xff); + bsW(8, (u >> 16) & 0xff); + bsW(8, (u >> 8) & 0xff); + bsW(8, u & 0xff); + } + + private void bsPutIntVS(int numBits, int c) throws IOException { + bsW(numBits, c); + } + + private void sendMTFValues() throws IOException { + char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE]; + + int v, t, i, j, gs, ge, totc, bt, bc, iter; + int nSelectors = 0, alphaSize, minLen, maxLen, selCtr; + int nGroups;//, nBytes; + + alphaSize = nInUse + 2; + for (t = 0; t < N_GROUPS; t++) { + for (v = 0; v < alphaSize; v++) { + len[t][v] = (char) GREATER_ICOST; + } + } + + /* Decide how many coding tables to use */ + if (nMTF <= 0) { + panic(); + } + + if (nMTF < 200) { + nGroups = 2; + } else if (nMTF < 600) { + nGroups = 3; + } else if (nMTF < 1200) { + nGroups = 4; + } else if (nMTF < 2400) { + nGroups = 5; + } else { + nGroups = 6; + } + + /* Generate an initial set of coding tables */ { + int nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs - 1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize - 1) { + ge++; + aFreq += mtfFreq[ge]; + } + + if (ge > gs && nPart != nGroups && nPart != 1 + && ((nGroups - nPart) % 2 == 1)) { + aFreq -= mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) { + if (v >= gs && v <= ge) { + len[nPart - 1][v] = (char) LESSER_ICOST; + } else { + len[nPart - 1][v] = (char) GREATER_ICOST; + } + } + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + } + + int[][] rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE]; + int[] fave = new int[N_GROUPS]; + short[] cost = new short[N_GROUPS]; + /* + Iterate up to N_ITERS times to improve the tables. + */ + for (iter = 0; iter < N_ITERS; iter++) { + for (t = 0; t < nGroups; t++) { + fave[t] = 0; + } + + for (t = 0; t < nGroups; t++) { + for (v = 0; v < alphaSize; v++) { + rfreq[t][v] = 0; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (true) { + + /* Set group start & end marks. */ + if (gs >= nMTF) { + break; + } + ge = gs + G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + + /* + Calculate the cost of this group as coded + by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) { + cost[t] = 0; + } + + if (nGroups == 6) { + short cost0, cost1, cost2, cost3, cost4, cost5; + cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + cost0 += len[0][icv]; + cost1 += len[1][icv]; + cost2 += len[2][icv]; + cost3 += len[3][icv]; + cost4 += len[4][icv]; + cost5 += len[5][icv]; + } + cost[0] = cost0; + cost[1] = cost1; + cost[2] = cost2; + cost[3] = cost3; + cost[4] = cost4; + cost[5] = cost5; + } else { + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + for (t = 0; t < nGroups; t++) { + cost[t] += len[t][icv]; + } + } + } + + /* + Find the coding table which is best for this group, + and record its identity in the selector table. + */ + bc = 999999999; + bt = -1; + for (t = 0; t < nGroups; t++) { + if (cost[t] < bc) { + bc = cost[t]; + bt = t; + } + } + totc += bc; + fave[bt]++; + selector[nSelectors] = (char) bt; + nSelectors++; + + /* + Increment the symbol frequencies for the selected table. + */ + for (i = gs; i <= ge; i++) { + rfreq[bt][szptr[i]]++; + } + + gs = ge + 1; + } + + /* + Recompute the tables based on the accumulated frequencies. + */ + for (t = 0; t < nGroups; t++) { + hbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); + } + } + + rfreq = null; + fave = null; + cost = null; + + if (!(nGroups < 8)) { + panic(); + } + if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / G_SIZE)))) { + panic(); + } + + + /* Compute MTF values for the selectors. */ + { + char[] pos = new char[N_GROUPS]; + char ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) { + pos[i] = (char) i; + } + for (i = 0; i < nSelectors; i++) { + ll_i = selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + selectorMtf[i] = (char) j; + } + } + + int[][] code = new int[N_GROUPS][MAX_ALPHA_SIZE]; + + /* Assign actual codes for the tables. */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + if (maxLen > 20) { + panic(); + } + if (minLen < 1) { + panic(); + } + hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); + } + + /* Transmit the mapping table. */ + { + boolean[] inUse16 = new boolean[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = false; + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + inUse16[i] = true; + } + } + } + +// nBytes = bytesOut; + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + bsW(1, 1); + } else { + bsW(1, 0); + } + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + bsW(1, 1); + } else { + bsW(1, 0); + } + } + } + } + + } + + /* Now the selectors. */ +// nBytes = bytesOut; + bsW (3, nGroups); + bsW (15, nSelectors); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < selectorMtf[i]; j++) { + bsW(1, 1); + } + bsW(1, 0); + } + + /* Now the coding tables. */ +// nBytes = bytesOut; + + for (t = 0; t < nGroups; t++) { + int curr = len[t][0]; + bsW(5, curr); + for (i = 0; i < alphaSize; i++) { + while (curr < len[t][i]) { + bsW(2, 2); + curr++; /* 10 */ + } + while (curr > len[t][i]) { + bsW(2, 3); + curr--; /* 11 */ + } + bsW (1, 0); + } + } + + /* And finally, the block data proper */ +// nBytes = bytesOut; + selCtr = 0; + gs = 0; + while (true) { + if (gs >= nMTF) { + break; + } + ge = gs + G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + for (i = gs; i <= ge; i++) { + bsW(len[selector[selCtr]][szptr[i]], + code[selector[selCtr]][szptr[i]]); + } + + gs = ge + 1; + selCtr++; + } + if (!(selCtr == nSelectors)) { + panic(); + } + } + + private void moveToFrontCodeAndSend () throws IOException { + bsPutIntVS(24, origPtr); + generateMTFValues(); + sendMTFValues(); + } + + private OutputStream bsStream; + + private void simpleSort(int lo, int hi, int d) { + int i, j, h, bigN, hp; + int v; + + bigN = hi - lo + 1; + if (bigN < 2) { + return; + } + + hp = 0; + while (incs[hp] < bigN) { + hp++; + } + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (true) { + /* copy 1 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 2 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 3 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + if (workDone > workLimit && firstAttempt) { + return; + } + } + } + } + + private void vswap(int p1, int p2, int n) { + int temp = 0; + while (n > 0) { + temp = zptr[p1]; + zptr[p1] = zptr[p2]; + zptr[p2] = temp; + p1++; + p2++; + n--; + } + } + + private char med3(char a, char b, char c) { + char t; + if (a > b) { + t = a; + a = b; + b = t; + } + if (b > c) { + t = b; + b = c; + c = t; + } + if (a > b) { + b = a; + } + return b; + } + + private static class StackElem { + int ll; + int hh; + int dd; + } + + private void qSort3(int loSt, int hiSt, int dSt) { + int unLo, unHi, ltLo, gtHi, med, n, m; + int sp, lo, hi, d; + StackElem[] stack = new StackElem[QSORT_STACK_SIZE]; + for (int count = 0; count < QSORT_STACK_SIZE; count++) { + stack[count] = new StackElem(); + } + + sp = 0; + + stack[sp].ll = loSt; + stack[sp].hh = hiSt; + stack[sp].dd = dSt; + sp++; + + while (sp > 0) { + if (sp >= QSORT_STACK_SIZE) { + panic(); + } + + sp--; + lo = stack[sp].ll; + hi = stack[sp].hh; + d = stack[sp].dd; + + if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) { + simpleSort(lo, hi, d); + if (workDone > workLimit && firstAttempt) { + return; + } + continue; + } + + med = med3(block[zptr[lo] + d + 1], + block[zptr[hi ] + d + 1], + block[zptr[(lo + hi) >> 1] + d + 1]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (true) { + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unLo] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[ltLo]; + zptr[ltLo] = temp; + ltLo++; + unLo++; + continue; + } + if (n > 0) { + break; + } + unLo++; + } + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unHi] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unHi]; + zptr[unHi] = zptr[gtHi]; + zptr[gtHi] = temp; + gtHi--; + unHi--; + continue; + } + if (n < 0) { + break; + } + unHi--; + } + if (unLo > unHi) { + break; + } + int temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[unHi]; + zptr[unHi] = temp; + unLo++; + unHi--; + } + + if (gtHi < ltLo) { + stack[sp].ll = lo; + stack[sp].hh = hi; + stack[sp].dd = d + 1; + sp++; + continue; + } + + n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); + vswap(lo, unLo - n, n); + m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); + vswap(unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack[sp].ll = lo; + stack[sp].hh = n; + stack[sp].dd = d; + sp++; + + stack[sp].ll = n + 1; + stack[sp].hh = m - 1; + stack[sp].dd = d + 1; + sp++; + + stack[sp].ll = m; + stack[sp].hh = hi; + stack[sp].dd = d; + sp++; + } + } + + private void mainSort() { + int i, j, ss, sb; + int[] runningOrder = new int[256]; + int[] copy = new int[256]; + boolean[] bigDone = new boolean[256]; + int c1, c2; + int numQSorted; + + /* + In the various block-sized structures, live data runs + from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, + set up the overshoot area for block. + */ + + // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); + for (i = 0; i < NUM_OVERSHOOT_BYTES; i++) { + block[last + i + 2] = block[(i % (last + 1)) + 1]; + } + for (i = 0; i <= last + NUM_OVERSHOOT_BYTES; i++) { + quadrant[i] = 0; + } + + block[0] = (char) (block[last + 1]); + + if (last < 4000) { + /* + Use simpleSort(), since the full sorting mechanism + has quite a large constant overhead. + */ + for (i = 0; i <= last; i++) { + zptr[i] = i; + } + firstAttempt = false; + workDone = workLimit = 0; + simpleSort(0, last, 0); + } else { + numQSorted = 0; + for (i = 0; i <= 255; i++) { + bigDone[i] = false; + } + + for (i = 0; i <= 65536; i++) { + ftab[i] = 0; + } + + c1 = block[0]; + for (i = 0; i <= last; i++) { + c2 = block[i + 1]; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } + + for (i = 1; i <= 65536; i++) { + ftab[i] += ftab[i - 1]; + } + + c1 = block[1]; + for (i = 0; i < last; i++) { + c2 = block[i + 2]; + j = (c1 << 8) + c2; + c1 = c2; + ftab[j]--; + zptr[ftab[j]] = i; + } + + j = ((block[last + 1]) << 8) + (block[1]); + ftab[j]--; + zptr[ftab[j]] = last; + + /* + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + */ + + for (i = 0; i <= 255; i++) { + runningOrder[i] = i; + } + + { + int vv; + int h = 1; + do { + h = 3 * h + 1; + } + while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ((ftab[((runningOrder[j - h]) + 1) << 8] + - ftab[(runningOrder[j - h]) << 8]) > + (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) { + runningOrder[j] = runningOrder[j - h]; + j = j - h; + if (j <= (h - 1)) { + break; + } + } + runningOrder[j] = vv; + } + } while (h != 1); + } + + /* + The main sorting loop. + */ + for (i = 0; i <= 255; i++) { + + /* + Process big buckets, starting with the least full. + */ + ss = runningOrder[i]; + + /* + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j]. Hopefully + previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { + sb = (ss << 8) + j; + if (!((ftab[sb] & SETMASK) == SETMASK)) { + int lo = ftab[sb] & CLEARMASK; + int hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) { + qSort3(lo, hi, 2); + numQSorted += (hi - lo + 1); + if (workDone > workLimit && firstAttempt) { + return; + } + } + ftab[sb] |= SETMASK; + } + } + + /* + The ss big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + */ + bigDone[ss] = true; + + if (i < 255) { + int bbStart = ftab[ss << 8] & CLEARMASK; + int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + int shifts = 0; + + while ((bbSize >> shifts) > 65534) { + shifts++; + } + + for (j = 0; j < bbSize; j++) { + int a2update = zptr[bbStart + j]; + int qVal = (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < NUM_OVERSHOOT_BYTES) { + quadrant[a2update + last + 1] = qVal; + } + } + + if (!(((bbSize - 1) >> shifts) <= 65535)) { + panic(); + } + } + + /* + Now scan this big bucket so as to synthesise the + sorted order for small buckets [t, ss] for all t != ss. + */ + for (j = 0; j <= 255; j++) { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (j = ftab[ss << 8] & CLEARMASK; + j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) { + c1 = block[zptr[j]]; + if (!bigDone[c1]) { + zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; + copy[c1]++; + } + } + + for (j = 0; j <= 255; j++) { + ftab[(j << 8) + ss] |= SETMASK; + } + } + } + } + + private void randomiseBlock() { + int i; + int rNToGo = 0; + int rTPos = 0; + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i <= last; i++) { + if (rNToGo == 0) { + rNToGo = (char) rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + block[i + 1] ^= ((rNToGo == 1) ? 1 : 0); + // handle 16 bit signed numbers + block[i + 1] &= 0xFF; + + inUse[block[i + 1]] = true; + } + } + + private void doReversibleTransformation() { + int i; + + workLimit = workFactor * last; + workDone = 0; + blockRandomised = false; + firstAttempt = true; + + mainSort(); + + if (workDone > workLimit && firstAttempt) { + randomiseBlock(); + workLimit = workDone = 0; + blockRandomised = true; + firstAttempt = false; + mainSort(); + } + + origPtr = -1; + for (i = 0; i <= last; i++) { + if (zptr[i] == 0) { + origPtr = i; + break; + } + } + + if (origPtr == -1) { + panic(); + } + } + + private boolean fullGtU(int i1, int i2) { + int k; + char c1, c2; + int s1, s2; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + k = last + 1; + + do { + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + if (i1 > last) { + i1 -= last; + i1--; + } + if (i2 > last) { + i2 -= last; + i2--; + } + + k -= 4; + workDone++; + } while (k >= 0); + + return false; + } + + /* + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. + */ + private int[] incs = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + + private void allocateCompressStructures () { + int n = baseBlockSize * blockSize100k; + block = new char[(n + 1 + NUM_OVERSHOOT_BYTES)]; + quadrant = new int[(n + NUM_OVERSHOOT_BYTES)]; + zptr = new int[n]; + ftab = new int[65537]; + + if (block == null || quadrant == null || zptr == null + || ftab == null) { + //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; + //compressOutOfMemory ( totalDraw, n ); + } + + /* + The back end needs a place to store the MTF values + whilst it calculates the coding tables. We could + put them in the zptr array. However, these values + will fit in a short, so we overlay szptr at the + start of zptr, in the hope of reducing the number + of cache misses induced by the multiple traversals + of the MTF values when calculating coding tables. + Seems to improve compression speed by about 1%. + */ + // szptr = zptr; + + + szptr = new short[2 * n]; + } + + private void generateMTFValues() { + char[] yy = new char[256]; + int i, j; + char tmp; + char tmp2; + int zPend; + int wr; + int EOB; + + makeMaps(); + EOB = nInUse + 1; + + for (i = 0; i <= EOB; i++) { + mtfFreq[i] = 0; + } + + wr = 0; + zPend = 0; + for (i = 0; i < nInUse; i++) { + yy[i] = (char) i; + } + + + for (i = 0; i <= last; i++) { + char ll_i; + + ll_i = unseqToSeq[block[zptr[i]]]; + + j = 0; + tmp = yy[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + } + yy[0] = tmp; + + if (j == 0) { + zPend++; + } else { + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) RUNA; + wr++; + mtfFreq[RUNA]++; + break; + case 1: + szptr[wr] = (short) RUNB; + wr++; + mtfFreq[RUNB]++; + break; + } + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + } + zPend = 0; + } + szptr[wr] = (short) (j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) RUNA; + wr++; + mtfFreq[RUNA]++; + break; + case 1: + szptr[wr] = (short) RUNB; + wr++; + mtfFreq[RUNB]++; + break; + } + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + } + } + + szptr[wr] = (short) EOB; + wr++; + mtfFreq[EOB]++; + + nMTF = wr; + } +} + + diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CRC.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CRC.java new file mode 100644 index 000000000..acb2e80ee --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/apache/bzip2/CRC.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.spongycastle.apache.bzip2; + +/** + * A simple class the hold and calculate the CRC for sanity checking + * of the data. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +class CRC { + public static int crc32Table[] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + public CRC() { + initialiseCRC(); + } + + void initialiseCRC() { + globalCrc = 0xffffffff; + } + + int getFinalCRC() { + return ~globalCrc; + } + + int getGlobalCRC() { + return globalCrc; + } + + void setGlobalCRC(int newCrc) { + globalCrc = newCrc; + } + + void updateCRC(int inCh) { + int temp = (globalCrc >> 24) ^ inCh; + if (temp < 0) { + temp = 256 + temp; + } + globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp]; + } + + int globalCrc; +} + diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredInputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredInputStream.java new file mode 100644 index 000000000..2a0f9e3ee --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredInputStream.java @@ -0,0 +1,473 @@ +package org.spongycastle.bcpg; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +/** + * reader for Base64 armored objects - read the headers and then start returning + * bytes when the data is reached. An IOException is thrown if the CRC check + * fails. + */ +public class ArmoredInputStream + extends InputStream +{ + /* + * set up the decoding table. + */ + private static final byte[] decodingTable; + + static + { + decodingTable = new byte[128]; + + for (int i = 'A'; i <= 'Z'; i++) + { + decodingTable[i] = (byte)(i - 'A'); + } + + for (int i = 'a'; i <= 'z'; i++) + { + decodingTable[i] = (byte)(i - 'a' + 26); + } + + for (int i = '0'; i <= '9'; i++) + { + decodingTable[i] = (byte)(i - '0' + 52); + } + + decodingTable['+'] = 62; + decodingTable['/'] = 63; + } + + /** + * decode the base 64 encoded input data. + * + * @return the offset the data starts in out. + */ + private int decode( + int in0, + int in1, + int in2, + int in3, + int[] out) + throws EOFException + { + int b1, b2, b3, b4; + + if (in3 < 0) + { + throw new EOFException("unexpected end of file in armored stream."); + } + + if (in2 == '=') + { + b1 = decodingTable[in0] &0xff; + b2 = decodingTable[in1] & 0xff; + + out[2] = ((b1 << 2) | (b2 >> 4)) & 0xff; + + return 2; + } + else if (in3 == '=') + { + b1 = decodingTable[in0]; + b2 = decodingTable[in1]; + b3 = decodingTable[in2]; + + out[1] = ((b1 << 2) | (b2 >> 4)) & 0xff; + out[2] = ((b2 << 4) | (b3 >> 2)) & 0xff; + + return 1; + } + else + { + b1 = decodingTable[in0]; + b2 = decodingTable[in1]; + b3 = decodingTable[in2]; + b4 = decodingTable[in3]; + + out[0] = ((b1 << 2) | (b2 >> 4)) & 0xff; + out[1] = ((b2 << 4) | (b3 >> 2)) & 0xff; + out[2] = ((b3 << 6) | b4) & 0xff; + + return 0; + } + } + + InputStream in; + boolean start = true; + int[] outBuf = new int[3]; + int bufPtr = 3; + CRC24 crc = new CRC24(); + boolean crcFound = false; + boolean hasHeaders = true; + String header = null; + boolean newLineFound = false; + boolean clearText = false; + boolean restart = false; + Vector headerList= new Vector(); + int lastC = 0; + boolean isEndOfStream; + + /** + * Create a stream for reading a PGP armoured message, parsing up to a header + * and then reading the data that follows. + * + * @param in + */ + public ArmoredInputStream( + InputStream in) + throws IOException + { + this(in, true); + } + + /** + * Create an armoured input stream which will assume the data starts + * straight away, or parse for headers first depending on the value of + * hasHeaders. + * + * @param in + * @param hasHeaders true if headers are to be looked for, false otherwise. + */ + public ArmoredInputStream( + InputStream in, + boolean hasHeaders) + throws IOException + { + this.in = in; + this.hasHeaders = hasHeaders; + + if (hasHeaders) + { + parseHeaders(); + } + + start = false; + } + + public int available() + throws IOException + { + return in.available(); + } + + private boolean parseHeaders() + throws IOException + { + header = null; + + int c; + int last = 0; + boolean headerFound = false; + + headerList = new Vector(); + + // + // if restart we already have a header + // + if (restart) + { + headerFound = true; + } + else + { + while ((c = in.read()) >= 0) + { + if (c == '-' && (last == 0 || last == '\n' || last == '\r')) + { + headerFound = true; + break; + } + + last = c; + } + } + + if (headerFound) + { + StringBuffer buf = new StringBuffer("-"); + boolean eolReached = false; + boolean crLf = false; + + if (restart) // we've had to look ahead two '-' + { + buf.append('-'); + } + + while ((c = in.read()) >= 0) + { + if (last == '\r' && c == '\n') + { + crLf = true; + } + if (eolReached && (last != '\r' && c == '\n')) + { + break; + } + if (eolReached && c == '\r') + { + break; + } + if (c == '\r' || (last != '\r' && c == '\n')) + { + String line = buf.toString(); + if (line.trim().length() == 0) + { + break; + } + headerList.addElement(line); + buf.setLength(0); + } + + if (c != '\n' && c != '\r') + { + buf.append((char)c); + eolReached = false; + } + else + { + if (c == '\r' || (last != '\r' && c == '\n')) + { + eolReached = true; + } + } + + last = c; + } + + if (crLf) + { + in.read(); // skip last \n + } + } + + if (headerList.size() > 0) + { + header = (String)headerList.elementAt(0); + } + + clearText = "-----BEGIN PGP SIGNED MESSAGE-----".equals(header); + newLineFound = true; + + return headerFound; + } + + /** + * @return true if we are inside the clear text section of a PGP + * signed message. + */ + public boolean isClearText() + { + return clearText; + } + + /** + * @return true if the stream is actually at end of file. + */ + public boolean isEndOfStream() + { + return isEndOfStream; + } + + /** + * Return the armor header line (if there is one) + * @return the armor header line, null if none present. + */ + public String getArmorHeaderLine() + { + return header; + } + + /** + * Return the armor headers (the lines after the armor header line), + * @return an array of armor headers, null if there aren't any. + */ + public String[] getArmorHeaders() + { + if (headerList.size() <= 1) + { + return null; + } + + String[] hdrs = new String[headerList.size() - 1]; + + for (int i = 0; i != hdrs.length; i++) + { + hdrs[i] = (String)headerList.elementAt(i + 1); + } + + return hdrs; + } + + private int readIgnoreSpace() + throws IOException + { + int c = in.read(); + + while (c == ' ' || c == '\t') + { + c = in.read(); + } + + return c; + } + + public int read() + throws IOException + { + int c; + + if (start) + { + if (hasHeaders) + { + parseHeaders(); + } + + crc.reset(); + start = false; + } + + if (clearText) + { + c = in.read(); + + if (c == '\r' || (c == '\n' && lastC != '\r')) + { + newLineFound = true; + } + else if (newLineFound && c == '-') + { + c = in.read(); + if (c == '-') // a header, not dash escaped + { + clearText = false; + start = true; + restart = true; + } + else // a space - must be a dash escape + { + c = in.read(); + } + newLineFound = false; + } + else + { + if (c != '\n' && lastC != '\r') + { + newLineFound = false; + } + } + + lastC = c; + + if (c < 0) + { + isEndOfStream = true; + } + + return c; + } + + if (bufPtr > 2 || crcFound) + { + c = readIgnoreSpace(); + + if (c == '\r' || c == '\n') + { + c = readIgnoreSpace(); + + while (c == '\n' || c == '\r') + { + c = readIgnoreSpace(); + } + + if (c < 0) // EOF + { + isEndOfStream = true; + return -1; + } + + if (c == '=') // crc reached + { + bufPtr = decode(readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf); + if (bufPtr == 0) + { + int i = ((outBuf[0] & 0xff) << 16) + | ((outBuf[1] & 0xff) << 8) + | (outBuf[2] & 0xff); + + crcFound = true; + + if (i != crc.getValue()) + { + throw new IOException("crc check failed in armored message."); + } + return read(); + } + else + { + throw new IOException("no crc found in armored message."); + } + } + else if (c == '-') // end of record reached + { + while ((c = in.read()) >= 0) + { + if (c == '\n' || c == '\r') + { + break; + } + } + + if (!crcFound) + { + throw new IOException("crc check not found."); + } + + crcFound = false; + start = true; + bufPtr = 3; + + if (c < 0) + { + isEndOfStream = true; + } + + return -1; + } + else // data + { + bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf); + } + } + else + { + if (c >= 0) + { + bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf); + } + else + { + isEndOfStream = true; + return -1; + } + } + } + + c = outBuf[bufPtr++]; + + crc.update(c); + + return c; + } + + public void close() + throws IOException + { + in.close(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredOutputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredOutputStream.java new file mode 100644 index 000000000..02f46efec --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ArmoredOutputStream.java @@ -0,0 +1,411 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * Basic output stream. + */ +public class ArmoredOutputStream + extends OutputStream +{ + private static final byte[] encodingTable = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', + (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', + (byte)'7', (byte)'8', (byte)'9', + (byte)'+', (byte)'/' + }; + + /** + * encode the input data producing a base 64 encoded byte array. + */ + private void encode( + OutputStream out, + int[] data, + int len) + throws IOException + { + int d1, d2, d3; + + switch (len) + { + case 0: /* nothing left to do */ + break; + case 1: + d1 = data[0]; + + out.write(encodingTable[(d1 >>> 2) & 0x3f]); + out.write(encodingTable[(d1 << 4) & 0x3f]); + out.write('='); + out.write('='); + break; + case 2: + d1 = data[0]; + d2 = data[1]; + + out.write(encodingTable[(d1 >>> 2) & 0x3f]); + out.write(encodingTable[((d1 << 4) | (d2 >>> 4)) & 0x3f]); + out.write(encodingTable[(d2 << 2) & 0x3f]); + out.write('='); + break; + case 3: + d1 = data[0]; + d2 = data[1]; + d3 = data[2]; + + out.write(encodingTable[(d1 >>> 2) & 0x3f]); + out.write(encodingTable[((d1 << 4) | (d2 >>> 4)) & 0x3f]); + out.write(encodingTable[((d2 << 2) | (d3 >>> 6)) & 0x3f]); + out.write(encodingTable[d3 & 0x3f]); + break; + default: + throw new IOException("unknown length in encode"); + } + } + + OutputStream out; + int[] buf = new int[3]; + int bufPtr = 0; + CRC24 crc = new CRC24(); + int chunkCount = 0; + int lastb; + + boolean start = true; + boolean clearText = false; + boolean newLine = false; + + String nl = System.getProperty("line.separator"); + + String type; + String headerStart = "-----BEGIN PGP "; + String headerTail = "-----"; + String footerStart = "-----END PGP "; + String footerTail = "-----"; + + String version = "BCPG v@RELEASE_NAME@"; + + Hashtable headers = new Hashtable(); + + public ArmoredOutputStream( + OutputStream out) + { + this.out = out; + + if (nl == null) + { + nl = "\r\n"; + } + + resetHeaders(); + } + + public ArmoredOutputStream( + OutputStream out, + Hashtable headers) + { + this(out); + + Enumeration e = headers.keys(); + + while (e.hasMoreElements()) + { + Object key = e.nextElement(); + + this.headers.put(key, headers.get(key)); + } + } + + /** + * Set an additional header entry. + * + * @param name the name of the header entry. + * @param value the value of the header entry. + */ + public void setHeader( + String name, + String value) + { + this.headers.put(name, value); + } + + /** + * Reset the headers to only contain a Version string. + */ + public void resetHeaders() + { + headers.clear(); + headers.put("Version", version); + } + + /** + * Start a clear text signed message. + * @param hashAlgorithm + */ + public void beginClearText( + int hashAlgorithm) + throws IOException + { + String hash; + + switch (hashAlgorithm) + { + case HashAlgorithmTags.SHA1: + hash = "SHA1"; + break; + case HashAlgorithmTags.SHA256: + hash = "SHA256"; + break; + case HashAlgorithmTags.SHA384: + hash = "SHA384"; + break; + case HashAlgorithmTags.SHA512: + hash = "SHA512"; + break; + case HashAlgorithmTags.MD2: + hash = "MD2"; + break; + case HashAlgorithmTags.MD5: + hash = "MD5"; + break; + case HashAlgorithmTags.RIPEMD160: + hash = "RIPEMD160"; + break; + default: + throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm); + } + + String armorHdr = "-----BEGIN PGP SIGNED MESSAGE-----" + nl; + String hdrs = "Hash: " + hash + nl + nl; + + for (int i = 0; i != armorHdr.length(); i++) + { + out.write(armorHdr.charAt(i)); + } + + for (int i = 0; i != hdrs.length(); i++) + { + out.write(hdrs.charAt(i)); + } + + clearText = true; + newLine = true; + lastb = 0; + } + + public void endClearText() + { + clearText = false; + } + + private void writeHeaderEntry( + String name, + String value) + throws IOException + { + for (int i = 0; i != name.length(); i++) + { + out.write(name.charAt(i)); + } + + out.write(':'); + out.write(' '); + + for (int i = 0; i != value.length(); i++) + { + out.write(value.charAt(i)); + } + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + } + + public void write( + int b) + throws IOException + { + if (clearText) + { + out.write(b); + + if (newLine) + { + if (!(b == '\n' && lastb == '\r')) + { + newLine = false; + } + if (b == '-') + { + out.write(' '); + out.write('-'); // dash escape + } + } + if (b == '\r' || (b == '\n' && lastb != '\r')) + { + newLine = true; + } + lastb = b; + return; + } + + if (start) + { + boolean newPacket = (b & 0x40) != 0; + int tag = 0; + + if (newPacket) + { + tag = b & 0x3f; + } + else + { + tag = (b & 0x3f) >> 2; + } + + switch (tag) + { + case PacketTags.PUBLIC_KEY: + type = "PUBLIC KEY BLOCK"; + break; + case PacketTags.SECRET_KEY: + type = "PRIVATE KEY BLOCK"; + break; + case PacketTags.SIGNATURE: + type = "SIGNATURE"; + break; + default: + type = "MESSAGE"; + } + + for (int i = 0; i != headerStart.length(); i++) + { + out.write(headerStart.charAt(i)); + } + + for (int i = 0; i != type.length(); i++) + { + out.write(type.charAt(i)); + } + + for (int i = 0; i != headerTail.length(); i++) + { + out.write(headerTail.charAt(i)); + } + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + + writeHeaderEntry("Version", (String)headers.get("Version")); + + Enumeration e = headers.keys(); + while (e.hasMoreElements()) + { + String key = (String)e.nextElement(); + + if (!key.equals("Version")) + { + writeHeaderEntry(key, (String)headers.get(key)); + } + } + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + + start = false; + } + + if (bufPtr == 3) + { + encode(out, buf, bufPtr); + bufPtr = 0; + if ((++chunkCount & 0xf) == 0) + { + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + } + } + + crc.update(b); + buf[bufPtr++] = b & 0xff; + } + + public void flush() + throws IOException + { + } + + /** + * <b>Note</b>: close does nor close the underlying stream. So it is possible to write + * multiple objects using armoring to a single stream. + */ + public void close() + throws IOException + { + if (type != null) + { + encode(out, buf, bufPtr); + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + out.write('='); + + int crcV = crc.getValue(); + + buf[0] = ((crcV >> 16) & 0xff); + buf[1] = ((crcV >> 8) & 0xff); + buf[2] = (crcV & 0xff); + + encode(out, buf, 3); + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + + for (int i = 0; i != footerStart.length(); i++) + { + out.write(footerStart.charAt(i)); + } + + for (int i = 0; i != type.length(); i++) + { + out.write(type.charAt(i)); + } + + for (int i = 0; i != footerTail.length(); i++) + { + out.write(footerTail.charAt(i)); + } + + for (int i = 0; i != nl.length(); i++) + { + out.write(nl.charAt(i)); + } + + out.flush(); + + type = null; + start = true; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGInputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGInputStream.java new file mode 100644 index 000000000..77b0a364a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGInputStream.java @@ -0,0 +1,391 @@ +package org.spongycastle.bcpg; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.util.io.Streams; + +/** + * reader for PGP objects + */ +public class BCPGInputStream + extends InputStream implements PacketTags +{ + InputStream in; + boolean next = false; + int nextB; + + public BCPGInputStream( + InputStream in) + { + this.in = in; + } + + public int available() + throws IOException + { + return in.available(); + } + + public int read() + throws IOException + { + if (next) + { + next = false; + + return nextB; + } + else + { + return in.read(); + } + } + + public int read( + byte[] buf, + int off, + int len) + throws IOException + { + if (len == 0) + { + return 0; + } + + if (!next) + { + return in.read(buf, off, len); + } + + // We have next byte waiting, so return it + + if (nextB < 0) + { + return -1; // EOF + } + + buf[off] = (byte)nextB; // May throw NullPointerException... + next = false; // ...so only set this afterwards + + return 1; + } + + public void readFully( + byte[] buf, + int off, + int len) + throws IOException + { + if (Streams.readFully(this, buf, off, len) < len) + { + throw new EOFException(); + } + } + + public byte[] readAll() + throws IOException + { + return Streams.readAll(this); + } + + public void readFully( + byte[] buf) + throws IOException + { + readFully(buf, 0, buf.length); + } + + /** + * returns the next packet tag in the stream. + * + * @return the tag number. + * + * @throws IOException + */ + public int nextPacketTag() + throws IOException + { + if (!next) + { + try + { + nextB = in.read(); + } + catch (EOFException e) + { + nextB = -1; + } + } + + next = true; + + if (nextB >= 0) + { + if ((nextB & 0x40) != 0) // new + { + return (nextB & 0x3f); + } + else // old + { + return ((nextB & 0x3f) >> 2); + } + } + + return nextB; + } + + public Packet readPacket() + throws IOException + { + int hdr = this.read(); + + if (hdr < 0) + { + return null; + } + + if ((hdr & 0x80) == 0) + { + throw new IOException("invalid header encountered"); + } + + boolean newPacket = (hdr & 0x40) != 0; + int tag = 0; + int bodyLen = 0; + boolean partial = false; + + if (newPacket) + { + tag = hdr & 0x3f; + + int l = this.read(); + + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + int b = in.read(); + + bodyLen = ((l - 192) << 8) + (b) + 192; + } + else if (l == 255) + { + bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + partial = true; + bodyLen = 1 << (l & 0x1f); + } + } + else + { + int lengthType = hdr & 0x3; + + tag = (hdr & 0x3f) >> 2; + + switch (lengthType) + { + case 0: + bodyLen = this.read(); + break; + case 1: + bodyLen = (this.read() << 8) | this.read(); + break; + case 2: + bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read(); + break; + case 3: + partial = true; + break; + default: + throw new IOException("unknown length type encountered"); + } + } + + BCPGInputStream objStream; + + if (bodyLen == 0 && partial) + { + objStream = this; + } + else + { + objStream = new BCPGInputStream(new PartialInputStream(this, partial, bodyLen)); + } + + switch (tag) + { + case RESERVED: + return new InputStreamPacket(objStream); + case PUBLIC_KEY_ENC_SESSION: + return new PublicKeyEncSessionPacket(objStream); + case SIGNATURE: + return new SignaturePacket(objStream); + case SYMMETRIC_KEY_ENC_SESSION: + return new SymmetricKeyEncSessionPacket(objStream); + case ONE_PASS_SIGNATURE: + return new OnePassSignaturePacket(objStream); + case SECRET_KEY: + return new SecretKeyPacket(objStream); + case PUBLIC_KEY: + return new PublicKeyPacket(objStream); + case SECRET_SUBKEY: + return new SecretSubkeyPacket(objStream); + case COMPRESSED_DATA: + return new CompressedDataPacket(objStream); + case SYMMETRIC_KEY_ENC: + return new SymmetricEncDataPacket(objStream); + case MARKER: + return new MarkerPacket(objStream); + case LITERAL_DATA: + return new LiteralDataPacket(objStream); + case TRUST: + return new TrustPacket(objStream); + case USER_ID: + return new UserIDPacket(objStream); + case USER_ATTRIBUTE: + return new UserAttributePacket(objStream); + case PUBLIC_SUBKEY: + return new PublicSubkeyPacket(objStream); + case SYM_ENC_INTEGRITY_PRO: + return new SymmetricEncIntegrityPacket(objStream); + case MOD_DETECTION_CODE: + return new ModDetectionCodePacket(objStream); + case EXPERIMENTAL_1: + case EXPERIMENTAL_2: + case EXPERIMENTAL_3: + case EXPERIMENTAL_4: + return new ExperimentalPacket(tag, objStream); + default: + throw new IOException("unknown packet type encountered: " + tag); + } + } + + public void close() + throws IOException + { + in.close(); + } + + /** + * a stream that overlays our input stream, allowing the user to only read a segment of it. + * + * NB: dataLength will be negative if the segment length is in the upper range above 2**31. + */ + private static class PartialInputStream + extends InputStream + { + private BCPGInputStream in; + private boolean partial; + private int dataLength; + + PartialInputStream( + BCPGInputStream in, + boolean partial, + int dataLength) + { + this.in = in; + this.partial = partial; + this.dataLength = dataLength; + } + + public int available() + throws IOException + { + int avail = in.available(); + + if (avail <= dataLength || dataLength < 0) + { + return avail; + } + else + { + if (partial && dataLength == 0) + { + return 1; + } + return dataLength; + } + } + + private int loadDataLength() + throws IOException + { + int l = in.read(); + + if (l < 0) + { + return -1; + } + + partial = false; + if (l < 192) + { + dataLength = l; + } + else if (l <= 223) + { + dataLength = ((l - 192) << 8) + (in.read()) + 192; + } + else if (l == 255) + { + dataLength = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + partial = true; + dataLength = 1 << (l & 0x1f); + } + + return dataLength; + } + + public int read(byte[] buf, int offset, int len) + throws IOException + { + do + { + if (dataLength != 0) + { + int readLen = (dataLength > len || dataLength < 0) ? len : dataLength; + readLen = in.read(buf, offset, readLen); + if (readLen < 0) + { + throw new EOFException("premature end of stream in PartialInputStream"); + } + dataLength -= readLen; + return readLen; + } + } + while (partial && loadDataLength() >= 0); + + return -1; + } + + public int read() + throws IOException + { + do + { + if (dataLength != 0) + { + int ch = in.read(); + if (ch < 0) + { + throw new EOFException("premature end of stream in PartialInputStream"); + } + dataLength--; + return ch; + } + } + while (partial && loadDataLength() >= 0); + + return -1; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGKey.java new file mode 100644 index 000000000..7025bb311 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGKey.java @@ -0,0 +1,24 @@ +package org.spongycastle.bcpg; + +/** + * base interface for a PGP key + */ +public interface BCPGKey +{ + /** + * Return the base format for this key - in the case of the symmetric keys it will generally + * be raw indicating that the key is just a straight byte representation, for an asymmetric + * key the format will be PGP, indicating the key is a string of MPIs encoded in PGP format. + * + * @return "RAW" or "PGP" + */ + public String getFormat(); + + /** + * return a string of bytes giving the encoded format of the key, as described by it's format. + * + * @return byte[] + */ + public byte[] getEncoded(); + +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGObject.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGObject.java new file mode 100644 index 000000000..55d14d896 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGObject.java @@ -0,0 +1,24 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * base class for a PGP object. + */ +public abstract class BCPGObject +{ + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.writeObject(this); + + return bOut.toByteArray(); + } + + public abstract void encode(BCPGOutputStream out) + throws IOException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGOutputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGOutputStream.java new file mode 100644 index 000000000..560a64564 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/BCPGOutputStream.java @@ -0,0 +1,361 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Basic output stream. + */ +public class BCPGOutputStream + extends OutputStream + implements PacketTags, CompressionAlgorithmTags +{ + OutputStream out; + private byte[] partialBuffer; + private int partialBufferLength; + private int partialPower; + private int partialOffset; + + private static final int BUF_SIZE_POWER = 16; // 2^16 size buffer on long files + + public BCPGOutputStream( + OutputStream out) + { + this.out = out; + } + + /** + * Create a stream representing an old style partial object. + * + * @param tag the packet tag for the object. + */ + public BCPGOutputStream( + OutputStream out, + int tag) + throws IOException + { + this.out = out; + this.writeHeader(tag, true, true, 0); + } + + /** + * Create a stream representing a general packet. + * + * @param out + * @param tag + * @param length + * @param oldFormat + * @throws IOException + */ + public BCPGOutputStream( + OutputStream out, + int tag, + long length, + boolean oldFormat) + throws IOException + { + this.out = out; + + if (length > 0xFFFFFFFFL) + { + this.writeHeader(tag, false, true, 0); + this.partialBufferLength = 1 << BUF_SIZE_POWER; + this.partialBuffer = new byte[partialBufferLength]; + this.partialPower = BUF_SIZE_POWER; + this.partialOffset = 0; + } + else + { + this.writeHeader(tag, oldFormat, false, length); + } + } + + /** + * + * @param tag + * @param length + * @throws IOException + */ + public BCPGOutputStream( + OutputStream out, + int tag, + long length) + throws IOException + { + this.out = out; + + this.writeHeader(tag, false, false, length); + } + + /** + * Create a new style partial input stream buffered into chunks. + * + * @param out output stream to write to. + * @param tag packet tag. + * @param buffer size of chunks making up the packet. + * @throws IOException + */ + public BCPGOutputStream( + OutputStream out, + int tag, + byte[] buffer) + throws IOException + { + this.out = out; + this.writeHeader(tag, false, true, 0); + + this.partialBuffer = buffer; + + int length = partialBuffer.length; + + for (partialPower = 0; length != 1; partialPower++) + { + length >>>= 1; + } + + if (partialPower > 30) + { + throw new IOException("Buffer cannot be greater than 2^30 in length."); + } + + this.partialBufferLength = 1 << partialPower; + this.partialOffset = 0; + } + + private void writeNewPacketLength( + long bodyLen) + throws IOException + { + if (bodyLen < 192) + { + out.write((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + out.write((byte)(((bodyLen >> 8) & 0xff) + 192)); + out.write((byte)bodyLen); + } + else + { + out.write(0xff); + out.write((byte)(bodyLen >> 24)); + out.write((byte)(bodyLen >> 16)); + out.write((byte)(bodyLen >> 8)); + out.write((byte)bodyLen); + } + } + + private void writeHeader( + int tag, + boolean oldPackets, + boolean partial, + long bodyLen) + throws IOException + { + int hdr = 0x80; + + if (partialBuffer != null) + { + partialFlush(true); + partialBuffer = null; + } + + if (oldPackets) + { + hdr |= tag << 2; + + if (partial) + { + this.write(hdr | 0x03); + } + else + { + if (bodyLen <= 0xff) + { + this.write(hdr); + this.write((byte)bodyLen); + } + else if (bodyLen <= 0xffff) + { + this.write(hdr | 0x01); + this.write((byte)(bodyLen >> 8)); + this.write((byte)(bodyLen)); + } + else + { + this.write(hdr | 0x02); + this.write((byte)(bodyLen >> 24)); + this.write((byte)(bodyLen >> 16)); + this.write((byte)(bodyLen >> 8)); + this.write((byte)bodyLen); + } + } + } + else + { + hdr |= 0x40 | tag; + this.write(hdr); + + if (partial) + { + partialOffset = 0; + } + else + { + this.writeNewPacketLength(bodyLen); + } + } + } + + private void partialFlush( + boolean isLast) + throws IOException + { + if (isLast) + { + writeNewPacketLength(partialOffset); + out.write(partialBuffer, 0, partialOffset); + } + else + { + out.write(0xE0 | partialPower); + out.write(partialBuffer, 0, partialBufferLength); + } + + partialOffset = 0; + } + + private void writePartial( + byte b) + throws IOException + { + if (partialOffset == partialBufferLength) + { + partialFlush(false); + } + + partialBuffer[partialOffset++] = b; + } + + private void writePartial( + byte[] buf, + int off, + int len) + throws IOException + { + if (partialOffset == partialBufferLength) + { + partialFlush(false); + } + + if (len <= (partialBufferLength - partialOffset)) + { + System.arraycopy(buf, off, partialBuffer, partialOffset, len); + partialOffset += len; + } + else + { + System.arraycopy(buf, off, partialBuffer, partialOffset, partialBufferLength - partialOffset); + off += partialBufferLength - partialOffset; + len -= partialBufferLength - partialOffset; + partialFlush(false); + + while (len > partialBufferLength) + { + System.arraycopy(buf, off, partialBuffer, 0, partialBufferLength); + off += partialBufferLength; + len -= partialBufferLength; + partialFlush(false); + } + + System.arraycopy(buf, off, partialBuffer, 0, len); + partialOffset += len; + } + } + + public void write( + int b) + throws IOException + { + if (partialBuffer != null) + { + writePartial((byte)b); + } + else + { + out.write(b); + } + } + + public void write( + byte[] bytes, + int off, + int len) + throws IOException + { + if (partialBuffer != null) + { + writePartial(bytes, off, len); + } + else + { + out.write(bytes, off, len); + } + } + + public void writePacket( + ContainedPacket p) + throws IOException + { + p.encode(this); + } + + void writePacket( + int tag, + byte[] body, + boolean oldFormat) + throws IOException + { + this.writeHeader(tag, oldFormat, false, body.length); + this.write(body); + } + + public void writeObject( + BCPGObject o) + throws IOException + { + o.encode(this); + } + + /** + * Flush the underlying stream. + */ + public void flush() + throws IOException + { + out.flush(); + } + + /** + * Finish writing out the current packet without closing the underlying stream. + */ + public void finish() + throws IOException + { + if (partialBuffer != null) + { + partialFlush(true); + partialBuffer = null; + } + } + + public void close() + throws IOException + { + this.finish(); + out.flush(); + out.close(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CRC24.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CRC24.java new file mode 100644 index 000000000..b9db421d7 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CRC24.java @@ -0,0 +1,37 @@ +package org.spongycastle.bcpg; + +public class CRC24 +{ + private static final int CRC24_INIT = 0x0b704ce; + private static final int CRC24_POLY = 0x1864cfb; + + private int crc = CRC24_INIT; + + public CRC24() + { + } + + public void update( + int b) + { + crc ^= b << 16; + for (int i = 0; i < 8; i++) + { + crc <<= 1; + if ((crc & 0x1000000) != 0) + { + crc ^= CRC24_POLY; + } + } + } + + public int getValue() + { + return crc; + } + + public void reset() + { + crc = CRC24_INIT; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressedDataPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressedDataPacket.java new file mode 100644 index 000000000..3513f528a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressedDataPacket.java @@ -0,0 +1,31 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +/** + * generic compressed data object. + */ +public class CompressedDataPacket + extends InputStreamPacket +{ + int algorithm; + + CompressedDataPacket( + BCPGInputStream in) + throws IOException + { + super(in); + + algorithm = in.read(); + } + + /** + * return the algorithm tag value. + * + * @return algorithm tag value. + */ + public int getAlgorithm() + { + return algorithm; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressionAlgorithmTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressionAlgorithmTags.java new file mode 100644 index 000000000..4de0a188e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/CompressionAlgorithmTags.java @@ -0,0 +1,12 @@ +package org.spongycastle.bcpg; + +/** + * Basic tags for compression algorithms + */ +public interface CompressionAlgorithmTags +{ + public static final int UNCOMPRESSED = 0; // Uncompressed + public static final int ZIP = 1; // ZIP (RFC 1951) + public static final int ZLIB = 2; // ZLIB (RFC 1950) + public static final int BZIP2 = 3; // BZ2 +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ContainedPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ContainedPacket.java new file mode 100644 index 000000000..75964559c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ContainedPacket.java @@ -0,0 +1,26 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Basic type for a PGP packet. + */ +public abstract class ContainedPacket + extends Packet +{ + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.writePacket(this); + + return bOut.toByteArray(); + } + + public abstract void encode( + BCPGOutputStream pOut) + throws IOException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSAPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSAPublicBCPGKey.java new file mode 100644 index 000000000..54d0738c5 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSAPublicBCPGKey.java @@ -0,0 +1,116 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * base class for a DSA Public Key. + */ +public class DSAPublicBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger p; + MPInteger q; + MPInteger g; + MPInteger y; + + /** + * @param in the stream to read the packet from. + */ + public DSAPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + this.p = new MPInteger(in); + this.q = new MPInteger(in); + this.g = new MPInteger(in); + this.y = new MPInteger(in); + } + + public DSAPublicBCPGKey( + BigInteger p, + BigInteger q, + BigInteger g, + BigInteger y) + { + this.p = new MPInteger(p); + this.q = new MPInteger(q); + this.g = new MPInteger(g); + this.y = new MPInteger(y); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(p); + out.writeObject(q); + out.writeObject(g); + out.writeObject(y); + } + + /** + * @return g + */ + public BigInteger getG() + { + return g.getValue(); + } + + /** + * @return p + */ + public BigInteger getP() + { + return p.getValue(); + } + + /** + * @return q + */ + public BigInteger getQ() + { + return q.getValue(); + } + + /** + * @return g + */ + public BigInteger getY() + { + return y.getValue(); + } + +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSASecretBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSASecretBCPGKey.java new file mode 100644 index 000000000..17dd68825 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/DSASecretBCPGKey.java @@ -0,0 +1,82 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * base class for a DSA Secret Key. + */ +public class DSASecretBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger x; + + /** + * + * @param in + * @throws IOException + */ + public DSASecretBCPGKey( + BCPGInputStream in) + throws IOException + { + this.x = new MPInteger(in); + } + + /** + * + * @param x + */ + public DSASecretBCPGKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(x); + } + + /** + * @return x + */ + public BigInteger getX() + { + return x.getValue(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDHPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDHPublicBCPGKey.java new file mode 100644 index 000000000..a1ddfffb2 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDHPublicBCPGKey.java @@ -0,0 +1,113 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.math.ec.ECPoint; + +/** + * base class for an ECDH Public Key. + */ +public class ECDHPublicBCPGKey + extends ECPublicBCPGKey +{ + private byte reserved; + private byte hashFunctionId; + private byte symAlgorithmId; + + /** + * @param in the stream to read the packet from. + */ + public ECDHPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + super(in); + + int length = in.read(); + byte[] kdfParameters = new byte[length]; + if (kdfParameters.length != 3) + { + throw new IllegalStateException("kdf parameters size of 3 expected."); + } + + in.read(kdfParameters); + + reserved = kdfParameters[0]; + hashFunctionId = kdfParameters[1]; + symAlgorithmId = kdfParameters[2]; + + verifyHashAlgorithm(); + verifySymmetricKeyAlgorithm(); + } + + public ECDHPublicBCPGKey( + ASN1ObjectIdentifier oid, + ECPoint point, + int hashAlgorithm, + int symmetricKeyAlgorithm) + { + super(oid, point); + + reserved = 1; + hashFunctionId = (byte)hashAlgorithm; + symAlgorithmId = (byte)symmetricKeyAlgorithm; + + verifyHashAlgorithm(); + verifySymmetricKeyAlgorithm(); + } + + public byte getReserved() + { + return reserved; + } + + public byte getHashAlgorithm() + { + return hashFunctionId; + } + + public byte getSymmetricKeyAlgorithm() + { + return symAlgorithmId; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + super.encode(out); + out.write(0x3); + out.write(reserved); + out.write(hashFunctionId); + out.write(symAlgorithmId); + } + + private void verifyHashAlgorithm() + { + switch (hashFunctionId) + { + case HashAlgorithmTags.SHA256: + case HashAlgorithmTags.SHA384: + case HashAlgorithmTags.SHA512: + break; + + default: + throw new IllegalStateException("Hash algorithm must be SHA-256 or stronger."); + } + } + + private void verifySymmetricKeyAlgorithm() + { + switch (symAlgorithmId) + { + case SymmetricKeyAlgorithmTags.AES_128: + case SymmetricKeyAlgorithmTags.AES_192: + case SymmetricKeyAlgorithmTags.AES_256: + break; + + default: + throw new IllegalStateException("Symmetric key algorithm must be AES-128 or stronger."); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDSAPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDSAPublicBCPGKey.java new file mode 100644 index 000000000..36db932a6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECDSAPublicBCPGKey.java @@ -0,0 +1,31 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.math.ec.ECPoint; + +/** + * base class for an ECDSA Public Key. + */ +public class ECDSAPublicBCPGKey + extends ECPublicBCPGKey +{ + /** + * @param in the stream to read the packet from. + */ + protected ECDSAPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + super(in); + } + + public ECDSAPublicBCPGKey( + ASN1ObjectIdentifier oid, + ECPoint point) + { + super(oid, point); + } + +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECPublicBCPGKey.java new file mode 100644 index 000000000..df585146c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECPublicBCPGKey.java @@ -0,0 +1,146 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.x9.ECNamedCurveTable; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +/** + * base class for an EC Public Key. + */ +public abstract class ECPublicBCPGKey + extends BCPGObject + implements BCPGKey +{ + ASN1ObjectIdentifier oid; + ECPoint point; + + /** + * @param in the stream to read the packet from. + */ + protected ECPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + this.oid = ASN1ObjectIdentifier.getInstance(ASN1Primitive.fromByteArray(readBytesOfEncodedLength(in))); + this.point = decodePoint(new MPInteger(in).getValue(), oid); + } + + protected ECPublicBCPGKey( + ASN1ObjectIdentifier oid, + ECPoint point) + { + this.point = point.normalize(); + this.oid = oid; + } + + protected ECPublicBCPGKey( + BigInteger encodedPoint, + ASN1ObjectIdentifier oid) + throws IOException + { + this.point = decodePoint(encodedPoint, oid); + this.oid = oid; + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + byte[] oid = this.oid.getEncoded(); + out.write(oid, 1, oid.length - 1); + + MPInteger point = new MPInteger(new BigInteger(1, this.point.getEncoded())); + out.writeObject(point); + } + + /** + * @return point + */ + public ECPoint getPoint() + { + return point; + } + + /** + * @return oid + */ + public ASN1ObjectIdentifier getCurveOID() + { + return oid; + } + + protected static byte[] readBytesOfEncodedLength( + BCPGInputStream in) + throws IOException + { + int length = in.read(); + if (length == 0 || length == 0xFF) + { + throw new IOException("future extensions not yet implemented."); + } + + byte[] buffer = new byte[length + 2]; + in.readFully(buffer, 2, buffer.length - 2); + buffer[0] = (byte)0x06; + buffer[1] = (byte)length; + + return buffer; + } + + private static ECPoint decodePoint( + BigInteger encodedPoint, + ASN1ObjectIdentifier oid) + throws IOException + { + X9ECParameters curve = ECNamedCurveTable.getByOID(oid); + if (curve == null) + { + throw new IOException(oid.getId() + " does not match any known curve."); + } + if (!(curve.getCurve() instanceof ECCurve.Fp)) + { + throw new IOException("Only FPCurves are supported."); + } + + return curve.getCurve().decodePoint(encodedPoint.toByteArray()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECSecretBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECSecretBCPGKey.java new file mode 100644 index 000000000..124a8d120 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ECSecretBCPGKey.java @@ -0,0 +1,82 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +/** + * base class for an EC Secret Key. + */ +public class ECSecretBCPGKey + extends BCPGObject + implements BCPGKey +{ + MPInteger x; + + /** + * @param in + * @throws IOException + */ + public ECSecretBCPGKey( + BCPGInputStream in) + throws IOException + { + this.x = new MPInteger(in); + } + + /** + * @param x + */ + public ECSecretBCPGKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(x); + } + + /** + * @return x + */ + public BigInteger getX() + { + return x.getValue(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalPublicBCPGKey.java new file mode 100644 index 000000000..bb1bc0b4c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalPublicBCPGKey.java @@ -0,0 +1,93 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * base class for an ElGamal Public Key. + */ +public class ElGamalPublicBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger p; + MPInteger g; + MPInteger y; + + /** + * + */ + public ElGamalPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + this.p = new MPInteger(in); + this.g = new MPInteger(in); + this.y = new MPInteger(in); + } + + public ElGamalPublicBCPGKey( + BigInteger p, + BigInteger g, + BigInteger y) + { + this.p = new MPInteger(p); + this.g = new MPInteger(g); + this.y = new MPInteger(y); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public BigInteger getP() + { + return p.getValue(); + } + + public BigInteger getG() + { + return g.getValue(); + } + + public BigInteger getY() + { + return y.getValue(); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(p); + out.writeObject(g); + out.writeObject(y); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalSecretBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalSecretBCPGKey.java new file mode 100644 index 000000000..9a7d2b02b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ElGamalSecretBCPGKey.java @@ -0,0 +1,79 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * base class for an ElGamal Secret Key. + */ +public class ElGamalSecretBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger x; + + /** + * + * @param in + * @throws IOException + */ + public ElGamalSecretBCPGKey( + BCPGInputStream in) + throws IOException + { + this.x = new MPInteger(in); + } + + /** + * + * @param x + */ + public ElGamalSecretBCPGKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + public BigInteger getX() + { + return x.getValue(); + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(x); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ExperimentalPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ExperimentalPacket.java new file mode 100644 index 000000000..64575c481 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ExperimentalPacket.java @@ -0,0 +1,46 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +import org.spongycastle.util.Arrays; + +/** + * basic packet for an experimental packet. + */ +public class ExperimentalPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + private int tag; + private byte[] contents; + + /** + * + * @param in + * @throws IOException + */ + ExperimentalPacket( + int tag, + BCPGInputStream in) + throws IOException + { + this.tag = tag; + this.contents = in.readAll(); + } + + public int getTag() + { + return tag; + } + + public byte[] getContents() + { + return Arrays.clone(contents); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(tag, contents, true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/HashAlgorithmTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/HashAlgorithmTags.java new file mode 100644 index 000000000..868451b08 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/HashAlgorithmTags.java @@ -0,0 +1,20 @@ +package org.spongycastle.bcpg; + +/** + * basic tags for hash algorithms + */ +public interface HashAlgorithmTags +{ + public static final int MD5 = 1; // MD5 + public static final int SHA1 = 2; // SHA-1 + public static final int RIPEMD160 = 3; // RIPE-MD/160 + public static final int DOUBLE_SHA = 4; // Reserved for double-width SHA (experimental) + public static final int MD2 = 5; // MD2 + public static final int TIGER_192 = 6; // Reserved for TIGER/192 + public static final int HAVAL_5_160 = 7; // Reserved for HAVAL (5 pass, 160-bit) + + public static final int SHA256 = 8; // SHA-256 + public static final int SHA384 = 9; // SHA-384 + public static final int SHA512 = 10; // SHA-512 + public static final int SHA224 = 11; // SHA-224 +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/InputStreamPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/InputStreamPacket.java new file mode 100644 index 000000000..183a79135 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/InputStreamPacket.java @@ -0,0 +1,26 @@ +package org.spongycastle.bcpg; + +/** + * + */ +public class InputStreamPacket + extends Packet +{ + private BCPGInputStream in; + + public InputStreamPacket( + BCPGInputStream in) + { + this.in = in; + } + + /** + * Note: you can only read from this once... + * + * @return the InputStream + */ + public BCPGInputStream getInputStream() + { + return in; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/LiteralDataPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/LiteralDataPacket.java new file mode 100644 index 000000000..b9c8acfb0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/LiteralDataPacket.java @@ -0,0 +1,74 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +import org.spongycastle.util.Strings; + +/** + * generic literal data packet. + */ +public class LiteralDataPacket + extends InputStreamPacket +{ + int format; + byte[] fileName; + long modDate; + + LiteralDataPacket( + BCPGInputStream in) + throws IOException + { + super(in); + + format = in.read(); + int l = in.read(); + + fileName = new byte[l]; + for (int i = 0; i != fileName.length; i++) + { + fileName[i] = (byte)in.read(); + } + + modDate = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + + /** + * return the format tag value. + * + * @return format tag value. + */ + public int getFormat() + { + return format; + } + + /** + * Return the modification time of the file in milli-seconds. + * + * @return the modification time in millis + */ + public long getModificationTime() + { + return modDate * 1000L; + } + + /** + * @return filename + */ + public String getFileName() + { + return Strings.fromUTF8ByteArray(fileName); + } + + public byte[] getRawFileName() + { + byte[] tmp = new byte[fileName.length]; + + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = fileName[i]; + } + + return tmp; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MPInteger.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MPInteger.java new file mode 100644 index 000000000..05abda374 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MPInteger.java @@ -0,0 +1,62 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * a multiple precision integer + */ +public class MPInteger + extends BCPGObject +{ + BigInteger value = null; + + public MPInteger( + BCPGInputStream in) + throws IOException + { + int length = (in.read() << 8) | in.read(); + byte[] bytes = new byte[(length + 7) / 8]; + + in.readFully(bytes); + + value = new BigInteger(1, bytes); + } + + public MPInteger( + BigInteger value) + { + if (value == null || value.signum() < 0) + { + throw new IllegalArgumentException("value must not be null, or negative"); + } + + this.value = value; + } + + public BigInteger getValue() + { + return value; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + int length = value.bitLength(); + + out.write(length >> 8); + out.write(length); + + byte[] bytes = value.toByteArray(); + + if (bytes[0] == 0) + { + out.write(bytes, 1, bytes.length - 1); + } + else + { + out.write(bytes, 0, bytes.length); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MarkerPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MarkerPacket.java new file mode 100644 index 000000000..e4efbe85c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/MarkerPacket.java @@ -0,0 +1,28 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +/** + * Basic type for a marker packet + */ +public class MarkerPacket + extends ContainedPacket +{ + // "PGP" + + byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 }; + + public MarkerPacket( + BCPGInputStream in) + throws IOException + { + in.readFully(marker); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(MARKER, marker, true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ModDetectionCodePacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ModDetectionCodePacket.java new file mode 100644 index 000000000..70bf4d27f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/ModDetectionCodePacket.java @@ -0,0 +1,45 @@ +package org.spongycastle.bcpg; + +import java.io.*; + +/** + * basic packet for a modification detection code packet. + */ +public class ModDetectionCodePacket + extends ContainedPacket +{ + private byte[] digest; + + ModDetectionCodePacket( + BCPGInputStream in) + throws IOException + { + this.digest = new byte[20]; + in.readFully(this.digest); + } + + public ModDetectionCodePacket( + byte[] digest) + throws IOException + { + this.digest = new byte[digest.length]; + + System.arraycopy(digest, 0, this.digest, 0, this.digest.length); + } + + public byte[] getDigest() + { + byte[] tmp = new byte[digest.length]; + + System.arraycopy(digest, 0, tmp, 0, tmp.length); + + return tmp; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(MOD_DETECTION_CODE, digest, false); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OnePassSignaturePacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OnePassSignaturePacket.java new file mode 100644 index 000000000..59d73160d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OnePassSignaturePacket.java @@ -0,0 +1,115 @@ +package org.spongycastle.bcpg; + +import java.io.*; + +/** + * generic signature object + */ +public class OnePassSignaturePacket + extends ContainedPacket +{ + private int version; + private int sigType; + private int hashAlgorithm; + private int keyAlgorithm; + private long keyID; + private int nested; + + OnePassSignaturePacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + sigType = in.read(); + hashAlgorithm = in.read(); + keyAlgorithm = in.read(); + + keyID |= (long)in.read() << 56; + keyID |= (long)in.read() << 48; + keyID |= (long)in.read() << 40; + keyID |= (long)in.read() << 32; + keyID |= (long)in.read() << 24; + keyID |= (long)in.read() << 16; + keyID |= (long)in.read() << 8; + keyID |= in.read(); + + nested = in.read(); + } + + public OnePassSignaturePacket( + int sigType, + int hashAlgorithm, + int keyAlgorithm, + long keyID, + boolean isNested) + { + this.version = 3; + this.sigType = sigType; + this.hashAlgorithm = hashAlgorithm; + this.keyAlgorithm = keyAlgorithm; + this.keyID = keyID; + this.nested = (isNested) ? 0 : 1; + } + + /** + * Return the signature type. + * @return the signature type + */ + public int getSignatureType() + { + return sigType; + } + + /** + * return the encryption algorithm tag + */ + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + /** + * return the hashAlgorithm tag + */ + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + /** + * @return long + */ + public long getKeyID() + { + return keyID; + } + + /** + * + */ + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + pOut.write(sigType); + pOut.write(hashAlgorithm); + pOut.write(keyAlgorithm); + + pOut.write((byte)(keyID >> 56)); + pOut.write((byte)(keyID >> 48)); + pOut.write((byte)(keyID >> 40)); + pOut.write((byte)(keyID >> 32)); + pOut.write((byte)(keyID >> 24)); + pOut.write((byte)(keyID >> 16)); + pOut.write((byte)(keyID >> 8)); + pOut.write((byte)(keyID)); + + pOut.write(nested); + + out.writePacket(ONE_PASS_SIGNATURE, bOut.toByteArray(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OutputStreamPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OutputStreamPacket.java new file mode 100644 index 000000000..4575ee05b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/OutputStreamPacket.java @@ -0,0 +1,18 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +public abstract class OutputStreamPacket +{ + protected BCPGOutputStream out; + + public OutputStreamPacket( + BCPGOutputStream out) + { + this.out = out; + } + + public abstract BCPGOutputStream open() throws IOException; + + public abstract void close() throws IOException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/Packet.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/Packet.java new file mode 100644 index 000000000..6fcc840ea --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/Packet.java @@ -0,0 +1,9 @@ +package org.spongycastle.bcpg; + +/** + */ +public class Packet + implements PacketTags +{ + +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PacketTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PacketTags.java new file mode 100644 index 000000000..d20efcef9 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PacketTags.java @@ -0,0 +1,31 @@ +package org.spongycastle.bcpg; + +/** + * Basic PGP packet tag types. + */ +public interface PacketTags +{ + public static final int RESERVED = 0 ; // Reserved - a packet tag must not have this value + public static final int PUBLIC_KEY_ENC_SESSION = 1; // Public-Key Encrypted Session Key Packet + public static final int SIGNATURE = 2; // Signature Packet + public static final int SYMMETRIC_KEY_ENC_SESSION = 3; // Symmetric-Key Encrypted Session Key Packet + public static final int ONE_PASS_SIGNATURE = 4 ; // One-Pass Signature Packet + public static final int SECRET_KEY = 5; // Secret Key Packet + public static final int PUBLIC_KEY = 6 ; // Public Key Packet + public static final int SECRET_SUBKEY = 7; // Secret Subkey Packet + public static final int COMPRESSED_DATA = 8; // Compressed Data Packet + public static final int SYMMETRIC_KEY_ENC = 9; // Symmetrically Encrypted Data Packet + public static final int MARKER = 10; // Marker Packet + public static final int LITERAL_DATA = 11; // Literal Data Packet + public static final int TRUST = 12; // Trust Packet + public static final int USER_ID = 13; // User ID Packet + public static final int PUBLIC_SUBKEY = 14; // Public Subkey Packet + public static final int USER_ATTRIBUTE = 17; // User attribute + public static final int SYM_ENC_INTEGRITY_PRO = 18; // Symmetric encrypted, integrity protected + public static final int MOD_DETECTION_CODE = 19; // Modification detection code + + public static final int EXPERIMENTAL_1 = 60; // Private or Experimental Values + public static final int EXPERIMENTAL_2 = 61; + public static final int EXPERIMENTAL_3 = 62; + public static final int EXPERIMENTAL_4 = 63; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyAlgorithmTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyAlgorithmTags.java new file mode 100644 index 000000000..fe1d8d950 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyAlgorithmTags.java @@ -0,0 +1,30 @@ +package org.spongycastle.bcpg; + +/** + * Public Key Algorithm tag numbers + */ +public interface PublicKeyAlgorithmTags +{ + public static final int RSA_GENERAL = 1; // RSA (Encrypt or Sign) + public static final int RSA_ENCRYPT = 2; // RSA Encrypt-Only + public static final int RSA_SIGN = 3; // RSA Sign-Only + public static final int ELGAMAL_ENCRYPT = 16; // Elgamal (Encrypt-Only), see [ELGAMAL] + public static final int DSA = 17; // DSA (Digital Signature Standard) + public static final int EC = 18; // Reserved for Elliptic Curve + public static final int ECDH = 18; // Reserved for Elliptic Curve (actual algorithm name) + public static final int ECDSA = 19; // Reserved for ECDSA + public static final int ELGAMAL_GENERAL = 20; // Elgamal (Encrypt or Sign) + public static final int DIFFIE_HELLMAN = 21; // Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME) + + public static final int EXPERIMENTAL_1 = 100; + public static final int EXPERIMENTAL_2 = 101; + public static final int EXPERIMENTAL_3 = 102; + public static final int EXPERIMENTAL_4 = 103; + public static final int EXPERIMENTAL_5 = 104; + public static final int EXPERIMENTAL_6 = 105; + public static final int EXPERIMENTAL_7 = 106; + public static final int EXPERIMENTAL_8 = 107; + public static final int EXPERIMENTAL_9 = 108; + public static final int EXPERIMENTAL_10 = 109; + public static final int EXPERIMENTAL_11 = 110; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyEncSessionPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyEncSessionPacket.java new file mode 100644 index 000000000..fcc9b31dd --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyEncSessionPacket.java @@ -0,0 +1,125 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.spongycastle.util.Arrays; +import org.spongycastle.util.io.Streams; + +/** + * basic packet for a PGP public key + */ +public class PublicKeyEncSessionPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + private int version; + private long keyID; + private int algorithm; + private byte[][] data; + + PublicKeyEncSessionPacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + + keyID |= (long)in.read() << 56; + keyID |= (long)in.read() << 48; + keyID |= (long)in.read() << 40; + keyID |= (long)in.read() << 32; + keyID |= (long)in.read() << 24; + keyID |= (long)in.read() << 16; + keyID |= (long)in.read() << 8; + keyID |= in.read(); + + algorithm = in.read(); + + switch (algorithm) + { + case RSA_ENCRYPT: + case RSA_GENERAL: + data = new byte[1][]; + + data[0] = new MPInteger(in).getEncoded(); + break; + case ELGAMAL_ENCRYPT: + case ELGAMAL_GENERAL: + data = new byte[2][]; + + data[0] = new MPInteger(in).getEncoded(); + data[1] = new MPInteger(in).getEncoded(); + break; + case ECDH: + data = new byte[1][]; + + data[0] = Streams.readAll(in); + break; + default: + throw new IOException("unknown PGP public key algorithm encountered"); + } + } + + public PublicKeyEncSessionPacket( + long keyID, + int algorithm, + byte[][] data) + { + this.version = 3; + this.keyID = keyID; + this.algorithm = algorithm; + this.data = new byte[data.length][]; + + for (int i = 0; i != data.length; i++) + { + this.data[i] = Arrays.clone(data[i]); + } + } + + public int getVersion() + { + return version; + } + + public long getKeyID() + { + return keyID; + } + + public int getAlgorithm() + { + return algorithm; + } + + public byte[][] getEncSessionKey() + { + return data; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + + pOut.write((byte)(keyID >> 56)); + pOut.write((byte)(keyID >> 48)); + pOut.write((byte)(keyID >> 40)); + pOut.write((byte)(keyID >> 32)); + pOut.write((byte)(keyID >> 24)); + pOut.write((byte)(keyID >> 16)); + pOut.write((byte)(keyID >> 8)); + pOut.write((byte)(keyID)); + + pOut.write(algorithm); + + for (int i = 0; i != data.length; i++) + { + pOut.write(data[i]); + } + + out.writePacket(PUBLIC_KEY_ENC_SESSION , bOut.toByteArray(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyPacket.java new file mode 100644 index 000000000..ac1dc3ba1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicKeyPacket.java @@ -0,0 +1,133 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; + +/** + * basic packet for a PGP public key + */ +public class PublicKeyPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + private int version; + private long time; + private int validDays; + private int algorithm; + private BCPGKey key; + + PublicKeyPacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + time = ((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + + if (version <= 3) + { + validDays = (in.read() << 8) | in.read(); + } + + algorithm = (byte)in.read(); + + switch (algorithm) + { + case RSA_ENCRYPT: + case RSA_GENERAL: + case RSA_SIGN: + key = new RSAPublicBCPGKey(in); + break; + case DSA: + key = new DSAPublicBCPGKey(in); + break; + case ELGAMAL_ENCRYPT: + case ELGAMAL_GENERAL: + key = new ElGamalPublicBCPGKey(in); + break; + case EC: + key = new ECDHPublicBCPGKey(in); + break; + case ECDSA: + key = new ECDSAPublicBCPGKey(in); + break; + default: + throw new IOException("unknown PGP public key algorithm encountered"); + } + } + + /** + * Construct version 4 public key packet. + * + * @param algorithm + * @param time + * @param key + */ + public PublicKeyPacket( + int algorithm, + Date time, + BCPGKey key) + { + this.version = 4; + this.time = time.getTime() / 1000; + this.algorithm = algorithm; + this.key = key; + } + + public int getVersion() + { + return version; + } + + public int getAlgorithm() + { + return algorithm; + } + + public int getValidDays() + { + return validDays; + } + + public Date getTime() + { + return new Date(time * 1000); + } + + public BCPGKey getKey() + { + return key; + } + + public byte[] getEncodedContents() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + + pOut.write((byte)(time >> 24)); + pOut.write((byte)(time >> 16)); + pOut.write((byte)(time >> 8)); + pOut.write((byte)time); + + if (version <= 3) + { + pOut.write((byte)(validDays >> 8)); + pOut.write((byte)validDays); + } + + pOut.write(algorithm); + + pOut.writeObject((BCPGObject)key); + + return bOut.toByteArray(); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(PUBLIC_KEY, getEncodedContents(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicSubkeyPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicSubkeyPacket.java new file mode 100644 index 000000000..5e745ef46 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/PublicSubkeyPacket.java @@ -0,0 +1,40 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.util.Date; + +/** + * basic packet for a PGP public key + */ +public class PublicSubkeyPacket + extends PublicKeyPacket +{ + PublicSubkeyPacket( + BCPGInputStream in) + throws IOException + { + super(in); + } + + /** + * Construct version 4 public key packet. + * + * @param algorithm + * @param time + * @param key + */ + public PublicSubkeyPacket( + int algorithm, + Date time, + BCPGKey key) + { + super(algorithm, time, key); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(PUBLIC_SUBKEY, getEncodedContents(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSAPublicBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSAPublicBCPGKey.java new file mode 100644 index 000000000..e6eed70f3 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSAPublicBCPGKey.java @@ -0,0 +1,91 @@ +package org.spongycastle.bcpg; + +import java.math.BigInteger; +import java.io.*; + +/** + * base class for an RSA Public Key. + */ +public class RSAPublicBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger n; + MPInteger e; + + /** + * Construct an RSA public key from the passed in stream. + * + * @param in + * @throws IOException + */ + public RSAPublicBCPGKey( + BCPGInputStream in) + throws IOException + { + this.n = new MPInteger(in); + this.e = new MPInteger(in); + } + + /** + * + * @param n the modulus + * @param e the public exponent + */ + public RSAPublicBCPGKey( + BigInteger n, + BigInteger e) + { + this.n = new MPInteger(n); + this.e = new MPInteger(e); + } + + public BigInteger getPublicExponent() + { + return e.getValue(); + } + + public BigInteger getModulus() + { + return n.getValue(); + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(n); + out.writeObject(e); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSASecretBCPGKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSASecretBCPGKey.java new file mode 100644 index 000000000..a8b211673 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/RSASecretBCPGKey.java @@ -0,0 +1,176 @@ +package org.spongycastle.bcpg; + +import java.io.*; +import java.math.BigInteger; + +/** + * base class for an RSA Secret (or Private) Key. + */ +public class RSASecretBCPGKey + extends BCPGObject implements BCPGKey +{ + MPInteger d; + MPInteger p; + MPInteger q; + MPInteger u; + + BigInteger expP, expQ, crt; + + /** + * + * @param in + * @throws IOException + */ + public RSASecretBCPGKey( + BCPGInputStream in) + throws IOException + { + this.d = new MPInteger(in); + this.p = new MPInteger(in); + this.q = new MPInteger(in); + this.u = new MPInteger(in); + + expP = d.getValue().remainder(p.getValue().subtract(BigInteger.valueOf(1))); + expQ = d.getValue().remainder(q.getValue().subtract(BigInteger.valueOf(1))); + crt = q.getValue().modInverse(p.getValue()); + } + + /** + * + * @param d + * @param p + * @param q + */ + public RSASecretBCPGKey( + BigInteger d, + BigInteger p, + BigInteger q) + { + // + // pgp requires (p < q) + // + int cmp = p.compareTo(q); + if (cmp >= 0) + { + if (cmp == 0) + { + throw new IllegalArgumentException("p and q cannot be equal"); + } + + BigInteger tmp = p; + p = q; + q = tmp; + } + + this.d = new MPInteger(d); + this.p = new MPInteger(p); + this.q = new MPInteger(q); + this.u = new MPInteger(p.modInverse(q)); + + expP = d.remainder(p.subtract(BigInteger.valueOf(1))); + expQ = d.remainder(q.subtract(BigInteger.valueOf(1))); + crt = q.modInverse(p); + } + + /** + * return the modulus for this key. + * + * @return BigInteger + */ + public BigInteger getModulus() + { + return p.getValue().multiply(q.getValue()); + } + + /** + * return the private exponent for this key. + * + * @return BigInteger + */ + public BigInteger getPrivateExponent() + { + return d.getValue(); + } + + /** + * return the prime P + */ + public BigInteger getPrimeP() + { + return p.getValue(); + } + + /** + * return the prime Q + */ + public BigInteger getPrimeQ() + { + return q.getValue(); + } + + /** + * return the prime exponent of p + */ + public BigInteger getPrimeExponentP() + { + return expP; + } + + /** + * return the prime exponent of q + */ + public BigInteger getPrimeExponentQ() + { + return expQ; + } + + /** + * return the crt coefficient + */ + public BigInteger getCrtCoefficient() + { + return crt; + } + + /** + * return "PGP" + * + * @see org.spongycastle.bcpg.BCPGKey#getFormat() + */ + public String getFormat() + { + return "PGP"; + } + + /** + * return the standard PGP encoding of the key. + * + * @see org.spongycastle.bcpg.BCPGKey#getEncoded() + */ + public byte[] getEncoded() + { + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pgpOut = new BCPGOutputStream(bOut); + + pgpOut.writeObject(this); + + return bOut.toByteArray(); + } + catch (IOException e) + { + return null; + } + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writeObject(d); + out.writeObject(p); + out.writeObject(q); + out.writeObject(u); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/S2K.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/S2K.java new file mode 100644 index 000000000..50bf2c4e2 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/S2K.java @@ -0,0 +1,151 @@ +package org.spongycastle.bcpg; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * The string to key specifier class + */ +public class S2K + extends BCPGObject +{ + private static final int EXPBIAS = 6; + + public static final int SIMPLE = 0; + public static final int SALTED = 1; + public static final int SALTED_AND_ITERATED = 3; + public static final int GNU_DUMMY_S2K = 101; + + int type; + int algorithm; + byte[] iv; + int itCount = -1; + int protectionMode = -1; + + S2K( + InputStream in) + throws IOException + { + DataInputStream dIn = new DataInputStream(in); + + type = dIn.read(); + algorithm = dIn.read(); + + // + // if this happens we have a dummy-S2K packet. + // + if (type != GNU_DUMMY_S2K) + { + if (type != 0) + { + iv = new byte[8]; + dIn.readFully(iv, 0, iv.length); + + if (type == 3) + { + itCount = dIn.read(); + } + } + } + else + { + dIn.read(); // G + dIn.read(); // N + dIn.read(); // U + protectionMode = dIn.read(); // protection mode + } + } + + public S2K( + int algorithm) + { + this.type = 0; + this.algorithm = algorithm; + } + + public S2K( + int algorithm, + byte[] iv) + { + this.type = 1; + this.algorithm = algorithm; + this.iv = iv; + } + + public S2K( + int algorithm, + byte[] iv, + int itCount) + { + this.type = 3; + this.algorithm = algorithm; + this.iv = iv; + this.itCount = itCount; + } + + public int getType() + { + return type; + } + + /** + * return the hash algorithm for this S2K + */ + public int getHashAlgorithm() + { + return algorithm; + } + + /** + * return the iv for the key generation algorithm + */ + public byte[] getIV() + { + return iv; + } + + /** + * return the iteration count + */ + public long getIterationCount() + { + return (16 + (itCount & 15)) << ((itCount >> 4) + EXPBIAS); + } + + /** + * the protection mode - only if GNU_DUMMY_S2K + */ + public int getProtectionMode() + { + return protectionMode; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.write(type); + out.write(algorithm); + + if (type != GNU_DUMMY_S2K) + { + if (type != 0) + { + out.write(iv); + } + + if (type == 3) + { + out.write(itCount); + } + } + else + { + out.write('G'); + out.write('N'); + out.write('U'); + out.write(protectionMode); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretKeyPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretKeyPacket.java new file mode 100644 index 000000000..e5e72c39e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretKeyPacket.java @@ -0,0 +1,185 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * basic packet for a PGP secret key + */ +public class SecretKeyPacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + public static final int USAGE_NONE = 0x00; + public static final int USAGE_CHECKSUM = 0xff; + public static final int USAGE_SHA1 = 0xfe; + + private PublicKeyPacket pubKeyPacket; + private byte[] secKeyData; + private int s2kUsage; + private int encAlgorithm; + private S2K s2k; + private byte[] iv; + + /** + * + * @param in + * @throws IOException + */ + SecretKeyPacket( + BCPGInputStream in) + throws IOException + { + if (this instanceof SecretSubkeyPacket) + { + pubKeyPacket = new PublicSubkeyPacket(in); + } + else + { + pubKeyPacket = new PublicKeyPacket(in); + } + + s2kUsage = in.read(); + + if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1) + { + encAlgorithm = in.read(); + s2k = new S2K(in); + } + else + { + encAlgorithm = s2kUsage; + } + + if (!(s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K && s2k.getProtectionMode() == 0x01)) + { + if (s2kUsage != 0) + { + if (encAlgorithm < 7) + { + iv = new byte[8]; + } + else + { + iv = new byte[16]; + } + in.readFully(iv, 0, iv.length); + } + } + + this.secKeyData = in.readAll(); + } + + /** + * + * @param pubKeyPacket + * @param encAlgorithm + * @param s2k + * @param iv + * @param secKeyData + */ + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + + if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL) + { + this.s2kUsage = USAGE_CHECKSUM; + } + else + { + this.s2kUsage = USAGE_NONE; + } + + this.s2k = s2k; + this.iv = iv; + this.secKeyData = secKeyData; + } + + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + int s2kUsage, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + this.s2kUsage = s2kUsage; + this.s2k = s2k; + this.iv = iv; + this.secKeyData = secKeyData; + } + + public int getEncAlgorithm() + { + return encAlgorithm; + } + + public int getS2KUsage() + { + return s2kUsage; + } + + public byte[] getIV() + { + return iv; + } + + public S2K getS2K() + { + return s2k; + } + + public PublicKeyPacket getPublicKeyPacket() + { + return pubKeyPacket; + } + + public byte[] getSecretKeyData() + { + return secKeyData; + } + + public byte[] getEncodedContents() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(pubKeyPacket.getEncodedContents()); + + pOut.write(s2kUsage); + + if (s2kUsage == USAGE_CHECKSUM || s2kUsage == USAGE_SHA1) + { + pOut.write(encAlgorithm); + pOut.writeObject(s2k); + } + + if (iv != null) + { + pOut.write(iv); + } + + if (secKeyData != null && secKeyData.length > 0) + { + pOut.write(secKeyData); + } + + return bOut.toByteArray(); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(SECRET_KEY, getEncodedContents(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretSubkeyPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretSubkeyPacket.java new file mode 100644 index 000000000..d978d5c42 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SecretSubkeyPacket.java @@ -0,0 +1,58 @@ +package org.spongycastle.bcpg; + +import java.io.*; + +/** + * basic packet for a PGP secret key + */ +public class SecretSubkeyPacket + extends SecretKeyPacket +{ + /** + * + * @param in + * @throws IOException + */ + SecretSubkeyPacket( + BCPGInputStream in) + throws IOException + { + super(in); + } + + /** + * + * @param pubKeyPacket + * @param encAlgorithm + * @param s2k + * @param iv + * @param secKeyData + */ + public SecretSubkeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + super(pubKeyPacket, encAlgorithm, s2k, iv, secKeyData); + } + + public SecretSubkeyPacket( + PublicKeyPacket pubKeyPacket, + int encAlgorithm, + int s2kUsage, + S2K s2k, + byte[] iv, + byte[] secKeyData) + { + super(pubKeyPacket, encAlgorithm, s2kUsage, s2k, iv, secKeyData); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(SECRET_SUBKEY, getEncodedContents(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignaturePacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignaturePacket.java new file mode 100644 index 000000000..38c06ccb5 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignaturePacket.java @@ -0,0 +1,523 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Vector; + +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.util.Arrays; + +/** + * generic signature packet + */ +public class SignaturePacket + extends ContainedPacket implements PublicKeyAlgorithmTags +{ + private int version; + private int signatureType; + private long creationTime; + private long keyID; + private int keyAlgorithm; + private int hashAlgorithm; + private MPInteger[] signature; + private byte[] fingerPrint; + private SignatureSubpacket[] hashedData; + private SignatureSubpacket[] unhashedData; + private byte[] signatureEncoding; + + SignaturePacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + + if (version == 3 || version == 2) + { + int l = in.read(); + + signatureType = in.read(); + creationTime = (((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read()) * 1000; + keyID |= (long)in.read() << 56; + keyID |= (long)in.read() << 48; + keyID |= (long)in.read() << 40; + keyID |= (long)in.read() << 32; + keyID |= (long)in.read() << 24; + keyID |= (long)in.read() << 16; + keyID |= (long)in.read() << 8; + keyID |= in.read(); + keyAlgorithm = in.read(); + hashAlgorithm = in.read(); + } + else if (version == 4) + { + signatureType = in.read(); + keyAlgorithm = in.read(); + hashAlgorithm = in.read(); + + int hashedLength = (in.read() << 8) | in.read(); + byte[] hashed = new byte[hashedLength]; + + in.readFully(hashed); + + // + // read the signature sub packet data. + // + SignatureSubpacket sub; + SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream( + new ByteArrayInputStream(hashed)); + + Vector v = new Vector(); + while ((sub = sIn.readPacket()) != null) + { + v.addElement(sub); + } + + hashedData = new SignatureSubpacket[v.size()]; + + for (int i = 0; i != hashedData.length; i++) + { + SignatureSubpacket p = (SignatureSubpacket)v.elementAt(i); + if (p instanceof IssuerKeyID) + { + keyID = ((IssuerKeyID)p).getKeyID(); + } + else if (p instanceof SignatureCreationTime) + { + creationTime = ((SignatureCreationTime)p).getTime().getTime(); + } + + hashedData[i] = p; + } + + int unhashedLength = (in.read() << 8) | in.read(); + byte[] unhashed = new byte[unhashedLength]; + + in.readFully(unhashed); + + sIn = new SignatureSubpacketInputStream( + new ByteArrayInputStream(unhashed)); + + v.removeAllElements(); + while ((sub = sIn.readPacket()) != null) + { + v.addElement(sub); + } + + unhashedData = new SignatureSubpacket[v.size()]; + + for (int i = 0; i != unhashedData.length; i++) + { + SignatureSubpacket p = (SignatureSubpacket)v.elementAt(i); + if (p instanceof IssuerKeyID) + { + keyID = ((IssuerKeyID)p).getKeyID(); + } + + unhashedData[i] = p; + } + } + else + { + throw new RuntimeException("unsupported version: " + version); + } + + fingerPrint = new byte[2]; + in.readFully(fingerPrint); + + switch (keyAlgorithm) + { + case RSA_GENERAL: + case RSA_SIGN: + MPInteger v = new MPInteger(in); + + signature = new MPInteger[1]; + signature[0] = v; + break; + case DSA: + MPInteger r = new MPInteger(in); + MPInteger s = new MPInteger(in); + + signature = new MPInteger[2]; + signature[0] = r; + signature[1] = s; + break; + case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes. + case ELGAMAL_GENERAL: + MPInteger p = new MPInteger(in); + MPInteger g = new MPInteger(in); + MPInteger y = new MPInteger(in); + + signature = new MPInteger[3]; + signature[0] = p; + signature[1] = g; + signature[2] = y; + break; + case ECDSA: + MPInteger ecR = new MPInteger(in); + MPInteger ecS = new MPInteger(in); + + signature = new MPInteger[2]; + signature[0] = ecR; + signature[1] = ecS; + break; + default: + if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11) + { + signature = null; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int ch; + while ((ch = in.read()) >= 0) + { + bOut.write(ch); + } + signatureEncoding = bOut.toByteArray(); + } + else + { + throw new IOException("unknown signature key algorithm: " + keyAlgorithm); + } + } + } + + /** + * Generate a version 4 signature packet. + * + * @param signatureType + * @param keyAlgorithm + * @param hashAlgorithm + * @param hashedData + * @param unhashedData + * @param fingerPrint + * @param signature + */ + public SignaturePacket( + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) + { + this(4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature); + } + + /** + * Generate a version 2/3 signature packet. + * + * @param signatureType + * @param keyAlgorithm + * @param hashAlgorithm + * @param fingerPrint + * @param signature + */ + public SignaturePacket( + int version, + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + long creationTime, + byte[] fingerPrint, + MPInteger[] signature) + { + this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, signature); + + this.creationTime = creationTime; + } + + public SignaturePacket( + int version, + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) + { + this.version = version; + this.signatureType = signatureType; + this.keyID = keyID; + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + this.hashedData = hashedData; + this.unhashedData = unhashedData; + this.fingerPrint = fingerPrint; + this.signature = signature; + + if (hashedData != null) + { + setCreationTime(); + } + } + + /** + * get the version number + */ + public int getVersion() + { + return version; + } + + /** + * return the signature type. + */ + public int getSignatureType() + { + return signatureType; + } + + /** + * return the keyID + * @return the keyID that created the signature. + */ + public long getKeyID() + { + return keyID; + } + + /** + * return the signature trailer that must be included with the data + * to reconstruct the signature + * + * @return byte[] + */ + public byte[] getSignatureTrailer() + { + byte[] trailer = null; + + if (version == 3 || version == 2) + { + trailer = new byte[5]; + + long time = creationTime / 1000; + + trailer[0] = (byte)signatureType; + trailer[1] = (byte)(time >> 24); + trailer[2] = (byte)(time >> 16); + trailer[3] = (byte)(time >> 8); + trailer[4] = (byte)(time); + } + else + { + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + + try + { + sOut.write((byte)this.getVersion()); + sOut.write((byte)this.getSignatureType()); + sOut.write((byte)this.getKeyAlgorithm()); + sOut.write((byte)this.getHashAlgorithm()); + + ByteArrayOutputStream hOut = new ByteArrayOutputStream(); + SignatureSubpacket[] hashed = this.getHashedSubPackets(); + + for (int i = 0; i != hashed.length; i++) + { + hashed[i].encode(hOut); + } + + byte[] data = hOut.toByteArray(); + + sOut.write((byte)(data.length >> 8)); + sOut.write((byte)data.length); + sOut.write(data); + + byte[] hData = sOut.toByteArray(); + + sOut.write((byte)this.getVersion()); + sOut.write((byte)0xff); + sOut.write((byte)(hData.length>> 24)); + sOut.write((byte)(hData.length >> 16)); + sOut.write((byte)(hData.length >> 8)); + sOut.write((byte)(hData.length)); + } + catch (IOException e) + { + throw new RuntimeException("exception generating trailer: " + e); + } + + trailer = sOut.toByteArray(); + } + + return trailer; + } + + /** + * return the encryption algorithm tag + */ + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + /** + * return the hashAlgorithm tag + */ + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + /** + * return the signature as a set of integers - note this is normalised to be the + * ASN.1 encoding of what appears in the signature packet. + */ + public MPInteger[] getSignature() + { + return signature; + } + + /** + * Return the byte encoding of the signature section. + * @return uninterpreted signature bytes. + */ + public byte[] getSignatureBytes() + { + if (signatureEncoding == null) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream bcOut = new BCPGOutputStream(bOut); + + for (int i = 0; i != signature.length; i++) + { + try + { + bcOut.writeObject(signature[i]); + } + catch (IOException e) + { + throw new RuntimeException("internal error: " + e); + } + } + return bOut.toByteArray(); + } + else + { + return Arrays.clone(signatureEncoding); + } + } + public SignatureSubpacket[] getHashedSubPackets() + { + return hashedData; + } + + public SignatureSubpacket[] getUnhashedSubPackets() + { + return unhashedData; + } + + /** + * Return the creation time of the signature in milli-seconds. + * + * @return the creation time in millis + */ + public long getCreationTime() + { + return creationTime; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + + if (version == 3 || version == 2) + { + pOut.write(5); // the length of the next block + + long time = creationTime / 1000; + + pOut.write(signatureType); + pOut.write((byte)(time >> 24)); + pOut.write((byte)(time >> 16)); + pOut.write((byte)(time >> 8)); + pOut.write((byte)time); + + pOut.write((byte)(keyID >> 56)); + pOut.write((byte)(keyID >> 48)); + pOut.write((byte)(keyID >> 40)); + pOut.write((byte)(keyID >> 32)); + pOut.write((byte)(keyID >> 24)); + pOut.write((byte)(keyID >> 16)); + pOut.write((byte)(keyID >> 8)); + pOut.write((byte)(keyID)); + + pOut.write(keyAlgorithm); + pOut.write(hashAlgorithm); + } + else if (version == 4) + { + pOut.write(signatureType); + pOut.write(keyAlgorithm); + pOut.write(hashAlgorithm); + + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + + for (int i = 0; i != hashedData.length; i++) + { + hashedData[i].encode(sOut); + } + + byte[] data = sOut.toByteArray(); + + pOut.write(data.length >> 8); + pOut.write(data.length); + pOut.write(data); + + sOut.reset(); + + for (int i = 0; i != unhashedData.length; i++) + { + unhashedData[i].encode(sOut); + } + + data = sOut.toByteArray(); + + pOut.write(data.length >> 8); + pOut.write(data.length); + pOut.write(data); + } + else + { + throw new IOException("unknown version: " + version); + } + + pOut.write(fingerPrint); + + if (signature != null) + { + for (int i = 0; i != signature.length; i++) + { + pOut.writeObject(signature[i]); + } + } + else + { + pOut.write(signatureEncoding); + } + + out.writePacket(SIGNATURE, bOut.toByteArray(), true); + } + + private void setCreationTime() + { + for (int i = 0; i != hashedData.length; i++) + { + if (hashedData[i] instanceof SignatureCreationTime) + { + creationTime = ((SignatureCreationTime)hashedData[i]).getTime().getTime(); + break; + } + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacket.java new file mode 100644 index 000000000..c427cef94 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacket.java @@ -0,0 +1,81 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Basic type for a PGP Signature sub-packet. + */ +public class SignatureSubpacket +{ + int type; + boolean critical; + + protected byte[] data; + + protected SignatureSubpacket( + int type, + boolean critical, + byte[] data) + { + this.type = type; + this.critical = critical; + this.data = data; + } + + public int getType() + { + return type; + } + + public boolean isCritical() + { + return critical; + } + + /** + * return the generic data making up the packet. + */ + public byte[] getData() + { + return data; + } + + public void encode( + OutputStream out) + throws IOException + { + int bodyLen = data.length + 1; + + if (bodyLen < 192) + { + out.write((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + out.write((byte)(((bodyLen >> 8) & 0xff) + 192)); + out.write((byte)bodyLen); + } + else + { + out.write(0xff); + out.write((byte)(bodyLen >> 24)); + out.write((byte)(bodyLen >> 16)); + out.write((byte)(bodyLen >> 8)); + out.write((byte)bodyLen); + } + + if (critical) + { + out.write(0x80 | type); + } + else + { + out.write(type); + } + + out.write(data); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketInputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketInputStream.java new file mode 100644 index 000000000..678062a58 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketInputStream.java @@ -0,0 +1,159 @@ +package org.spongycastle.bcpg; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.spongycastle.bcpg.sig.Exportable; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.KeyExpirationTime; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.bcpg.sig.PreferredAlgorithms; +import org.spongycastle.bcpg.sig.PrimaryUserID; +import org.spongycastle.bcpg.sig.Revocable; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.bcpg.sig.SignatureExpirationTime; +import org.spongycastle.bcpg.sig.SignerUserID; +import org.spongycastle.bcpg.sig.TrustSignature; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.io.Streams; + +/** + * reader for signature sub-packets + */ +public class SignatureSubpacketInputStream + extends InputStream implements SignatureSubpacketTags +{ + InputStream in; + + public SignatureSubpacketInputStream( + InputStream in) + { + this.in = in; + } + + public int available() + throws IOException + { + return in.available(); + } + + public int read() + throws IOException + { + return in.read(); + } + + public SignatureSubpacket readPacket() + throws IOException + { + int l = this.read(); + int bodyLen = 0; + + if (l < 0) + { + return null; + } + + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + bodyLen = ((l - 192) << 8) + (in.read()) + 192; + } + else if (l == 255) + { + bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + // TODO Error? + } + + int tag = in.read(); + + if (tag < 0) + { + throw new EOFException("unexpected EOF reading signature sub packet"); + } + + byte[] data = new byte[bodyLen - 1]; + + // + // this may seem a bit strange but it turns out some applications miscode the length + // in fixed length fields, so we check the length we do get, only throwing an exception if + // we really cannot continue + // + int bytesRead = Streams.readFully(in, data); + + boolean isCritical = ((tag & 0x80) != 0); + int type = tag & 0x7f; + + if (bytesRead != data.length) + { + switch (type) + { + case CREATION_TIME: + data = checkData(data, 4, bytesRead, "Signature Creation Time"); + break; + case ISSUER_KEY_ID: + data = checkData(data, 8, bytesRead, "Issuer"); + break; + case KEY_EXPIRE_TIME: + data = checkData(data, 4, bytesRead, "Signature Key Expiration Time"); + break; + case EXPIRE_TIME: + data = checkData(data, 4, bytesRead, "Signature Expiration Time"); + break; + default: + throw new EOFException("truncated subpacket data."); + } + } + + switch (type) + { + case CREATION_TIME: + return new SignatureCreationTime(isCritical, data); + case KEY_EXPIRE_TIME: + return new KeyExpirationTime(isCritical, data); + case EXPIRE_TIME: + return new SignatureExpirationTime(isCritical, data); + case REVOCABLE: + return new Revocable(isCritical, data); + case EXPORTABLE: + return new Exportable(isCritical, data); + case ISSUER_KEY_ID: + return new IssuerKeyID(isCritical, data); + case TRUST_SIG: + return new TrustSignature(isCritical, data); + case PREFERRED_COMP_ALGS: + case PREFERRED_HASH_ALGS: + case PREFERRED_SYM_ALGS: + return new PreferredAlgorithms(type, isCritical, data); + case KEY_FLAGS: + return new KeyFlags(isCritical, data); + case PRIMARY_USER_ID: + return new PrimaryUserID(isCritical, data); + case SIGNER_USER_ID: + return new SignerUserID(isCritical, data); + case NOTATION_DATA: + return new NotationData(isCritical, data); + } + + return new SignatureSubpacket(type, isCritical, data); + } + + private byte[] checkData(byte[] data, int expected, int bytesRead, String name) + throws EOFException + { + if (bytesRead != expected) + { + throw new EOFException("truncated " + name + " subpacket data."); + } + + return Arrays.copyOfRange(data, 0, expected); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketTags.java new file mode 100644 index 000000000..fdeaae52f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SignatureSubpacketTags.java @@ -0,0 +1,32 @@ +package org.spongycastle.bcpg; + +/** + * Basic PGP signature sub-packet tag types. + */ +public interface SignatureSubpacketTags +{ + public static final int CREATION_TIME = 2; // signature creation time + public static final int EXPIRE_TIME = 3; // signature expiration time + public static final int EXPORTABLE = 4; // exportable certification + public static final int TRUST_SIG = 5; // trust signature + public static final int REG_EXP = 6; // regular expression + public static final int REVOCABLE = 7; // revocable + public static final int KEY_EXPIRE_TIME = 9; // key expiration time + public static final int PLACEHOLDER = 10; // placeholder for backward compatibility + public static final int PREFERRED_SYM_ALGS = 11; // preferred symmetric algorithms + public static final int REVOCATION_KEY = 12; // revocation key + public static final int ISSUER_KEY_ID = 16; // issuer key ID + public static final int NOTATION_DATA = 20; // notation data + public static final int PREFERRED_HASH_ALGS = 21; // preferred hash algorithms + public static final int PREFERRED_COMP_ALGS = 22; // preferred compression algorithms + public static final int KEY_SERVER_PREFS = 23; // key server preferences + public static final int PREFERRED_KEY_SERV = 24; // preferred key server + public static final int PRIMARY_USER_ID = 25; // primary user id + public static final int POLICY_URL = 26; // policy URL + public static final int KEY_FLAGS = 27; // key flags + public static final int SIGNER_USER_ID = 28; // signer's user id + public static final int REVOCATION_REASON = 29; // reason for revocation + public static final int FEATURES = 30; // features + public static final int SIGNATURE_TARGET = 31; // signature target + public static final int EMBEDDED_SIGNATURE = 32; // embedded signature +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncDataPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncDataPacket.java new file mode 100644 index 000000000..51cace695 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncDataPacket.java @@ -0,0 +1,14 @@ +package org.spongycastle.bcpg; + +/** + * Basic type for a symmetric key encrypted packet + */ +public class SymmetricEncDataPacket + extends InputStreamPacket +{ + public SymmetricEncDataPacket( + BCPGInputStream in) + { + super(in); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncIntegrityPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncIntegrityPacket.java new file mode 100644 index 000000000..aa9df0970 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricEncIntegrityPacket.java @@ -0,0 +1,20 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +/** + */ +public class SymmetricEncIntegrityPacket + extends InputStreamPacket +{ + int version; + + SymmetricEncIntegrityPacket( + BCPGInputStream in) + throws IOException + { + super(in); + + version = in.read(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyAlgorithmTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyAlgorithmTags.java new file mode 100644 index 000000000..4f3c7a2a1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyAlgorithmTags.java @@ -0,0 +1,19 @@ +package org.spongycastle.bcpg; + +/** + * Basic tags for symmetric key algorithms + */ +public interface SymmetricKeyAlgorithmTags +{ + public static final int NULL = 0; // Plaintext or unencrypted data + public static final int IDEA = 1; // IDEA [IDEA] + public static final int TRIPLE_DES = 2; // Triple-DES (DES-EDE, as per spec -168 bit key derived from 192) + public static final int CAST5 = 3; // CAST5 (128 bit key, as per RFC 2144) + public static final int BLOWFISH = 4; // Blowfish (128 bit key, 16 rounds) [BLOWFISH] + public static final int SAFER = 5; // SAFER-SK128 (13 rounds) [SAFER] + public static final int DES = 6; // Reserved for DES/SK + public static final int AES_128 = 7; // Reserved for AES with 128-bit key + public static final int AES_192 = 8; // Reserved for AES with 192-bit key + public static final int AES_256 = 9; // Reserved for AES with 256-bit key + public static final int TWOFISH = 10; // Reserved for Twofish +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyEncSessionPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyEncSessionPacket.java new file mode 100644 index 000000000..138bbba4c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/SymmetricKeyEncSessionPacket.java @@ -0,0 +1,90 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Basic type for a symmetric encrypted session key packet + */ +public class SymmetricKeyEncSessionPacket + extends ContainedPacket +{ + private int version; + private int encAlgorithm; + private S2K s2k; + private byte[] secKeyData; + + public SymmetricKeyEncSessionPacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + encAlgorithm = in.read(); + + s2k = new S2K(in); + + this.secKeyData = in.readAll(); + } + + public SymmetricKeyEncSessionPacket( + int encAlgorithm, + S2K s2k, + byte[] secKeyData) + { + this.version = 4; + this.encAlgorithm = encAlgorithm; + this.s2k = s2k; + this.secKeyData = secKeyData; + } + + /** + * @return int + */ + public int getEncAlgorithm() + { + return encAlgorithm; + } + + /** + * @return S2K + */ + public S2K getS2K() + { + return s2k; + } + + /** + * @return byte[] + */ + public byte[] getSecKeyData() + { + return secKeyData; + } + + /** + * @return int + */ + public int getVersion() + { + return version; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.write(version); + pOut.write(encAlgorithm); + pOut.writeObject(s2k); + + if (secKeyData != null && secKeyData.length > 0) + { + pOut.write(secKeyData); + } + + out.writePacket(SYMMETRIC_KEY_ENC_SESSION, bOut.toByteArray(), true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/TrustPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/TrustPacket.java new file mode 100644 index 000000000..3a98003be --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/TrustPacket.java @@ -0,0 +1,48 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Basic type for a trust packet + */ +public class TrustPacket + extends ContainedPacket +{ + byte[] levelAndTrustAmount; + + public TrustPacket( + BCPGInputStream in) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int ch; + + while ((ch = in.read()) >= 0) + { + bOut.write(ch); + } + + levelAndTrustAmount = bOut.toByteArray(); + } + + public TrustPacket( + int trustCode) + { + this.levelAndTrustAmount = new byte[1]; + + this.levelAndTrustAmount[0] = (byte)trustCode; + } + + public byte[] getLevelAndTrustAmount() + { + return levelAndTrustAmount; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(TRUST, levelAndTrustAmount, true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributePacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributePacket.java new file mode 100644 index 000000000..64d3ca08a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributePacket.java @@ -0,0 +1,60 @@ +package org.spongycastle.bcpg; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Vector; + +/** + * Basic type for a user attribute packet. + */ +public class UserAttributePacket + extends ContainedPacket +{ + private UserAttributeSubpacket[] subpackets; + + public UserAttributePacket( + BCPGInputStream in) + throws IOException + { + UserAttributeSubpacketInputStream sIn = new UserAttributeSubpacketInputStream(in); + UserAttributeSubpacket sub; + + Vector v= new Vector(); + while ((sub = sIn.readPacket()) != null) + { + v.addElement(sub); + } + + subpackets = new UserAttributeSubpacket[v.size()]; + + for (int i = 0; i != subpackets.length; i++) + { + subpackets[i] = (UserAttributeSubpacket)v.elementAt(i); + } + } + + public UserAttributePacket( + UserAttributeSubpacket[] subpackets) + { + this.subpackets = subpackets; + } + + public UserAttributeSubpacket[] getSubpackets() + { + return subpackets; + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + for (int i = 0; i != subpackets.length; i++) + { + subpackets[i].encode(bOut); + } + + out.writePacket(USER_ATTRIBUTE, bOut.toByteArray(), false); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacket.java new file mode 100644 index 000000000..fabbc7a84 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacket.java @@ -0,0 +1,91 @@ +package org.spongycastle.bcpg; + +import org.spongycastle.util.Arrays; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Basic type for a user attribute sub-packet. + */ +public class UserAttributeSubpacket +{ + int type; + + protected byte[] data; + + protected UserAttributeSubpacket( + int type, + byte[] data) + { + this.type = type; + this.data = data; + } + + public int getType() + { + return type; + } + + /** + * return the generic data making up the packet. + */ + public byte[] getData() + { + return data; + } + + public void encode( + OutputStream out) + throws IOException + { + int bodyLen = data.length + 1; + + if (bodyLen < 192) + { + out.write((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + out.write((byte)(((bodyLen >> 8) & 0xff) + 192)); + out.write((byte)bodyLen); + } + else + { + out.write(0xff); + out.write((byte)(bodyLen >> 24)); + out.write((byte)(bodyLen >> 16)); + out.write((byte)(bodyLen >> 8)); + out.write((byte)bodyLen); + } + + out.write(type); + out.write(data); + } + + public boolean equals( + Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof UserAttributeSubpacket)) + { + return false; + } + + UserAttributeSubpacket other = (UserAttributeSubpacket)o; + + return this.type == other.type + && Arrays.areEqual(this.data, other.data); + } + + public int hashCode() + { + return type ^ Arrays.hashCode(data); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketInputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketInputStream.java new file mode 100644 index 000000000..ab55ea4e9 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketInputStream.java @@ -0,0 +1,116 @@ +package org.spongycastle.bcpg; + +import java.io.*; + +import org.spongycastle.bcpg.attr.ImageAttribute; + +/** + * reader for user attribute sub-packets + */ +public class UserAttributeSubpacketInputStream + extends InputStream implements UserAttributeSubpacketTags +{ + InputStream in; + + public UserAttributeSubpacketInputStream( + InputStream in) + { + this.in = in; + } + + public int available() + throws IOException + { + return in.available(); + } + + public int read() + throws IOException + { + return in.read(); + } + + private void readFully( + byte[] buf, + int off, + int len) + throws IOException + { + if (len > 0) + { + int b = this.read(); + + if (b < 0) + { + throw new EOFException(); + } + + buf[off] = (byte)b; + off++; + len--; + } + + while (len > 0) + { + int l = in.read(buf, off, len); + + if (l < 0) + { + throw new EOFException(); + } + + off += l; + len -= l; + } + } + + public UserAttributeSubpacket readPacket() + throws IOException + { + int l = this.read(); + int bodyLen = 0; + + if (l < 0) + { + return null; + } + + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + bodyLen = ((l - 192) << 8) + (in.read()) + 192; + } + else if (l == 255) + { + bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); + } + else + { + // TODO Error? + } + + int tag = in.read(); + + if (tag < 0) + { + throw new EOFException("unexpected EOF reading user attribute sub packet"); + } + + byte[] data = new byte[bodyLen - 1]; + + this.readFully(data, 0, data.length); + + int type = tag; + + switch (type) + { + case IMAGE_ATTRIBUTE: + return new ImageAttribute(data); + } + + return new UserAttributeSubpacket(type, data); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketTags.java new file mode 100644 index 000000000..fc653140a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserAttributeSubpacketTags.java @@ -0,0 +1,9 @@ +package org.spongycastle.bcpg; + +/** + * Basic PGP user attribute sub-packet tag types. + */ +public interface UserAttributeSubpacketTags +{ + public static final int IMAGE_ATTRIBUTE = 1; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserIDPacket.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserIDPacket.java new file mode 100644 index 000000000..0a9d64428 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/UserIDPacket.java @@ -0,0 +1,39 @@ +package org.spongycastle.bcpg; + +import java.io.IOException; + +import org.spongycastle.util.Strings; + +/** + * Basic type for a user ID packet. + */ +public class UserIDPacket + extends ContainedPacket +{ + private byte[] idData; + + public UserIDPacket( + BCPGInputStream in) + throws IOException + { + this.idData = in.readAll(); + } + + public UserIDPacket( + String id) + { + this.idData = Strings.toUTF8ByteArray(id); + } + + public String getID() + { + return Strings.fromUTF8ByteArray(idData); + } + + public void encode( + BCPGOutputStream out) + throws IOException + { + out.writePacket(USER_ID, idData, true); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/attr/ImageAttribute.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/attr/ImageAttribute.java new file mode 100644 index 000000000..df48463f9 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/attr/ImageAttribute.java @@ -0,0 +1,77 @@ +package org.spongycastle.bcpg.attr; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.UserAttributeSubpacketTags; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Basic type for a image attribute packet. + */ +public class ImageAttribute + extends UserAttributeSubpacket +{ + public static final int JPEG = 1; + + private static final byte[] ZEROES = new byte[12]; + + private int hdrLength; + private int version; + private int encoding; + private byte[] imageData; + + public ImageAttribute( + byte[] data) + { + super(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE, data); + + hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff); + version = data[2] & 0xff; + encoding = data[3] & 0xff; + + imageData = new byte[data.length - hdrLength]; + System.arraycopy(data, hdrLength, imageData, 0, imageData.length); + } + + public ImageAttribute( + int imageType, + byte[] imageData) + { + this(toByteArray(imageType, imageData)); + } + + private static byte[] toByteArray(int imageType, byte[] imageData) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + bOut.write(0x10); bOut.write(0x00); bOut.write(0x01); + bOut.write(imageType); + bOut.write(ZEROES); + bOut.write(imageData); + } + catch (IOException e) + { + throw new RuntimeException("unable to encode to byte array!"); + } + + return bOut.toByteArray(); + } + + public int version() + { + return version; + } + + public int getEncoding() + { + return encoding; + } + + public byte[] getImageData() + { + return imageData; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/EmbeddedSignature.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/EmbeddedSignature.java new file mode 100644 index 000000000..b4861b129 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/EmbeddedSignature.java @@ -0,0 +1,18 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * Packet embedded signature + */ +public class EmbeddedSignature + extends SignatureSubpacket +{ + public EmbeddedSignature( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.EMBEDDED_SIGNATURE, critical, data); + } +}
\ No newline at end of file diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Exportable.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Exportable.java new file mode 100644 index 000000000..5831a69a0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Exportable.java @@ -0,0 +1,46 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving signature creation time. + */ +public class Exportable + extends SignatureSubpacket +{ + private static byte[] booleanToByteArray( + boolean value) + { + byte[] data = new byte[1]; + + if (value) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public Exportable( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.EXPORTABLE, critical, data); + } + + public Exportable( + boolean critical, + boolean isExportable) + { + super(SignatureSubpacketTags.EXPORTABLE, critical, booleanToByteArray(isExportable)); + } + + public boolean isExportable() + { + return data[0] != 0; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Features.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Features.java new file mode 100644 index 000000000..02ee57903 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Features.java @@ -0,0 +1,98 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +public class Features + extends SignatureSubpacket +{ + + /** Identifier for the modification detection feature */ + public static final byte FEATURE_MODIFICATION_DETECTION = 1; + + private static final byte[] featureToByteArray(byte feature) + { + byte[] data = new byte[1]; + data[0] = feature; + return data; + } + + public Features(boolean critical, byte[] data) + { + super(SignatureSubpacketTags.FEATURES, critical, data); + } + + public Features(boolean critical, byte feature) + { + super(SignatureSubpacketTags.FEATURES, critical, featureToByteArray(feature)); + } + + /** + * Returns if modification detection is supported. + */ + public boolean supportsModificationDetection() + { + return supportsFeature(FEATURE_MODIFICATION_DETECTION); + } + + +// /** Class should be immutable. +// * Set modification detection support. +// */ +// public void setSupportsModificationDetection(boolean support) +// { +// setSupportsFeature(FEATURE_MODIFICATION_DETECTION, support); +// } + + + /** + * Returns if a particular feature is supported. + */ + public boolean supportsFeature(byte feature) + { + for (int i = 0; i < data.length; i++) + { + if (data[i] == feature) + { + return true; + } + } + return false; + } + + + /** + * Sets support for a particular feature. + */ + private void setSupportsFeature(byte feature, boolean support) + { + if (feature == 0) + { + throw new IllegalArgumentException("feature == 0"); + } + if (supportsFeature(feature) != support) + { + if (support == true) + { + byte[] temp = new byte[data.length + 1]; + System.arraycopy(data, 0, temp, 0, data.length); + temp[data.length] = feature; + data = temp; + } + else + { + for (int i = 0; i < data.length; i++) + { + if (data[i] == feature) + { + byte[] temp = new byte[data.length - 1]; + System.arraycopy(data, 0, temp, 0, i); + System.arraycopy(data, i + 1, temp, i, temp.length - i); + data = temp; + break; + } + } + } + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/IssuerKeyID.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/IssuerKeyID.java new file mode 100644 index 000000000..b963b7974 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/IssuerKeyID.java @@ -0,0 +1,50 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving signature creation time. + */ +public class IssuerKeyID + extends SignatureSubpacket +{ + protected static byte[] keyIDToBytes( + long keyId) + { + byte[] data = new byte[8]; + + data[0] = (byte)(keyId >> 56); + data[1] = (byte)(keyId >> 48); + data[2] = (byte)(keyId >> 40); + data[3] = (byte)(keyId >> 32); + data[4] = (byte)(keyId >> 24); + data[5] = (byte)(keyId >> 16); + data[6] = (byte)(keyId >> 8); + data[7] = (byte)keyId; + + return data; + } + + public IssuerKeyID( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.ISSUER_KEY_ID, critical, data); + } + + public IssuerKeyID( + boolean critical, + long keyID) + { + super(SignatureSubpacketTags.ISSUER_KEY_ID, critical, keyIDToBytes(keyID)); + } + + public long getKeyID() + { + long keyID = ((long)(data[0] & 0xff) << 56) | ((long)(data[1] & 0xff) << 48) | ((long)(data[2] & 0xff) << 40) | ((long)(data[3] & 0xff) << 32) + | ((long)(data[4] & 0xff) << 24) | ((data[5] & 0xff) << 16) | ((data[6] & 0xff) << 8) | (data[7] & 0xff); + + return keyID; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyExpirationTime.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyExpirationTime.java new file mode 100644 index 000000000..dc0817c99 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyExpirationTime.java @@ -0,0 +1,50 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving time after creation at which the key expires. + */ +public class KeyExpirationTime + extends SignatureSubpacket +{ + protected static byte[] timeToBytes( + long t) + { + byte[] data = new byte[4]; + + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + + return data; + } + + public KeyExpirationTime( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.KEY_EXPIRE_TIME, critical, data); + } + + public KeyExpirationTime( + boolean critical, + long seconds) + { + super(SignatureSubpacketTags.KEY_EXPIRE_TIME, critical, timeToBytes(seconds)); + } + + /** + * Return the number of seconds after creation time a key is valid for. + * + * @return second count for key validity. + */ + public long getTime() + { + long time = ((long)(data[0] & 0xff) << 24) | ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff); + + return time; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyFlags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyFlags.java new file mode 100644 index 000000000..6ebb1fa87 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/KeyFlags.java @@ -0,0 +1,73 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * Packet holding the key flag values. + */ +public class KeyFlags + extends SignatureSubpacket +{ + public static final int CERTIFY_OTHER = 0x01; + public static final int SIGN_DATA = 0x02; + public static final int ENCRYPT_COMMS = 0x04; + public static final int ENCRYPT_STORAGE = 0x08; + public static final int SPLIT = 0x10; + public static final int AUTHENTICATION = 0x20; + public static final int SHARED = 0x80; + + private static byte[] intToByteArray( + int v) + { + byte[] tmp = new byte[4]; + int size = 0; + + for (int i = 0; i != 4; i++) + { + tmp[i] = (byte)(v >> (i * 8)); + if (tmp[i] != 0) + { + size = i; + } + } + + byte[] data = new byte[size + 1]; + + System.arraycopy(tmp, 0, data, 0, data.length); + + return data; + } + + public KeyFlags( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.KEY_FLAGS, critical, data); + } + + public KeyFlags( + boolean critical, + int flags) + { + super(SignatureSubpacketTags.KEY_FLAGS, critical, intToByteArray(flags)); + } + + /** + * Return the flag values contained in the first 4 octets (note: at the moment + * the standard only uses the first one). + * + * @return flag values. + */ + public int getFlags() + { + int flags = 0; + + for (int i = 0; i != data.length; i++) + { + flags |= (data[i] & 0xff) << (i * 8); + } + + return flags; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/NotationData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/NotationData.java new file mode 100644 index 000000000..d4b04323b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/NotationData.java @@ -0,0 +1,113 @@ +package org.spongycastle.bcpg.sig; + +import java.io.ByteArrayOutputStream; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.util.Strings; + +/** + * Class provided a NotationData object according to + * RFC2440, Chapter 5.2.3.15. Notation Data + */ +public class NotationData + extends SignatureSubpacket +{ + public static final int HEADER_FLAG_LENGTH = 4; + public static final int HEADER_NAME_LENGTH = 2; + public static final int HEADER_VALUE_LENGTH = 2; + + public NotationData(boolean critical, byte[] data) + { + super(SignatureSubpacketTags.NOTATION_DATA, critical, data); + } + + public NotationData( + boolean critical, + boolean humanReadable, + String notationName, + String notationValue) + { + super(SignatureSubpacketTags.NOTATION_DATA, critical, createData(humanReadable, notationName, notationValue)); + } + + private static byte[] createData(boolean humanReadable, String notationName, String notationValue) + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + +// (4 octets of flags, 2 octets of name length (M), +// 2 octets of value length (N), +// M octets of name data, +// N octets of value data) + + // flags + out.write(humanReadable ? 0x80 : 0x00); + out.write(0x0); + out.write(0x0); + out.write(0x0); + + byte[] nameData, valueData = null; + int nameLength, valueLength; + + nameData = Strings.toUTF8ByteArray(notationName); + nameLength = Math.min(nameData.length, 0xFFFF); + + if (nameLength != nameData.length) + { + throw new IllegalArgumentException("notationName exceeds maximum length."); + } + + valueData = Strings.toUTF8ByteArray(notationValue); + valueLength = Math.min(valueData.length, 0xFFFF); + if (valueLength != valueData.length) + { + throw new IllegalArgumentException("notationValue exceeds maximum length."); + } + + // name length + out.write((nameLength >>> 8) & 0xFF); + out.write((nameLength >>> 0) & 0xFF); + + // value length + out.write((valueLength >>> 8) & 0xFF); + out.write((valueLength >>> 0) & 0xFF); + + // name + out.write(nameData, 0, nameLength); + + // value + out.write(valueData, 0, valueLength); + + return out.toByteArray(); + } + + public boolean isHumanReadable() + { + return data[0] == (byte)0x80; + } + + public String getNotationName() + { + int nameLength = (((data[HEADER_FLAG_LENGTH] & 0xff) << 8) + (data[HEADER_FLAG_LENGTH + 1] & 0xff)); + + byte bName[] = new byte[nameLength]; + System.arraycopy(data, HEADER_FLAG_LENGTH + HEADER_NAME_LENGTH + HEADER_VALUE_LENGTH, bName, 0, nameLength); + + return Strings.fromUTF8ByteArray(bName); + } + + public String getNotationValue() + { + return Strings.fromUTF8ByteArray(getNotationValueBytes()); + } + + public byte[] getNotationValueBytes() + { + int nameLength = (((data[HEADER_FLAG_LENGTH] & 0xff) << 8) + (data[HEADER_FLAG_LENGTH + 1] & 0xff)); + int valueLength = (((data[HEADER_FLAG_LENGTH + HEADER_NAME_LENGTH] & 0xff) << 8) + (data[HEADER_FLAG_LENGTH + HEADER_NAME_LENGTH + 1] & 0xff)); + + byte bValue[] = new byte[valueLength]; + System.arraycopy(data, HEADER_FLAG_LENGTH + HEADER_NAME_LENGTH + HEADER_VALUE_LENGTH + nameLength, bValue, 0, valueLength); + return bValue; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PreferredAlgorithms.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PreferredAlgorithms.java new file mode 100644 index 000000000..ce99a1dc1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PreferredAlgorithms.java @@ -0,0 +1,59 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; + +/** + * packet giving signature creation time. + */ +public class PreferredAlgorithms + extends SignatureSubpacket +{ + private static byte[] intToByteArray( + int[] v) + { + byte[] data = new byte[v.length]; + + for (int i = 0; i != v.length; i++) + { + data[i] = (byte)v[i]; + } + + return data; + } + + public PreferredAlgorithms( + int type, + boolean critical, + byte[] data) + { + super(type, critical, data); + } + + public PreferredAlgorithms( + int type, + boolean critical, + int[] preferrences) + { + super(type, critical, intToByteArray(preferrences)); + } + + /** + * @deprecated mispelt! + */ + public int[] getPreferrences() + { + return getPreferences(); + } + + public int[] getPreferences() + { + int[] v = new int[data.length]; + + for (int i = 0; i != v.length; i++) + { + v[i] = data[i] & 0xff; + } + + return v; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PrimaryUserID.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PrimaryUserID.java new file mode 100644 index 000000000..80829ad88 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/PrimaryUserID.java @@ -0,0 +1,46 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving whether or not the signature is signed using the primary user ID for the key. + */ +public class PrimaryUserID + extends SignatureSubpacket +{ + private static byte[] booleanToByteArray( + boolean value) + { + byte[] data = new byte[1]; + + if (value) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public PrimaryUserID( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.PRIMARY_USER_ID, critical, data); + } + + public PrimaryUserID( + boolean critical, + boolean isPrimaryUserID) + { + super(SignatureSubpacketTags.PRIMARY_USER_ID, critical, booleanToByteArray(isPrimaryUserID)); + } + + public boolean isPrimaryUserID() + { + return data[0] != 0; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Revocable.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Revocable.java new file mode 100644 index 000000000..b982b0147 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/Revocable.java @@ -0,0 +1,46 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving whether or not is revocable. + */ +public class Revocable + extends SignatureSubpacket +{ + private static byte[] booleanToByteArray( + boolean value) + { + byte[] data = new byte[1]; + + if (value) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public Revocable( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.REVOCABLE, critical, data); + } + + public Revocable( + boolean critical, + boolean isRevocable) + { + super(SignatureSubpacketTags.REVOCABLE, critical, booleanToByteArray(isRevocable)); + } + + public boolean isRevocable() + { + return data[0] != 0; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKey.java new file mode 100644 index 000000000..b10593303 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKey.java @@ -0,0 +1,52 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * Represents revocation key OpenPGP signature sub packet. + */ +public class RevocationKey extends SignatureSubpacket +{ + // 1 octet of class, + // 1 octet of public-key algorithm ID, + // 20 octets of fingerprint + public RevocationKey(boolean isCritical, byte[] data) + { + super(SignatureSubpacketTags.REVOCATION_KEY, isCritical, data); + } + + public RevocationKey(boolean isCritical, byte signatureClass, int keyAlgorithm, + byte[] fingerprint) + { + super(SignatureSubpacketTags.REVOCATION_KEY, isCritical, createData(signatureClass, + (byte)(keyAlgorithm & 0xff), fingerprint)); + } + + private static byte[] createData(byte signatureClass, byte keyAlgorithm, byte[] fingerprint) + { + byte[] data = new byte[2 + fingerprint.length]; + data[0] = signatureClass; + data[1] = keyAlgorithm; + System.arraycopy(fingerprint, 0, data, 2, fingerprint.length); + return data; + } + + public byte getSignatureClass() + { + return this.getData()[0]; + } + + public int getAlgorithm() + { + return this.getData()[1]; + } + + public byte[] getFingerprint() + { + byte[] data = this.getData(); + byte[] fingerprint = new byte[data.length - 2]; + System.arraycopy(data, 2, fingerprint, 0, fingerprint.length); + return fingerprint; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKeyTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKeyTags.java new file mode 100644 index 000000000..294fdd34e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationKeyTags.java @@ -0,0 +1,8 @@ +package org.spongycastle.bcpg.sig; + +public interface RevocationKeyTags +{ + public static final byte CLASS_DEFAULT = (byte)0x80; + public static final byte CLASS_SENSITIVE = (byte)0x40; + +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReason.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReason.java new file mode 100644 index 000000000..33cf451d3 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReason.java @@ -0,0 +1,51 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.util.Strings; + +/** + * Represents revocation reason OpenPGP signature sub packet. + */ +public class RevocationReason extends SignatureSubpacket +{ + public RevocationReason(boolean isCritical, byte[] data) + { + super(SignatureSubpacketTags.REVOCATION_REASON, isCritical, data); + } + + public RevocationReason(boolean isCritical, byte reason, String description) + { + super(SignatureSubpacketTags.REVOCATION_REASON, isCritical, createData(reason, description)); + } + + private static byte[] createData(byte reason, String description) + { + byte[] descriptionBytes = Strings.toUTF8ByteArray(description); + byte[] data = new byte[1 + descriptionBytes.length]; + + data[0] = reason; + System.arraycopy(descriptionBytes, 0, data, 1, descriptionBytes.length); + + return data; + } + + public byte getRevocationReason() + { + return getData()[0]; + } + + public String getRevocationDescription() + { + byte[] data = getData(); + if (data.length == 1) + { + return ""; + } + + byte[] description = new byte[data.length - 1]; + System.arraycopy(data, 1, description, 0, description.length); + + return Strings.fromUTF8ByteArray(description); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReasonTags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReasonTags.java new file mode 100644 index 000000000..94233fb31 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/RevocationReasonTags.java @@ -0,0 +1,12 @@ +package org.spongycastle.bcpg.sig; + +public interface RevocationReasonTags +{ + public static final byte NO_REASON = 0; // No reason specified (key revocations or cert revocations) + public static final byte KEY_SUPERSEDED = 1; // Key is superseded (key revocations) + public static final byte KEY_COMPROMISED = 2; // Key material has been compromised (key revocations) + public static final byte KEY_RETIRED = 3; // Key is retired and no longer used (key revocations) + public static final byte USER_NO_LONGER_VALID = 32; // User ID information is no longer valid (cert revocations) + + // 100-110 - Private Use +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureCreationTime.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureCreationTime.java new file mode 100644 index 000000000..888cb9a9f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureCreationTime.java @@ -0,0 +1,48 @@ +package org.spongycastle.bcpg.sig; + +import java.util.Date; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving signature creation time. + */ +public class SignatureCreationTime + extends SignatureSubpacket +{ + protected static byte[] timeToBytes( + Date date) + { + byte[] data = new byte[4]; + long t = date.getTime() / 1000; + + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + + return data; + } + + public SignatureCreationTime( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.CREATION_TIME, critical, data); + } + + public SignatureCreationTime( + boolean critical, + Date date) + { + super(SignatureSubpacketTags.CREATION_TIME, critical, timeToBytes(date)); + } + + public Date getTime() + { + long time = ((long)(data[0] & 0xff) << 24) | ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff); + + return new Date(time * 1000); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureExpirationTime.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureExpirationTime.java new file mode 100644 index 000000000..bcf1444dc --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignatureExpirationTime.java @@ -0,0 +1,48 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving signature expiration time. + */ +public class SignatureExpirationTime + extends SignatureSubpacket +{ + protected static byte[] timeToBytes( + long t) + { + byte[] data = new byte[4]; + + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + + return data; + } + + public SignatureExpirationTime( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.EXPIRE_TIME, critical, data); + } + + public SignatureExpirationTime( + boolean critical, + long seconds) + { + super(SignatureSubpacketTags.EXPIRE_TIME, critical, timeToBytes(seconds)); + } + + /** + * return time in seconds before signature expires after creation time. + */ + public long getTime() + { + long time = ((long)(data[0] & 0xff) << 24) | ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff); + + return time; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignerUserID.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignerUserID.java new file mode 100644 index 000000000..c0cb27865 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/SignerUserID.java @@ -0,0 +1,50 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving the User ID of the signer. + */ +public class SignerUserID + extends SignatureSubpacket +{ + private static byte[] userIDToBytes( + String id) + { + byte[] idData = new byte[id.length()]; + + for (int i = 0; i != id.length(); i++) + { + idData[i] = (byte)id.charAt(i); + } + + return idData; + } + + public SignerUserID( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.SIGNER_USER_ID, critical, data); + } + + public SignerUserID( + boolean critical, + String userID) + { + super(SignatureSubpacketTags.SIGNER_USER_ID, critical, userIDToBytes(userID)); + } + + public String getID() + { + char[] chars = new char[data.length]; + + for (int i = 0; i != chars.length; i++) + { + chars[i] = (char)(data[i] & 0xff); + } + + return new String(chars); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/TrustSignature.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/TrustSignature.java new file mode 100644 index 000000000..4555f2223 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/bcpg/sig/TrustSignature.java @@ -0,0 +1,48 @@ +package org.spongycastle.bcpg.sig; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; + +/** + * packet giving trust. + */ +public class TrustSignature + extends SignatureSubpacket +{ + private static byte[] intToByteArray( + int v1, + int v2) + { + byte[] data = new byte[2]; + + data[0] = (byte)v1; + data[1] = (byte)v2; + + return data; + } + + public TrustSignature( + boolean critical, + byte[] data) + { + super(SignatureSubpacketTags.TRUST_SIG, critical, data); + } + + public TrustSignature( + boolean critical, + int depth, + int trustAmount) + { + super(SignatureSubpacketTags.TRUST_SIG, critical, intToByteArray(depth, trustAmount)); + } + + public int getDepth() + { + return data[0] & 0xff; + } + + public int getTrustAmount() + { + return data[1] & 0xff; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPAlgorithmParameters.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPAlgorithmParameters.java new file mode 100644 index 000000000..a9628afbb --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPAlgorithmParameters.java @@ -0,0 +1,5 @@ +package org.spongycastle.openpgp; + +public interface PGPAlgorithmParameters +{ +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedData.java new file mode 100644 index 000000000..36e684416 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedData.java @@ -0,0 +1,143 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.CompressedDataPacket; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.apache.bzip2.CBZip2InputStream; + +/** + * Compressed data objects. + */ +public class PGPCompressedData + implements CompressionAlgorithmTags +{ + CompressedDataPacket data; + + public PGPCompressedData( + BCPGInputStream pIn) + throws IOException + { + data = (CompressedDataPacket)pIn.readPacket(); + } + + /** + * Return the algorithm used for compression + * + * @return algorithm code + */ + public int getAlgorithm() + { + return data.getAlgorithm(); + } + + /** + * Return the raw input stream contained in the object. + * + * @return InputStream + */ + public InputStream getInputStream() + { + return data.getInputStream(); + } + + /** + * Return an uncompressed input stream which allows reading of the + * compressed data. + * + * @return InputStream + * @throws PGPException + */ + public InputStream getDataStream() + throws PGPException + { + if (this.getAlgorithm() == UNCOMPRESSED) + { + return this.getInputStream(); + } + if (this.getAlgorithm() == ZIP) + { + return new InflaterInputStream(this.getInputStream(), new Inflater(true)) + { + // If the "nowrap" inflater option is used the stream can + // apparently overread - we override fill() and provide + // an extra byte for the end of the input stream to get + // around this. + // + // Totally weird... + // + protected void fill() throws IOException + { + if (eof) + { + throw new EOFException("Unexpected end of ZIP input stream"); + } + + len = this.in.read(buf, 0, buf.length); + + if (len == -1) + { + buf[0] = 0; + len = 1; + eof = true; + } + + inf.setInput(buf, 0, len); + } + + private boolean eof = false; + }; + } + if (this.getAlgorithm() == ZLIB) + { + return new InflaterInputStream(this.getInputStream()) + { + // If the "nowrap" inflater option is used the stream can + // apparently overread - we override fill() and provide + // an extra byte for the end of the input stream to get + // around this. + // + // Totally weird... + // + protected void fill() throws IOException + { + if (eof) + { + throw new EOFException("Unexpected end of ZIP input stream"); + } + + len = this.in.read(buf, 0, buf.length); + + if (len == -1) + { + buf[0] = 0; + len = 1; + eof = true; + } + + inf.setInput(buf, 0, len); + } + + private boolean eof = false; + }; + } + if (this.getAlgorithm() == BZIP2) + { + try + { + return new CBZip2InputStream(this.getInputStream()); + } + catch (IOException e) + { + throw new PGPException("I/O problem with stream: " + e, e); + } + } + + throw new PGPException("can't recognise compression algorithm: " + this.getAlgorithm()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedDataGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedDataGenerator.java new file mode 100644 index 000000000..c0e8c883d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPCompressedDataGenerator.java @@ -0,0 +1,201 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.apache.bzip2.CBZip2OutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.PacketTags; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + *class for producing compressed data packets. + */ +public class PGPCompressedDataGenerator + implements CompressionAlgorithmTags, StreamGenerator +{ + private int algorithm; + private int compression; + + private OutputStream dOut; + private BCPGOutputStream pkOut; + + public PGPCompressedDataGenerator( + int algorithm) + { + this(algorithm, Deflater.DEFAULT_COMPRESSION); + } + + public PGPCompressedDataGenerator( + int algorithm, + int compression) + { + switch (algorithm) + { + case CompressionAlgorithmTags.UNCOMPRESSED: + case CompressionAlgorithmTags.ZIP: + case CompressionAlgorithmTags.ZLIB: + case CompressionAlgorithmTags.BZIP2: + break; + default: + throw new IllegalArgumentException("unknown compression algorithm"); + } + + if (compression != Deflater.DEFAULT_COMPRESSION) + { + if ((compression < Deflater.NO_COMPRESSION) || (compression > Deflater.BEST_COMPRESSION)) + { + throw new IllegalArgumentException("unknown compression level: " + compression); + } + } + + this.algorithm = algorithm; + this.compression = compression; + } + + /** + * Return an OutputStream which will save the data being written to + * the compressed object. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out underlying OutputStream to be used. + * @return OutputStream + * @throws IOException, IllegalStateException + */ + public OutputStream open( + OutputStream out) + throws IOException + { + if (dOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + this.pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA); + + doOpen(); + + return new WrappedGeneratorStream(dOut, this); + } + + /** + * Return an OutputStream which will compress the data as it is written + * to it. The stream will be written out in chunks according to the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * </p> + * <p> + * <b>Note</b>: using this may break compatibility with RFC 1991 compliant tools. Only recent OpenPGP + * implementations are capable of accepting these streams. + * </p> + * + * @param out underlying OutputStream to be used. + * @param buffer the buffer to use. + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + byte[] buffer) + throws IOException, PGPException + { + if (dOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + this.pkOut = new BCPGOutputStream(out, PacketTags.COMPRESSED_DATA, buffer); + + doOpen(); + + return new WrappedGeneratorStream(dOut, this); + } + + private void doOpen() throws IOException + { + pkOut.write(algorithm); + + switch (algorithm) + { + case CompressionAlgorithmTags.UNCOMPRESSED: + dOut = pkOut; + break; + case CompressionAlgorithmTags.ZIP: + dOut = new SafeDeflaterOutputStream(pkOut, compression, true); + break; + case CompressionAlgorithmTags.ZLIB: + dOut = new SafeDeflaterOutputStream(pkOut, compression, false); + break; + case CompressionAlgorithmTags.BZIP2: + dOut = new SafeCBZip2OutputStream(pkOut); + break; + default: + // Constructor should guard against this possibility + throw new IllegalStateException(); + } + } + + /** + * Close the compressed object - this is equivalent to calling close on the stream + * returned by the open() method. + * + * @throws IOException + */ + public void close() + throws IOException + { + if (dOut != null) + { + if (dOut != pkOut) + { + dOut.close(); + dOut.flush(); + } + + dOut = null; + + pkOut.finish(); + pkOut.flush(); + pkOut = null; + } + } + + private static class SafeCBZip2OutputStream extends CBZip2OutputStream + { + public SafeCBZip2OutputStream(OutputStream output) throws IOException + { + super(output); + } + + public void close() throws IOException + { + finish(); + } + } + + private class SafeDeflaterOutputStream extends DeflaterOutputStream + { + public SafeDeflaterOutputStream(OutputStream output, int compression, boolean nowrap) + { + super(output, new Deflater(compression, nowrap)); + } + + public void close() throws IOException + { + finish(); + def.end(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPDataValidationException.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPDataValidationException.java new file mode 100644 index 000000000..1b6fb10b7 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPDataValidationException.java @@ -0,0 +1,17 @@ +package org.spongycastle.openpgp; + +/** + * Thrown if the iv at the start of a data stream indicates the wrong key + * is being used. + */ +public class PGPDataValidationException + extends PGPException +{ + /** + * @param message + */ + public PGPDataValidationException(String message) + { + super(message); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedData.java new file mode 100644 index 000000000..7ff398101 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedData.java @@ -0,0 +1,147 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.util.Arrays; + +public abstract class PGPEncryptedData + implements SymmetricKeyAlgorithmTags +{ + protected class TruncatedStream extends InputStream + { + int[] lookAhead = new int[22]; + int bufPtr; + InputStream in; + + TruncatedStream( + InputStream in) + throws IOException + { + for (int i = 0; i != lookAhead.length; i++) + { + if ((lookAhead[i] = in.read()) < 0) + { + throw new EOFException(); + } + } + + bufPtr = 0; + this.in = in; + } + + public int read() + throws IOException + { + int ch = in.read(); + + if (ch >= 0) + { + int c = lookAhead[bufPtr]; + + lookAhead[bufPtr] = ch; + bufPtr = (bufPtr + 1) % lookAhead.length; + + return c; + } + + return -1; + } + + int[] getLookAhead() + { + int[] tmp = new int[lookAhead.length]; + int count = 0; + + for (int i = bufPtr; i != lookAhead.length; i++) + { + tmp[count++] = lookAhead[i]; + } + for (int i = 0; i != bufPtr; i++) + { + tmp[count++] = lookAhead[i]; + } + + return tmp; + } + } + + InputStreamPacket encData; + InputStream encStream; + TruncatedStream truncStream; + PGPDigestCalculator integrityCalculator; + + PGPEncryptedData( + InputStreamPacket encData) + { + this.encData = encData; + } + + /** + * Return the raw input stream for the data stream. + * + * @return InputStream + */ + public InputStream getInputStream() + { + return encData.getInputStream(); + } + + /** + * Return true if the message is integrity protected. + * @return true if there is a modification detection code package associated with this stream + */ + public boolean isIntegrityProtected() + { + return (encData instanceof SymmetricEncIntegrityPacket); + } + + /** + * Note: This can only be called after the message has been read. + * + * @return true if the message verifies, false otherwise. + * @throws PGPException if the message is not integrity protected. + */ + public boolean verify() + throws PGPException, IOException + { + if (!this.isIntegrityProtected()) + { + throw new PGPException("data not integrity protected."); + } + + // + // make sure we are at the end. + // + while (encStream.read() >= 0) + { + // do nothing + } + + // + // process the MDC packet + // + int[] lookAhead = truncStream.getLookAhead(); + + OutputStream dOut = integrityCalculator.getOutputStream(); + + dOut.write((byte)lookAhead[0]); + dOut.write((byte)lookAhead[1]); + + byte[] digest = integrityCalculator.getDigest(); + byte[] streamDigest = new byte[digest.length]; + + for (int i = 0; i != streamDigest.length; i++) + { + streamDigest[i] = (byte)lookAhead[i + 2]; + } + + return Arrays.constantTimeAreEqual(digest, streamDigest); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java new file mode 100644 index 000000000..9f816e392 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataGenerator.java @@ -0,0 +1,537 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.PGPDataEncryptor; +import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.TeeOutputStream; + +/** + * Generator for encrypted objects. + */ +public class PGPEncryptedDataGenerator + implements SymmetricKeyAlgorithmTags, StreamGenerator +{ + /** + * Specifier for SHA-1 S2K PBE generator. + */ + public static final int S2K_SHA1 = HashAlgorithmTags.SHA1; + + /** + * Specifier for SHA-224 S2K PBE generator. + */ + public static final int S2K_SHA224 = HashAlgorithmTags.SHA224; + + /** + * Specifier for SHA-256 S2K PBE generator. + */ + public static final int S2K_SHA256 = HashAlgorithmTags.SHA256; + + /** + * Specifier for SHA-384 S2K PBE generator. + */ + public static final int S2K_SHA384 = HashAlgorithmTags.SHA384; + + /** + * Specifier for SHA-512 S2K PBE generator. + */ + public static final int S2K_SHA512 = HashAlgorithmTags.SHA512; + + private BCPGOutputStream pOut; + private OutputStream cOut; + private boolean oldFormat = false; + private PGPDigestCalculator digestCalc; + private OutputStream genOut; + private PGPDataEncryptorBuilder dataEncryptorBuilder; + + private List methods = new ArrayList(); + private int defAlgorithm; + private SecureRandom rand; + + private static Provider defProvider; + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param provider the provider name to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptor + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Creates a cipher stream which will have an integrity packet + * associated with it. + * + * @param encAlgorithm + * @param withIntegrityPacket + * @param rand + * @param provider + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + boolean withIntegrityPacket, + SecureRandom rand, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setWithIntegrityPacket(withIntegrityPacket).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Creates a cipher stream which will have an integrity packet + * associated with it. + * + * @param encAlgorithm + * @param withIntegrityPacket + * @param rand + * @param provider + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + boolean withIntegrityPacket, + SecureRandom rand, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setWithIntegrityPacket(withIntegrityPacket).setSecureRandom(rand).setProvider(provider)); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param oldFormat PGP 2.6.x compatibility required. + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + boolean oldFormat, + String provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider), oldFormat); + } + + /** + * Base constructor. + * + * @param encAlgorithm the symmetric algorithm to use. + * @param rand source of randomness + * @param oldFormat PGP 2.6.x compatibility required. + * @param provider the provider to use for encryption algorithms. + * @deprecated use constructor that takes a PGPDataEncryptorBuilder + */ + public PGPEncryptedDataGenerator( + int encAlgorithm, + SecureRandom rand, + boolean oldFormat, + Provider provider) + { + this(new JcePGPDataEncryptorBuilder(encAlgorithm).setSecureRandom(rand).setProvider(provider), oldFormat); + } + + /** + * Base constructor. + * + * @param encryptorBuilder builder to create actual data encryptor. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder) + { + this(encryptorBuilder, false); + } + + /** + * Base constructor with the option to turn on formatting for PGP 2.6.x compatibility. + * + * @param encryptorBuilder builder to create actual data encryptor. + * @param oldFormat PGP 2.6.x compatibility required. + */ + public PGPEncryptedDataGenerator(PGPDataEncryptorBuilder encryptorBuilder, boolean oldFormat) + { + this.dataEncryptorBuilder = encryptorBuilder; + this.oldFormat = oldFormat; + + this.defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + this.rand = dataEncryptorBuilder.getSecureRandom(); + } + + /** + * Add a PBE encryption method to the encrypted object using the default algorithm (S2K_SHA1). + * + * @param passPhrase + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + char[] passPhrase) + throws NoSuchProviderException, PGPException + { + addMethod(passPhrase, HashAlgorithmTags.SHA1); + } + + /** + * Add a PBE encryption method to the encrypted object. + * + * @param passPhrase passphrase to use to generate key. + * @param s2kDigest digest algorithm to use for S2K calculation + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + char[] passPhrase, + int s2kDigest) + throws NoSuchProviderException, PGPException + { + if (defProvider == null) + { + defProvider = new BouncyCastleProvider(); + } + + addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase, new JcaPGPDigestCalculatorProviderBuilder().setProvider(defProvider).build().get(s2kDigest)).setProvider(defProvider).setSecureRandom(rand)); + } + + /** + * Add a public key encrypted session key to the encrypted object. + * + * @param key + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use addMethod that takes PGPKeyEncryptionMethodGenerator + */ + public void addMethod( + PGPPublicKey key) + throws NoSuchProviderException, PGPException + { + if (!key.isEncryptionKey()) + { + throw new IllegalArgumentException("passed in key not an encryption key!"); + } + + if (defProvider == null) + { + defProvider = new BouncyCastleProvider(); + } + + addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(key).setProvider(defProvider).setSecureRandom(rand)); + } + + /** + * Added a key encryption method to be used to encrypt the session data associated + * with this encrypted data. + * + * @param method key encryption method to use. + */ + public void addMethod(PGPKeyEncryptionMethodGenerator method) + { + methods.add(method); + } + + private void addCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8); + sessionInfo[sessionInfo.length - 1] = (byte)(check); + } + + private byte[] createSessionInfo( + int algorithm, + byte[] keyBytes) + { + byte[] sessionInfo = new byte[keyBytes.length + 3]; + sessionInfo[0] = (byte) algorithm; + System.arraycopy(keyBytes, 0, sessionInfo, 1, keyBytes.length); + addCheckSum(sessionInfo); + return sessionInfo; + } + + /** + * If buffer is non null stream assumed to be partial, otherwise the + * length will be used to output a fixed length packet. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @param buffer + * @return + * @throws java.io.IOException + * @throws PGPException + * @throws IllegalStateException + */ + private OutputStream open( + OutputStream out, + long length, + byte[] buffer) + throws IOException, PGPException, IllegalStateException + { + if (cOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + if (methods.size() == 0) + { + throw new IllegalStateException("no encryption methods specified"); + } + + byte[] key = null; + + pOut = new BCPGOutputStream(out); + + defAlgorithm = dataEncryptorBuilder.getAlgorithm(); + rand = dataEncryptorBuilder.getSecureRandom(); + + if (methods.size() == 1) + { + + if (methods.get(0) instanceof PBEKeyEncryptionMethodGenerator) + { + PBEKeyEncryptionMethodGenerator m = (PBEKeyEncryptionMethodGenerator)methods.get(0); + + key = m.getKey(dataEncryptorBuilder.getAlgorithm()); + + pOut.writePacket(((PGPKeyEncryptionMethodGenerator)methods.get(0)).generate(defAlgorithm, null)); + } + else + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(0); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + else // multiple methods + { + key = PGPUtil.makeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = createSessionInfo(defAlgorithm, key); + + for (int i = 0; i != methods.size(); i++) + { + PGPKeyEncryptionMethodGenerator m = (PGPKeyEncryptionMethodGenerator)methods.get(i); + + pOut.writePacket(m.generate(defAlgorithm, sessionInfo)); + } + } + + try + { + PGPDataEncryptor dataEncryptor = dataEncryptorBuilder.build(key); + + digestCalc = dataEncryptor.getIntegrityCalculator(); + + if (buffer == null) + { + // + // we have to add block size + 2 for the generated IV and + 1 + 22 if integrity protected + // + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, length + dataEncryptor.getBlockSize() + 2 + 1 + 22); + + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, length + dataEncryptor.getBlockSize() + 2, oldFormat); + } + } + else + { + if (digestCalc != null) + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYM_ENC_INTEGRITY_PRO, buffer); + pOut.write(1); // version number + } + else + { + pOut = new ClosableBCPGOutputStream(out, PacketTags.SYMMETRIC_KEY_ENC, buffer); + } + } + + genOut = cOut = dataEncryptor.getOutputStream(pOut); + + if (digestCalc != null) + { + genOut = new TeeOutputStream(digestCalc.getOutputStream(), cOut); + } + + byte[] inLineIv = new byte[dataEncryptor.getBlockSize() + 2]; + rand.nextBytes(inLineIv); + inLineIv[inLineIv.length - 1] = inLineIv[inLineIv.length - 3]; + inLineIv[inLineIv.length - 2] = inLineIv[inLineIv.length - 4]; + + genOut.write(inLineIv); + + return new WrappedGeneratorStream(genOut, this); + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param length + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + long length) + throws IOException, PGPException + { + return this.open(out, length, null); + } + + /** + * Return an outputstream which will encrypt the data as it is written + * to it. The stream will be written out in chunks according to the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * + * @param out + * @param buffer the buffer to use. + * @return OutputStream + * @throws IOException + * @throws PGPException + */ + public OutputStream open( + OutputStream out, + byte[] buffer) + throws IOException, PGPException + { + return this.open(out, 0, buffer); + } + + /** + * Close off the encrypted object - this is equivalent to calling close on the stream + * returned by the open() method. + * <p> + * <b>Note</b>: This does not close the underlying output stream, only the stream on top of it created by the open() method. + * @throws java.io.IOException + */ + public void close() + throws IOException + { + if (cOut != null) + { + if (digestCalc != null) + { + // + // hand code a mod detection packet + // + BCPGOutputStream bOut = new BCPGOutputStream(genOut, PacketTags.MOD_DETECTION_CODE, 20); + + bOut.flush(); + + byte[] dig = digestCalc.getDigest(); + + cOut.write(dig); + } + + cOut.close(); + + cOut = null; + pOut = null; + } + } + + private class ClosableBCPGOutputStream + extends BCPGOutputStream + { + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, byte[] buffer) + throws IOException + { + super(out, symmetricKeyEnc, buffer); + } + + public ClosableBCPGOutputStream(OutputStream out, int symmetricKeyEnc, long length, boolean oldFormat) + throws IOException + { + super(out, symmetricKeyEnc, length, oldFormat); + } + + public ClosableBCPGOutputStream(OutputStream out, int symEncIntegrityPro, long length) + throws IOException + { + super(out, symEncIntegrityPro, length); + } + + public void close() + throws IOException + { + this.finish(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataList.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataList.java new file mode 100644 index 000000000..8fec479e6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPEncryptedDataList.java @@ -0,0 +1,75 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.PublicKeyEncSessionPacket; +import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket; + +/** + * A holder for a list of PGP encryption method packets. + */ +public class PGPEncryptedDataList +{ + List list = new ArrayList(); + InputStreamPacket data; + + public PGPEncryptedDataList( + BCPGInputStream pIn) + throws IOException + { + while (pIn.nextPacketTag() == PacketTags.PUBLIC_KEY_ENC_SESSION + || pIn.nextPacketTag() == PacketTags.SYMMETRIC_KEY_ENC_SESSION) + { + list.add(pIn.readPacket()); + } + + data = (InputStreamPacket)pIn.readPacket(); + + for (int i = 0; i != list.size(); i++) + { + if (list.get(i) instanceof SymmetricKeyEncSessionPacket) + { + list.set(i, new PGPPBEEncryptedData((SymmetricKeyEncSessionPacket)list.get(i), data)); + } + else + { + list.set(i, new PGPPublicKeyEncryptedData((PublicKeyEncSessionPacket)list.get(i), data)); + } + } + } + + public Object get( + int index) + { + return list.get(index); + } + + public int size() + { + return list.size(); + } + + public boolean isEmpty() + { + return list.isEmpty(); + } + + /** + * @deprecated misspelt - use getEncryptedDataObjects() + */ + public Iterator getEncyptedDataObjects() + { + return list.iterator(); + } + + public Iterator getEncryptedDataObjects() + { + return list.iterator(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPException.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPException.java new file mode 100644 index 000000000..220672ac6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPException.java @@ -0,0 +1,35 @@ +package org.spongycastle.openpgp; + +/** + * generic exception class for PGP encoding/decoding problems + */ +public class PGPException + extends Exception +{ + Exception underlying; + + public PGPException( + String message) + { + super(message); + } + + public PGPException( + String message, + Exception underlying) + { + super(message); + this.underlying = underlying; + } + + public Exception getUnderlyingException() + { + return underlying; + } + + + public Throwable getCause() + { + return underlying; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKdfParameters.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKdfParameters.java new file mode 100644 index 000000000..fc37af74b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKdfParameters.java @@ -0,0 +1,24 @@ +package org.spongycastle.openpgp; + +public class PGPKdfParameters + implements PGPAlgorithmParameters +{ + private final int hashAlgorithm; + private final int symmetricWrapAlgorithm; + + public PGPKdfParameters(int hashAlgorithm, int symmetricWrapAlgorithm) + { + this.hashAlgorithm = hashAlgorithm; + this.symmetricWrapAlgorithm = symmetricWrapAlgorithm; + } + + public int getSymmetricWrapAlgorithm() + { + return symmetricWrapAlgorithm; + } + + public int getHashAlgorithm() + { + return hashAlgorithm; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyFlags.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyFlags.java new file mode 100644 index 000000000..2c1691268 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyFlags.java @@ -0,0 +1,19 @@ +package org.spongycastle.openpgp; + +/** + * key flag values for the KeyFlags subpacket. + */ +public interface PGPKeyFlags +{ + public static final int CAN_CERTIFY = 0x01; // This key may be used to certify other keys. + + public static final int CAN_SIGN = 0x02; // This key may be used to sign data. + + public static final int CAN_ENCRYPT_COMMS = 0x04; // This key may be used to encrypt communications. + + public static final int CAN_ENCRYPT_STORAGE = 0x08; // This key may be used to encrypt storage. + + public static final int MAYBE_SPLIT = 0x10; // The private component of this key may have been split by a secret-sharing mechanism. + + public static final int MAYBE_SHARED = 0x80; // The private component of this key may be in the possession of more than one person. +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyPair.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyPair.java new file mode 100644 index 000000000..0b430c71d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyPair.java @@ -0,0 +1,148 @@ +package org.spongycastle.openpgp; + +import java.security.KeyPair; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.util.Date; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; + + +/** + * General class to handle JCA key pairs and convert them into OpenPGP ones. + * <p> + * A word for the unwary, the KeyID for a OpenPGP public key is calculated from + * a hash that includes the time of creation, if you pass a different date to the + * constructor below with the same public private key pair the KeyID will not be the + * same as for previous generations of the key, so ideally you only want to do + * this once. + */ +public class PGPKeyPair +{ + protected PGPPublicKey pub; + protected PGPPrivateKey priv; + + /** + * @deprecated use BcPGPKeyPair or JcaPGPKeyPair as appropriate. + */ + public PGPKeyPair( + int algorithm, + KeyPair keyPair, + Date time, + String provider) + throws PGPException, NoSuchProviderException + { + this(algorithm, keyPair.getPublic(), keyPair.getPrivate(), time, provider); + } + + /** + * @deprecated use BcPGPKeyPair or JcaPGPKeyPair as appropriate. + */ + public PGPKeyPair( + int algorithm, + KeyPair keyPair, + Date time) + throws PGPException + { + this(algorithm, keyPair.getPublic(), keyPair.getPrivate(), time); + } + + /** + * @deprecated use BcPGPKeyPair or JcaPGPKeyPair as appropriate. + */ + public PGPKeyPair( + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time, + String provider) + throws PGPException, NoSuchProviderException + { + this(algorithm, pubKey, privKey, time); + } + + /** + * @deprecated use BcPGPKeyPair or JcaPGPKeyPair as appropriate. + */ + public PGPKeyPair( + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time) + throws PGPException + { + this.pub = new PGPPublicKey(algorithm, pubKey, time); + + BCPGKey privPk; + + switch (pub.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_SIGN: + case PGPPublicKey.RSA_GENERAL: + RSAPrivateCrtKey rsK = (RSAPrivateCrtKey)privKey; + + privPk = new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ()); + break; + case PGPPublicKey.DSA: + DSAPrivateKey dsK = (DSAPrivateKey)privKey; + + privPk = new DSASecretBCPGKey(dsK.getX()); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPrivateKey esK = (ElGamalPrivateKey)privKey; + + privPk = new ElGamalSecretBCPGKey(esK.getX()); + break; + default: + throw new PGPException("unknown key class"); + } + this.priv = new PGPPrivateKey(pub.getKeyID(), pub.getPublicKeyPacket(), privPk); + } + + /** + * Create a key pair from a PGPPrivateKey and a PGPPublicKey. + * + * @param pub the public key + * @param priv the private key + */ + public PGPKeyPair( + PGPPublicKey pub, + PGPPrivateKey priv) + { + this.pub = pub; + this.priv = priv; + } + + protected PGPKeyPair() + { + } + + /** + * Return the keyID associated with this key pair. + * + * @return keyID + */ + public long getKeyID() + { + return pub.getKeyID(); + } + + public PGPPublicKey getPublicKey() + { + return pub; + } + + public PGPPrivateKey getPrivateKey() + { + return priv; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRing.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRing.java new file mode 100644 index 000000000..50afce961 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRing.java @@ -0,0 +1,125 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.Packet; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserIDPacket; + +public abstract class PGPKeyRing +{ + PGPKeyRing() + { + } + + static BCPGInputStream wrap(InputStream in) + { + if (in instanceof BCPGInputStream) + { + return (BCPGInputStream)in; + } + + return new BCPGInputStream(in); + } + + static TrustPacket readOptionalTrustPacket( + BCPGInputStream pIn) + throws IOException + { + return (pIn.nextPacketTag() == PacketTags.TRUST) + ? (TrustPacket) pIn.readPacket() + : null; + } + + static List readSignaturesAndTrust( + BCPGInputStream pIn) + throws IOException + { + try + { + List sigList = new ArrayList(); + + while (pIn.nextPacketTag() == PacketTags.SIGNATURE) + { + SignaturePacket signaturePacket = (SignaturePacket)pIn.readPacket(); + TrustPacket trustPacket = readOptionalTrustPacket(pIn); + + sigList.add(new PGPSignature(signaturePacket, trustPacket)); + } + + return sigList; + } + catch (PGPException e) + { + throw new IOException("can't create signature object: " + e.getMessage() + + ", cause: " + e.getUnderlyingException().toString()); + } + } + + static void readUserIDs( + BCPGInputStream pIn, + List ids, + List idTrusts, + List idSigs) + throws IOException + { + while (pIn.nextPacketTag() == PacketTags.USER_ID + || pIn.nextPacketTag() == PacketTags.USER_ATTRIBUTE) + { + Packet obj = pIn.readPacket(); + if (obj instanceof UserIDPacket) + { + UserIDPacket id = (UserIDPacket)obj; + ids.add(id.getID()); + } + else + { + UserAttributePacket user = (UserAttributePacket)obj; + ids.add(new PGPUserAttributeSubpacketVector(user.getSubpackets())); + } + + idTrusts.add(readOptionalTrustPacket(pIn)); + idSigs.add(readSignaturesAndTrust(pIn)); + } + } + + /** + * Return the first public key in the ring. In the case of a {@link PGPSecretKeyRing} + * this is also the public key of the master key pair. + * + * @return PGPPublicKey + */ + public abstract PGPPublicKey getPublicKey(); + + /** + * Return an iterator containing all the public keys. + * + * @return Iterator + */ + public abstract Iterator getPublicKeys(); + + /** + * Return the public key referred to by the passed in keyID if it + * is present. + * + * @param keyID + * @return PGPPublicKey + */ + public abstract PGPPublicKey getPublicKey(long keyID); + + public abstract void encode(OutputStream outStream) + throws IOException; + + public abstract byte[] getEncoded() + throws IOException; + +}
\ No newline at end of file diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRingGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRingGenerator.java new file mode 100644 index 000000000..dc3fae711 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyRingGenerator.java @@ -0,0 +1,272 @@ +package org.spongycastle.openpgp; + +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicSubkeyPacket; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * Generator for a PGP master and subkey ring. This class will generate + * both the secret and public key rings + */ +public class PGPKeyRingGenerator +{ + List keys = new ArrayList(); + + private PBESecretKeyEncryptor keyEncryptor; + private PGPDigestCalculator checksumCalculator; + private PGPKeyPair masterKey; + private PGPSignatureSubpacketVector hashedPcks; + private PGPSignatureSubpacketVector unhashedPcks; + private PGPContentSignerBuilder keySignerBuilder; + + /** + * Create a new key ring generator using old style checksumming. It is recommended to use + * SHA1 checksumming where possible. + * + * @param certificationLevel the certification level for keys on this ring. + * @param masterKey the master key pair. + * @param id the id to be associated with the ring. + * @param encAlgorithm the algorithm to be used to protect secret keys. + * @param passPhrase the passPhrase to be used to protect secret keys. + * @param hashedPcks packets to be included in the certification hash. + * @param unhashedPcks packets to be attached unhashed to the certification. + * @param rand input secured random + * @param provider the provider to use for encryption. + * + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method taking PBESecretKeyDecryptor + */ + public PGPKeyRingGenerator( + int certificationLevel, + PGPKeyPair masterKey, + String id, + int encAlgorithm, + char[] passPhrase, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, false, hashedPcks, unhashedPcks, rand, provider); + } + + /** + * Create a new key ring generator. + * + * @param certificationLevel the certification level for keys on this ring. + * @param masterKey the master key pair. + * @param id the id to be associated with the ring. + * @param encAlgorithm the algorithm to be used to protect secret keys. + * @param passPhrase the passPhrase to be used to protect secret keys. + * @param useSHA1 checksum the secret keys with SHA1 rather than the older 16 bit checksum. + * @param hashedPcks packets to be included in the certification hash. + * @param unhashedPcks packets to be attached unhashed to the certification. + * @param rand input secured random + * @param provider the provider to use for encryption. + * + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method taking PBESecretKeyDecryptor + */ + public PGPKeyRingGenerator( + int certificationLevel, + PGPKeyPair masterKey, + String id, + int encAlgorithm, + char[] passPhrase, + boolean useSHA1, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSHA1, hashedPcks, unhashedPcks, rand, PGPUtil.getProvider(provider)); + } + + /** + * Create a new key ring generator. + * + * @param certificationLevel the certification level for keys on this ring. + * @param masterKey the master key pair. + * @param id the id to be associated with the ring. + * @param encAlgorithm the algorithm to be used to protect secret keys. + * @param passPhrase the passPhrase to be used to protect secret keys. + * @param useSHA1 checksum the secret keys with SHA1 rather than the older 16 bit checksum. + * @param hashedPcks packets to be included in the certification hash. + * @param unhashedPcks packets to be attached unhashed to the certification. + * @param rand input secured random + * @param provider the provider to use for encryption. + * + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPKeyRingGenerator( + int certificationLevel, + PGPKeyPair masterKey, + String id, + int encAlgorithm, + char[] passPhrase, + boolean useSHA1, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + Provider provider) + throws PGPException, NoSuchProviderException + { + this.masterKey = masterKey; + this.hashedPcks = hashedPcks; + this.unhashedPcks = unhashedPcks; + this.keyEncryptor = new JcePBESecretKeyEncryptorBuilder(encAlgorithm).setProvider(provider).setSecureRandom(rand).build(passPhrase); + this.checksumCalculator = convertSHA1Flag(useSHA1); + this.keySignerBuilder = new JcaPGPContentSignerBuilder(masterKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + keys.add(new PGPSecretKey(certificationLevel, masterKey, id, checksumCalculator, hashedPcks, unhashedPcks, keySignerBuilder, keyEncryptor)); + } + + /** + * Create a new key ring generator. + * + * @param certificationLevel + * @param masterKey + * @param id + * @param checksumCalculator + * @param hashedPcks + * @param unhashedPcks + * @param keySignerBuilder + * @param keyEncryptor + * @throws PGPException + */ + public PGPKeyRingGenerator( + int certificationLevel, + PGPKeyPair masterKey, + String id, + PGPDigestCalculator checksumCalculator, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder keySignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this.masterKey = masterKey; + this.keyEncryptor = keyEncryptor; + this.checksumCalculator = checksumCalculator; + this.keySignerBuilder = keySignerBuilder; + this.hashedPcks = hashedPcks; + this.unhashedPcks = unhashedPcks; + + keys.add(new PGPSecretKey(certificationLevel, masterKey, id, checksumCalculator, hashedPcks, unhashedPcks, keySignerBuilder, keyEncryptor)); + } + + /** + * Add a sub key to the key ring to be generated with default certification and inheriting + * the hashed/unhashed packets of the master key. + * + * @param keyPair + * @throws PGPException + */ + public void addSubKey( + PGPKeyPair keyPair) + throws PGPException + { + addSubKey(keyPair, hashedPcks, unhashedPcks); + } + + /** + * Add a subkey with specific hashed and unhashed packets associated with it and default + * certification. + * + * @param keyPair public/private key pair. + * @param hashedPcks hashed packet values to be included in certification. + * @param unhashedPcks unhashed packets values to be included in certification. + * @throws PGPException + */ + public void addSubKey( + PGPKeyPair keyPair, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks) + throws PGPException + { + try + { + // + // generate the certification + // + PGPSignatureGenerator sGen = new PGPSignatureGenerator(keySignerBuilder); + + sGen.init(PGPSignature.SUBKEY_BINDING, masterKey.getPrivateKey()); + + sGen.setHashedSubpackets(hashedPcks); + sGen.setUnhashedSubpackets(unhashedPcks); + + List subSigs = new ArrayList(); + + subSigs.add(sGen.generateCertification(masterKey.getPublicKey(), keyPair.getPublicKey())); + + keys.add(new PGPSecretKey(keyPair.getPrivateKey(), new PGPPublicKey(keyPair.getPublicKey(), null, subSigs), checksumCalculator, keyEncryptor)); + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("exception adding subkey: ", e); + } + } + + /** + * Return the secret key ring. + * + * @return a secret key ring. + */ + public PGPSecretKeyRing generateSecretKeyRing() + { + return new PGPSecretKeyRing(keys); + } + + /** + * Return the public key ring that corresponds to the secret key ring. + * + * @return a public key ring. + */ + public PGPPublicKeyRing generatePublicKeyRing() + { + Iterator it = keys.iterator(); + List pubKeys = new ArrayList(); + + pubKeys.add(((PGPSecretKey)it.next()).getPublicKey()); + + while (it.hasNext()) + { + PGPPublicKey k = new PGPPublicKey(((PGPSecretKey)it.next()).getPublicKey()); + + k.publicPk = new PublicSubkeyPacket(k.getAlgorithm(), k.getCreationTime(), k.publicPk.getKey()); + + pubKeys.add(k); + } + + return new PGPPublicKeyRing(pubKeys); + } + + private static PGPDigestCalculator convertSHA1Flag(boolean useSHA1) + throws PGPException + { + return useSHA1 ? new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1) : null; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyValidationException.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyValidationException.java new file mode 100644 index 000000000..63245c519 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPKeyValidationException.java @@ -0,0 +1,16 @@ +package org.spongycastle.openpgp; + +/** + * Thrown if the key checksum is invalid. + */ +public class PGPKeyValidationException + extends PGPException +{ + /** + * @param message + */ + public PGPKeyValidationException(String message) + { + super(message); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralData.java new file mode 100644 index 000000000..e6ac1bc62 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralData.java @@ -0,0 +1,96 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.LiteralDataPacket; + +/** + * class for processing literal data objects. + */ +public class PGPLiteralData +{ + public static final char BINARY = 'b'; + public static final char TEXT = 't'; + public static final char UTF8 = 'u'; + + /** + * The special name indicating a "for your eyes only" packet. + */ + public static final String CONSOLE = "_CONSOLE"; + + /** + * The special time for a modification time of "now" or + * the present time. + */ + public static final Date NOW = new Date(0L); + + LiteralDataPacket data; + + public PGPLiteralData( + BCPGInputStream pIn) + throws IOException + { + data = (LiteralDataPacket)pIn.readPacket(); + } + + /** + * Return the format of the data stream - BINARY or TEXT. + * + * @return int + */ + public int getFormat() + { + return data.getFormat(); + } + + /** + * Return the file name that's associated with the data stream. + * + * @return String + */ + public String getFileName() + { + return data.getFileName(); + } + + /** + * Return the file name as an unintrepreted byte array. + */ + public byte[] getRawFileName() + { + return data.getRawFileName(); + } + + /** + * Return the modification time for the file. + * + * @return the modification time. + */ + public Date getModificationTime() + { + return new Date(data.getModificationTime()); + } + + /** + * Return the raw input stream for the data stream. + * + * @return InputStream + */ + public InputStream getInputStream() + { + return data.getInputStream(); + } + + /** + * Return the input stream representing the data stream + * + * @return InputStream + */ + public InputStream getDataStream() + { + return this.getInputStream(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java new file mode 100644 index 000000000..4835be533 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPLiteralDataGenerator.java @@ -0,0 +1,202 @@ +package org.spongycastle.openpgp; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.util.Strings; + +/** + * Class for producing literal data packets. + */ +public class PGPLiteralDataGenerator implements StreamGenerator +{ + public static final char BINARY = PGPLiteralData.BINARY; + public static final char TEXT = PGPLiteralData.TEXT; + public static final char UTF8 = PGPLiteralData.UTF8; + + /** + * The special name indicating a "for your eyes only" packet. + */ + public static final String CONSOLE = PGPLiteralData.CONSOLE; + + /** + * The special time for a modification time of "now" or + * the present time. + */ + public static final Date NOW = PGPLiteralData.NOW; + + private BCPGOutputStream pkOut; + private boolean oldFormat = false; + + public PGPLiteralDataGenerator() + { + } + + /** + * Generates literal data objects in the old format, this is + * important if you need compatability with PGP 2.6.x. + * + * @param oldFormat + */ + public PGPLiteralDataGenerator( + boolean oldFormat) + { + this.oldFormat = oldFormat; + } + + private void writeHeader( + OutputStream out, + char format, + byte[] encName, + long modificationTime) + throws IOException + { + out.write(format); + + out.write((byte)encName.length); + + for (int i = 0; i != encName.length; i++) + { + out.write(encName[i]); + } + + long modDate = modificationTime / 1000; + + out.write((byte)(modDate >> 24)); + out.write((byte)(modDate >> 16)); + out.write((byte)(modDate >> 8)); + out.write((byte)(modDate)); + } + + /** + * Open a literal data packet, returning a stream to store the data inside + * the packet. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out the stream we want the packet in + * @param format the format we are using + * @param name the name of the "file" + * @param length the length of the data we will write + * @param modificationTime the time of last modification we want stored. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + long length, + Date modificationTime) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + byte[] encName = Strings.toUTF8ByteArray(name); + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, length + 2 + encName.length + 4, oldFormat); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Open a literal data packet, returning a stream to store the data inside + * the packet as an indefinite length stream. The stream is written out as a + * series of partial packets with a chunk size determined by the size of the + * passed in buffer. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * <p> + * <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + * bytes worth of the buffer will be used. + * + * @param out the stream we want the packet in + * @param format the format we are using + * @param name the name of the "file" + * @param modificationTime the time of last modification we want stored. + * @param buffer the buffer to use for collecting data to put into chunks. + */ + public OutputStream open( + OutputStream out, + char format, + String name, + Date modificationTime, + byte[] buffer) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, buffer); + + byte[] encName = Strings.toUTF8ByteArray(name); + + writeHeader(pkOut, format, encName, modificationTime.getTime()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Open a literal data packet for the passed in File object, returning + * an output stream for saving the file contents. + * <p> + * The stream created can be closed off by either calling close() + * on the stream or close() on the generator. Closing the returned + * stream does not close off the OutputStream parameter out. + * + * @param out + * @param format + * @param file + * @return OutputStream + * @throws IOException + */ + public OutputStream open( + OutputStream out, + char format, + File file) + throws IOException + { + if (pkOut != null) + { + throw new IllegalStateException("generator already in open state"); + } + + byte[] encName = Strings.toUTF8ByteArray(file.getName()); + + pkOut = new BCPGOutputStream(out, PacketTags.LITERAL_DATA, file.length() + 2 + encName.length + 4, oldFormat); + + writeHeader(pkOut, format, encName, file.lastModified()); + + return new WrappedGeneratorStream(pkOut, this); + } + + /** + * Close the literal data packet - this is equivalent to calling close on the stream + * returned by the open() method. + * + * @throws IOException + */ + public void close() + throws IOException + { + if (pkOut != null) + { + pkOut.finish(); + pkOut.flush(); + pkOut = null; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPMarker.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPMarker.java new file mode 100644 index 000000000..5719e90dc --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPMarker.java @@ -0,0 +1,34 @@ +/* + * Created on Mar 6, 2004 + * + * To change this generated comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package org.spongycastle.openpgp; + +import java.io.IOException; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.MarkerPacket; + +/** + * a PGP marker packet - in general these should be ignored other than where + * the idea is to preserve the original input stream. + */ +public class PGPMarker +{ + private MarkerPacket p; + + /** + * Default constructor. + * + * @param in + * @throws IOException + */ + public PGPMarker( + BCPGInputStream in) + throws IOException + { + p = (MarkerPacket)in.readPacket(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPObjectFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPObjectFactory.java new file mode 100644 index 000000000..11fb3a53c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPObjectFactory.java @@ -0,0 +1,151 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; + +/** + * General class for reading a PGP object stream. + * <p> + * Note: if this class finds a PGPPublicKey or a PGPSecretKey it + * will create a PGPPublicKeyRing, or a PGPSecretKeyRing for each + * key found. If all you are trying to do is read a key ring file use + * either PGPPublicKeyRingCollection or PGPSecretKeyRingCollection. + */ +public class PGPObjectFactory +{ + private BCPGInputStream in; + private KeyFingerPrintCalculator fingerPrintCalculator; + + public PGPObjectFactory( + InputStream in) + { + this(in, new JcaKeyFingerprintCalculator()); + } + + /** + * Create an object factor suitable for reading keys, key rings and key ring collections. + * + * @param in stream to read from + * @param fingerPrintCalculator calculator to use in key finger print calculations. + */ + public PGPObjectFactory( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + { + this.in = new BCPGInputStream(in); + this.fingerPrintCalculator = fingerPrintCalculator; + } + + public PGPObjectFactory( + byte[] bytes) + { + this(new ByteArrayInputStream(bytes)); + } + + /** + * Create an object factor suitable for reading keys, key rings and key ring collections. + * + * @param bytes stream to read from + * @param fingerPrintCalculator calculator to use in key finger print calculations. + */ + public PGPObjectFactory( + byte[] bytes, + KeyFingerPrintCalculator fingerPrintCalculator) + { + this(new ByteArrayInputStream(bytes), fingerPrintCalculator); + } + + /** + * Return the next object in the stream, or null if the end is reached. + * + * @return Object + * @throws IOException on a parse error + */ + public Object nextObject() + throws IOException + { + List l; + + switch (in.nextPacketTag()) + { + case -1: + return null; + case PacketTags.SIGNATURE: + l = new ArrayList(); + + while (in.nextPacketTag() == PacketTags.SIGNATURE) + { + try + { + l.add(new PGPSignature(in)); + } + catch (PGPException e) + { + throw new IOException("can't create signature object: " + e); + } + } + + return new PGPSignatureList((PGPSignature[])l.toArray(new PGPSignature[l.size()])); + case PacketTags.SECRET_KEY: + try + { + return new PGPSecretKeyRing(in, fingerPrintCalculator); + } + catch (PGPException e) + { + throw new IOException("can't create secret key object: " + e); + } + case PacketTags.PUBLIC_KEY: + return new PGPPublicKeyRing(in, fingerPrintCalculator); + case PacketTags.PUBLIC_SUBKEY: + try + { + return PGPPublicKeyRing.readSubkey(in, fingerPrintCalculator); + } + catch (PGPException e) + { + throw new IOException("processing error: " + e.getMessage()); + } + case PacketTags.COMPRESSED_DATA: + return new PGPCompressedData(in); + case PacketTags.LITERAL_DATA: + return new PGPLiteralData(in); + case PacketTags.PUBLIC_KEY_ENC_SESSION: + case PacketTags.SYMMETRIC_KEY_ENC_SESSION: + return new PGPEncryptedDataList(in); + case PacketTags.ONE_PASS_SIGNATURE: + l = new ArrayList(); + + while (in.nextPacketTag() == PacketTags.ONE_PASS_SIGNATURE) + { + try + { + l.add(new PGPOnePassSignature(in)); + } + catch (PGPException e) + { + throw new IOException("can't create one pass signature object: " + e); + } + } + + return new PGPOnePassSignatureList((PGPOnePassSignature[])l.toArray(new PGPOnePassSignature[l.size()])); + case PacketTags.MARKER: + return new PGPMarker(in); + case PacketTags.EXPERIMENTAL_1: + case PacketTags.EXPERIMENTAL_2: + case PacketTags.EXPERIMENTAL_3: + case PacketTags.EXPERIMENTAL_4: + return in.readPacket(); + } + + throw new IOException("unknown object in stream: " + in.nextPacketTag()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignature.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignature.java new file mode 100644 index 000000000..567edabe6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignature.java @@ -0,0 +1,265 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SignatureException; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; + +/** + * A one pass signature object. + */ +public class PGPOnePassSignature +{ + private OnePassSignaturePacket sigPack; + private int signatureType; + + private PGPContentVerifier verifier; + private byte lastb; + private OutputStream sigOut; + + PGPOnePassSignature( + BCPGInputStream pIn) + throws IOException, PGPException + { + this((OnePassSignaturePacket)pIn.readPacket()); + } + + PGPOnePassSignature( + OnePassSignaturePacket sigPack) + throws PGPException + { + this.sigPack = sigPack; + this.signatureType = sigPack.getSignatureType(); + } + + /** + * Initialise the signature object for verification. + * + * @param pubKey + * @param provider + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use init() method. + */ + public void initVerify( + PGPPublicKey pubKey, + String provider) + throws NoSuchProviderException, PGPException + { + initVerify(pubKey, PGPUtil.getProvider(provider)); + } + + /** + * Initialise the signature object for verification. + * + * @param pubKey + * @param provider + * @throws PGPException + * @deprecated use init() method. + */ + public void initVerify( + PGPPublicKey pubKey, + Provider provider) + throws PGPException + { + init(new JcaPGPContentVerifierBuilderProvider().setProvider(provider), pubKey); + } + + /** + * Initialise the signature object for verification. + * + * @param verifierBuilderProvider provider for a content verifier builder for the signature type of interest. + * @param pubKey the public key to use for verification + * @throws PGPException if there's an issue with creating the verifier. + */ + public void init(PGPContentVerifierBuilderProvider verifierBuilderProvider, PGPPublicKey pubKey) + throws PGPException + { + PGPContentVerifierBuilder verifierBuilder = verifierBuilderProvider.get(sigPack.getKeyAlgorithm(), sigPack.getHashAlgorithm()); + + verifier = verifierBuilder.build(pubKey); + + lastb = 0; + sigOut = verifier.getOutputStream(); + } + + public void update( + byte b) + throws SignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] bytes) + throws SignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + for (int i = 0; i != bytes.length; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, 0, bytes.length); + } + } + + public void update( + byte[] bytes, + int off, + int length) + throws SignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, off, length); + } + } + + private void byteUpdate(byte b) + throws SignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { // TODO: we really should get rid of signature exception next.... + throw new SignatureException(e.getMessage()); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws SignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } + + /** + * Verify the calculated signature against the passed in PGPSignature. + * + * @param pgpSig + * @return boolean + * @throws PGPException + * @throws SignatureException + */ + public boolean verify( + PGPSignature pgpSig) + throws PGPException, SignatureException + { + try + { + sigOut.write(pgpSig.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new PGPException("unable to add trailer: " + e.getMessage(), e); + } + + return verifier.verify(pgpSig.getSignature()); + } + + public long getKeyID() + { + return sigPack.getKeyID(); + } + + public int getSignatureType() + { + return sigPack.getSignatureType(); + } + + public int getHashAlgorithm() + { + return sigPack.getHashAlgorithm(); + } + + public int getKeyAlgorithm() + { + return sigPack.getKeyAlgorithm(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(sigPack); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignatureList.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignatureList.java new file mode 100644 index 000000000..471c64e91 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPOnePassSignatureList.java @@ -0,0 +1,40 @@ +package org.spongycastle.openpgp; + +/** + * Holder for a list of PGPOnePassSignatures + */ +public class PGPOnePassSignatureList +{ + PGPOnePassSignature[] sigs; + + public PGPOnePassSignatureList( + PGPOnePassSignature[] sigs) + { + this.sigs = new PGPOnePassSignature[sigs.length]; + + System.arraycopy(sigs, 0, this.sigs, 0, sigs.length); + } + + public PGPOnePassSignatureList( + PGPOnePassSignature sig) + { + this.sigs = new PGPOnePassSignature[1]; + this.sigs[0] = sig; + } + + public PGPOnePassSignature get( + int index) + { + return sigs[index]; + } + + public int size() + { + return sigs.length; + } + + public boolean isEmpty() + { + return (sigs.length == 0); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPBEEncryptedData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPBEEncryptedData.java new file mode 100644 index 000000000..b728eae8d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPBEEncryptedData.java @@ -0,0 +1,180 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.InputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; +import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.util.io.TeeInputStream; + +/** + * A password based encryption object. + */ +public class PGPPBEEncryptedData + extends PGPEncryptedData +{ + SymmetricKeyEncSessionPacket keyData; + + PGPPBEEncryptedData( + SymmetricKeyEncSessionPacket keyData, + InputStreamPacket encData) + { + super(encData); + + this.keyData = keyData; + } + + /** + * Return the raw input stream for the data stream. + * + * @return InputStream + */ + public InputStream getInputStream() + { + return encData.getInputStream(); + } + + /** + * Return the decrypted input stream, using the passed in passPhrase. + * + * @param passPhrase + * @param provider + * @return InputStream + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use PBEDataDecryptorFactory method + */ + public InputStream getDataStream( + char[] passPhrase, + String provider) + throws PGPException, NoSuchProviderException + { + return getDataStream(passPhrase, PGPUtil.getProvider(provider)); + } + + /** + * Return the decrypted input stream, using the passed in passPhrase. + * + * @param passPhrase + * @param provider + * @return InputStream + * @throws PGPException + * @deprecated use PBEDataDecryptorFactory method + */ + public InputStream getDataStream( + char[] passPhrase, + Provider provider) + throws PGPException + { + return getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build()).setProvider(provider).build(passPhrase)); + } + + /** + * Return the symmetric key algorithm required to decrypt the data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data. + * @return the integer encryption algorithm code. + * @throws PGPException if the session data cannot be recovered. + */ + public int getSymmetricAlgorithm( + PBEDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyData.getEncAlgorithm(), keyData.getS2K()); + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); + + return sessionData[0]; + } + + /** + * Open an input stream which will provide the decrypted data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data and provide the stream. + * @return the resulting input stream + * @throws PGPException if the session data cannot be recovered or the stream cannot be created. + */ + public InputStream getDataStream( + PBEDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + try + { + int keyAlgorithm = keyData.getEncAlgorithm(); + byte[] key = dataDecryptorFactory.makeKeyFromPassPhrase(keyAlgorithm, keyData.getS2K()); + boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket; + + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getEncAlgorithm(), key, keyData.getSecKeyData()); + byte[] sessionKey = new byte[sessionData.length - 1]; + + System.arraycopy(sessionData, 1, sessionKey, 0, sessionKey.length); + + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionData[0] & 0xff, sessionKey); + + encStream = new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); + + if (withIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + integrityCalculator = dataDecryptor.getIntegrityCalculator(); + + encStream = new TeeInputStream(truncStream, integrityCalculator.getOutputStream()); + } + + byte[] iv = new byte[dataDecryptor.getBlockSize()]; + for (int i = 0; i != iv.length; i++) + { + int ch = encStream.read(); + + if (ch < 0) + { + throw new EOFException("unexpected end of stream."); + } + + iv[i] = (byte)ch; + } + + int v1 = encStream.read(); + int v2 = encStream.read(); + + if (v1 < 0 || v2 < 0) + { + throw new EOFException("unexpected end of stream."); + } + + + // Note: the oracle attack on "quick check" bytes is not deemed + // a security risk for PBE (see PGPPublicKeyEncryptedData) + + boolean repeatCheckPassed = iv[iv.length - 2] == (byte) v1 + && iv[iv.length - 1] == (byte) v2; + + // Note: some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + boolean zeroesCheckPassed = v1 == 0 && v2 == 0; + + if (!repeatCheckPassed && !zeroesCheckPassed) + { + throw new PGPDataValidationException("data check failed."); + } + + return encStream; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPrivateKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPrivateKey.java new file mode 100644 index 000000000..a62cf3495 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPrivateKey.java @@ -0,0 +1,138 @@ +package org.spongycastle.openpgp; + +import java.security.PrivateKey; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.RSAPrivateCrtKey; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; + +/** + * general class to contain a private key for use with other openPGP + * objects. + */ +public class PGPPrivateKey +{ + private long keyID; + private PrivateKey privateKey; + private PublicKeyPacket publicKeyPacket; + private BCPGKey privateKeyDataPacket; + + /** + * Create a PGPPrivateKey from a regular private key and the keyID of its associated + * public key. + * + * @param privateKey private key tu use. + * @param keyID keyID of the corresponding public key. + * @deprecated use JcaPGPKeyConverter + */ + public PGPPrivateKey( + PrivateKey privateKey, + long keyID) + { + this.privateKey = privateKey; + this.keyID = keyID; + + if (privateKey instanceof RSAPrivateCrtKey) + { + RSAPrivateCrtKey rsK = (RSAPrivateCrtKey)privateKey; + + privateKeyDataPacket = new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ()); + } + else if (privateKey instanceof DSAPrivateKey) + { + DSAPrivateKey dsK = (DSAPrivateKey)privateKey; + + privateKeyDataPacket = new DSASecretBCPGKey(dsK.getX()); + } + else if (privateKey instanceof ElGamalPrivateKey) + { + ElGamalPrivateKey esK = (ElGamalPrivateKey)privateKey; + + privateKeyDataPacket = new ElGamalSecretBCPGKey(esK.getX()); + } + else + { + throw new IllegalArgumentException("unknown key class"); + } + + } + + /** + * Base constructor. + * + * Create a PGPPrivateKey from a keyID and the associated public/private data packets needed + * to fully describe it. + * + * @param keyID keyID associated with the public key. + * @param publicKeyPacket the public key data packet to be associated with this private key. + * @param privateKeyDataPacket the private key data packet to be associate with this private key. + */ + public PGPPrivateKey( + long keyID, + PublicKeyPacket publicKeyPacket, + BCPGKey privateKeyDataPacket) + { + this.keyID = keyID; + this.publicKeyPacket = publicKeyPacket; + this.privateKeyDataPacket = privateKeyDataPacket; + } + + /** + * Return the keyID associated with the contained private key. + * + * @return long + */ + public long getKeyID() + { + return keyID; + } + + /** + * Return the contained private key. + * + * @return PrivateKey + * @deprecated use a JcaPGPKeyConverter + */ + public PrivateKey getKey() + { + if (privateKey != null) + { + return privateKey; + } + + try + { + return new JcaPGPKeyConverter().setProvider(PGPUtil.getDefaultProvider()).getPrivateKey(this); + } + catch (PGPException e) + { + throw new IllegalStateException("unable to convert key: " + e.toString()); + } + } + + /** + * Return the public key packet associated with this private key, if available. + * + * @return associated public key packet, null otherwise. + */ + public PublicKeyPacket getPublicKeyPacket() + { + return publicKeyPacket; + } + + /** + * Return the private key packet associated with this private key, if available. + * + * @return associated private key packet, null otherwise. + */ + public BCPGKey getPrivateKeyDataPacket() + { + return privateKeyDataPacket; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKey.java new file mode 100644 index 000000000..ba6d6fe92 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKey.java @@ -0,0 +1,964 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.DSAPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalPublicBCPGKey; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserIDPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; +import org.spongycastle.util.Arrays; + +/** + * general class to handle a PGP public key object. + */ +public class PGPPublicKey + implements PublicKeyAlgorithmTags +{ + private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] { PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION }; + + PublicKeyPacket publicPk; + TrustPacket trustPk; + List keySigs = new ArrayList(); + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + + List subSigs = null; + + private long keyID; + private byte[] fingerprint; + private int keyStrength; + + private void init(KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + BCPGKey key = publicPk.getKey(); + + this.fingerprint = fingerPrintCalculator.calculateFingerprint(publicPk); + + if (publicPk.getVersion() <= 3) + { + RSAPublicBCPGKey rK = (RSAPublicBCPGKey)key; + + this.keyID = rK.getModulus().longValue(); + this.keyStrength = rK.getModulus().bitLength(); + } + else + { + this.keyID = ((long)(fingerprint[fingerprint.length - 8] & 0xff) << 56) + | ((long)(fingerprint[fingerprint.length - 7] & 0xff) << 48) + | ((long)(fingerprint[fingerprint.length - 6] & 0xff) << 40) + | ((long)(fingerprint[fingerprint.length - 5] & 0xff) << 32) + | ((long)(fingerprint[fingerprint.length - 4] & 0xff) << 24) + | ((long)(fingerprint[fingerprint.length - 3] & 0xff) << 16) + | ((long)(fingerprint[fingerprint.length - 2] & 0xff) << 8) + | ((fingerprint[fingerprint.length - 1] & 0xff)); + + if (key instanceof RSAPublicBCPGKey) + { + this.keyStrength = ((RSAPublicBCPGKey)key).getModulus().bitLength(); + } + else if (key instanceof DSAPublicBCPGKey) + { + this.keyStrength = ((DSAPublicBCPGKey)key).getP().bitLength(); + } + else if (key instanceof ElGamalPublicBCPGKey) + { + this.keyStrength = ((ElGamalPublicBCPGKey)key).getP().bitLength(); + } + } + } + + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @param provider provider to use for underlying digest calculations. + * @throws PGPException on key creation problem. + * @throws NoSuchProviderException if the specified provider is required and cannot be found. + * @deprecated use JcaPGPKeyConverter.getPGPPublicKey() + */ + public PGPPublicKey( + int algorithm, + PublicKey pubKey, + Date time, + String provider) + throws PGPException, NoSuchProviderException + { + this(new JcaPGPKeyConverter().setProvider(provider).getPGPPublicKey(algorithm, pubKey, time)); + } + + /** + * @deprecated use JcaPGPKeyConverter.getPGPPublicKey() + */ + public PGPPublicKey( + int algorithm, + PublicKey pubKey, + Date time) + throws PGPException + { + this(new JcaPGPKeyConverter().getPGPPublicKey(algorithm, pubKey, time)); + } + + /** + * Create a PGP public key from a packet descriptor using the passed in fingerPrintCalculator to do calculate + * the fingerprint and keyID. + * + * @param publicKeyPacket packet describing the public key. + * @param fingerPrintCalculator calculator providing the digest support ot create the key fingerprint. + * @throws PGPException if the packet is faulty, or the required calculations fail. + */ + public PGPPublicKey(PublicKeyPacket publicKeyPacket, KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicKeyPacket; + this.ids = new ArrayList(); + this.idSigs = new ArrayList(); + + init(fingerPrintCalculator); + } + + /* + * Constructor for a sub-key. + */ + PGPPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + List sigs, + KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.subSigs = sigs; + + init(fingerPrintCalculator); + } + + PGPPublicKey( + PGPPublicKey key, + TrustPacket trust, + List subSigs) + { + this.publicPk = key.publicPk; + this.trustPk = trust; + this.subSigs = subSigs; + + this.fingerprint = key.fingerprint; + this.keyID = key.keyID; + this.keyStrength = key.keyStrength; + } + + /** + * Copy constructor. + * @param pubKey the public key to copy. + */ + PGPPublicKey( + PGPPublicKey pubKey) + { + this.publicPk = pubKey.publicPk; + + this.keySigs = new ArrayList(pubKey.keySigs); + this.ids = new ArrayList(pubKey.ids); + this.idTrusts = new ArrayList(pubKey.idTrusts); + this.idSigs = new ArrayList(pubKey.idSigs.size()); + for (int i = 0; i != pubKey.idSigs.size(); i++) + { + this.idSigs.add(new ArrayList((ArrayList)pubKey.idSigs.get(i))); + } + + if (pubKey.subSigs != null) + { + this.subSigs = new ArrayList(pubKey.subSigs.size()); + for (int i = 0; i != pubKey.subSigs.size(); i++) + { + this.subSigs.add(pubKey.subSigs.get(i)); + } + } + + this.fingerprint = pubKey.fingerprint; + this.keyID = pubKey.keyID; + this.keyStrength = pubKey.keyStrength; + } + + PGPPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + List keySigs, + List ids, + List idTrusts, + List idSigs, + KeyFingerPrintCalculator fingerPrintCalculator) + throws PGPException + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + + init(fingerPrintCalculator); + } + + /** + * @return the version of this key. + */ + public int getVersion() + { + return publicPk.getVersion(); + } + + /** + * @return creation time of key. + */ + public Date getCreationTime() + { + return publicPk.getTime(); + } + + /** + * @return number of valid days from creation time - zero means no + * expiry. + */ + public int getValidDays() + { + if (publicPk.getVersion() > 3) + { + return (int)(this.getValidSeconds() / (24 * 60 * 60)); + } + else + { + return publicPk.getValidDays(); + } + } + + /** + * Return the trust data associated with the public key, if present. + * @return a byte array with trust data, null otherwise. + */ + public byte[] getTrustData() + { + if (trustPk == null) + { + return null; + } + + return Arrays.clone(trustPk.getLevelAndTrustAmount()); + } + + /** + * @return number of valid seconds from creation time - zero means no + * expiry. + */ + public long getValidSeconds() + { + if (publicPk.getVersion() > 3) + { + if (this.isMasterKey()) + { + for (int i = 0; i != MASTER_KEY_CERTIFICATION_TYPES.length; i++) + { + long seconds = getExpirationTimeFromSig(true, MASTER_KEY_CERTIFICATION_TYPES[i]); + + if (seconds >= 0) + { + return seconds; + } + } + } + else + { + long seconds = getExpirationTimeFromSig(false, PGPSignature.SUBKEY_BINDING); + + if (seconds >= 0) + { + return seconds; + } + } + + return 0; + } + else + { + return (long)publicPk.getValidDays() * 24 * 60 * 60; + } + } + + private long getExpirationTimeFromSig( + boolean selfSigned, + int signatureType) + { + Iterator signatures = this.getSignaturesOfType(signatureType); + long expiryTime = -1; + + while (signatures.hasNext()) + { + PGPSignature sig = (PGPSignature)signatures.next(); + + if (!selfSigned || sig.getKeyID() == this.getKeyID()) + { + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null) + { + long current = hashed.getKeyExpirationTime(); + + if (current == 0 || current > expiryTime) + { + expiryTime = current; + } + } + else + { + return 0; + } + } + } + + return expiryTime; + } + + /** + * Return the keyID associated with the public key. + * + * @return long + */ + public long getKeyID() + { + return keyID; + } + + /** + * Return the fingerprint of the key. + * + * @return key fingerprint. + */ + public byte[] getFingerprint() + { + byte[] tmp = new byte[fingerprint.length]; + + System.arraycopy(fingerprint, 0, tmp, 0, tmp.length); + + return tmp; + } + + /** + * Return true if this key has an algorithm type that makes it suitable to use for encryption. + * <p> + * Note: with version 4 keys KeyFlags subpackets should also be considered when present for + * determining the preferred use of the key. + * + * @return true if the key algorithm is suitable for encryption. + */ + public boolean isEncryptionKey() + { + int algorithm = publicPk.getAlgorithm(); + + return ((algorithm == RSA_GENERAL) || (algorithm == RSA_ENCRYPT) + || (algorithm == ELGAMAL_ENCRYPT) || (algorithm == ELGAMAL_GENERAL) || algorithm == ECDH); + } + + /** + * Return true if this is a master key. + * @return true if a master key. + */ + public boolean isMasterKey() + { + return (subSigs == null); + } + + /** + * Return the algorithm code associated with the public key. + * + * @return int + */ + public int getAlgorithm() + { + return publicPk.getAlgorithm(); + } + + /** + * Return the strength of the key in bits. + * + * @return bit strenght of key. + */ + public int getBitStrength() + { + return keyStrength; + } + + /** + * Return the public key contained in the object. + * + * @param provider provider to construct the key for. + * @return a JCE/JCA public key. + * @throws PGPException if the key algorithm is not recognised. + * @throws NoSuchProviderException if the provider cannot be found. + * @deprecated use a JcaPGPKeyConverter + */ + public PublicKey getKey( + String provider) + throws PGPException, NoSuchProviderException + { + return new JcaPGPKeyConverter().setProvider(provider).getPublicKey(this); + } + + /** + * Return the public key contained in the object. + * + * @param provider provider to construct the key for. + * @return a JCE/JCA public key. + * @throws PGPException if the key algorithm is not recognised. + * @deprecated use a JcaPGPKeyConverter + */ + public PublicKey getKey( + Provider provider) + throws PGPException + { + return new JcaPGPKeyConverter().setProvider(provider).getPublicKey(this); + } + + /** + * Return any userIDs associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserIDs() + { + List temp = new ArrayList(); + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof String) + { + temp.add(ids.get(i)); + } + } + + return temp.iterator(); + } + + /** + * Return any user attribute vectors associated with the key. + * + * @return an iterator of PGPUserAttributeSubpacketVector objects. + */ + public Iterator getUserAttributes() + { + List temp = new ArrayList(); + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof PGPUserAttributeSubpacketVector) + { + temp.add(ids.get(i)); + } + } + + return temp.iterator(); + } + + /** + * Return any signatures associated with the passed in id. + * + * @param id the id to be matched. + * @return an iterator of PGPSignature objects. + */ + public Iterator getSignaturesForID( + String id) + { + for (int i = 0; i != ids.size(); i++) + { + if (id.equals(ids.get(i))) + { + return ((ArrayList)idSigs.get(i)).iterator(); + } + } + + return null; + } + + /** + * Return an iterator of signatures associated with the passed in user attributes. + * + * @param userAttributes the vector of user attributes to be matched. + * @return an iterator of PGPSignature objects. + */ + public Iterator getSignaturesForUserAttribute( + PGPUserAttributeSubpacketVector userAttributes) + { + for (int i = 0; i != ids.size(); i++) + { + if (userAttributes.equals(ids.get(i))) + { + return ((ArrayList)idSigs.get(i)).iterator(); + } + } + + return null; + } + + /** + * Return signatures of the passed in type that are on this key. + * + * @param signatureType the type of the signature to be returned. + * @return an iterator (possibly empty) of signatures of the given type. + */ + public Iterator getSignaturesOfType( + int signatureType) + { + List l = new ArrayList(); + Iterator it = this.getSignatures(); + + while (it.hasNext()) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == signatureType) + { + l.add(sig); + } + } + + return l.iterator(); + } + + /** + * Return all signatures/certifications associated with this key. + * + * @return an iterator (possibly empty) with all signatures/certifications. + */ + public Iterator getSignatures() + { + if (subSigs == null) + { + List sigs = new ArrayList(); + + sigs.addAll(keySigs); + + for (int i = 0; i != idSigs.size(); i++) + { + sigs.addAll((Collection)idSigs.get(i)); + } + + return sigs.iterator(); + } + else + { + return subSigs.iterator(); + } + } + + public PublicKeyPacket getPublicKeyPacket() + { + return publicPk; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(publicPk); + if (trustPk != null) + { + out.writePacket(trustPk); + } + + if (subSigs == null) // not a sub-key + { + for (int i = 0; i != keySigs.size(); i++) + { + ((PGPSignature)keySigs.get(i)).encode(out); + } + + for (int i = 0; i != ids.size(); i++) + { + if (ids.get(i) instanceof String) + { + String id = (String)ids.get(i); + + out.writePacket(new UserIDPacket(id)); + } + else + { + PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)ids.get(i); + + out.writePacket(new UserAttributePacket(v.toSubpacketArray())); + } + + if (idTrusts.get(i) != null) + { + out.writePacket((ContainedPacket)idTrusts.get(i)); + } + + List sigs = (List)idSigs.get(i); + for (int j = 0; j != sigs.size(); j++) + { + ((PGPSignature)sigs.get(j)).encode(out); + } + } + } + else + { + for (int j = 0; j != subSigs.size(); j++) + { + ((PGPSignature)subSigs.get(j)).encode(out); + } + } + } + + /** + * Check whether this (sub)key has a revocation signature on it. + * + * @return boolean indicating whether this (sub)key has been revoked. + */ + public boolean isRevoked() + { + int ns = 0; + boolean revoked = false; + + if (this.isMasterKey()) // Master key + { + while (!revoked && (ns < keySigs.size())) + { + if (((PGPSignature)keySigs.get(ns++)).getSignatureType() == PGPSignature.KEY_REVOCATION) + { + revoked = true; + } + } + } + else // Sub-key + { + while (!revoked && (ns < subSigs.size())) + { + if (((PGPSignature)subSigs.get(ns++)).getSignatureType() == PGPSignature.SUBKEY_REVOCATION) + { + revoked = true; + } + } + } + + return revoked; + } + + + /** + * Add a certification for an id to the given public key. + * + * @param key the key the certification is to be added to. + * @param id the id the certification is associated with. + * @param certification the new certification. + * @return the re-certified key. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + String id, + PGPSignature certification) + { + return addCert(key, id, certification); + } + + /** + * Add a certification for the given UserAttributeSubpackets to the given public key. + * + * @param key the key the certification is to be added to. + * @param userAttributes the attributes the certification is associated with. + * @param certification the new certification. + * @return the re-certified key. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes, + PGPSignature certification) + { + return addCert(key, userAttributes, certification); + } + + private static PGPPublicKey addCert( + PGPPublicKey key, + Object id, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + List sigList = null; + + for (int i = 0; i != returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + sigList = (List)returnKey.idSigs.get(i); + } + } + + if (sigList != null) + { + sigList.add(certification); + } + else + { + sigList = new ArrayList(); + + sigList.add(certification); + returnKey.ids.add(id); + returnKey.idTrusts.add(null); + returnKey.idSigs.add(sigList); + } + + return returnKey; + } + + /** + * Remove any certifications associated with a given user attribute subpacket + * on a key. + * + * @param key the key the certifications are to be removed from. + * @param userAttributes the attributes to be removed. + * @return the re-certified key, null if the user attribute subpacket was not found on the key. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes) + { + return removeCert(key, userAttributes); + } + + /** + * Remove any certifications associated with a given id on a key. + * + * @param key the key the certifications are to be removed from. + * @param id the id that is to be removed. + * @return the re-certified key, null if the id was not found on the key. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + String id) + { + return removeCert(key, id); + } + + private static PGPPublicKey removeCert( + PGPPublicKey key, + Object id) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found = false; + + for (int i = 0; i < returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + found = true; + returnKey.ids.remove(i); + returnKey.idTrusts.remove(i); + returnKey.idSigs.remove(i); + } + } + + if (!found) + { + return null; + } + + return returnKey; + } + + /** + * Remove a certification associated with a given id on a key. + * + * @param key the key the certifications are to be removed from. + * @param id the id that the certification is to be removed from. + * @param certification the certification to be removed. + * @return the re-certified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + String id, + PGPSignature certification) + { + return removeCert(key, id, certification); + } + + /** + * Remove a certification associated with a given user attributes on a key. + * + * @param key the key the certifications are to be removed from. + * @param userAttributes the user attributes that the certification is to be removed from. + * @param certification the certification to be removed. + * @return the re-certified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPUserAttributeSubpacketVector userAttributes, + PGPSignature certification) + { + return removeCert(key, userAttributes, certification); + } + + private static PGPPublicKey removeCert( + PGPPublicKey key, + Object id, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found = false; + + for (int i = 0; i < returnKey.ids.size(); i++) + { + if (id.equals(returnKey.ids.get(i))) + { + found = ((List)returnKey.idSigs.get(i)).remove(certification); + } + } + + if (!found) + { + return null; + } + + return returnKey; + } + + /** + * Add a revocation or some other key certification to a key. + * + * @param key the key the revocation is to be added to. + * @param certification the key signature to be added. + * @return the new changed public key object. + */ + public static PGPPublicKey addCertification( + PGPPublicKey key, + PGPSignature certification) + { + if (key.isMasterKey()) + { + if (certification.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) + { + throw new IllegalArgumentException("signature type incorrect for master key revocation."); + } + } + else + { + if (certification.getSignatureType() == PGPSignature.KEY_REVOCATION) + { + throw new IllegalArgumentException("signature type incorrect for sub-key revocation."); + } + } + + PGPPublicKey returnKey = new PGPPublicKey(key); + + if (returnKey.subSigs != null) + { + returnKey.subSigs.add(certification); + } + else + { + returnKey.keySigs.add(certification); + } + + return returnKey; + } + + /** + * Remove a certification from the key. + * + * @param key the key the certifications are to be removed from. + * @param certification the certification to be removed. + * @return the modified key, null if the certification was not found. + */ + public static PGPPublicKey removeCertification( + PGPPublicKey key, + PGPSignature certification) + { + PGPPublicKey returnKey = new PGPPublicKey(key); + boolean found; + + if (returnKey.subSigs != null) + { + found = returnKey.subSigs.remove(certification); + } + else + { + found = returnKey.keySigs.remove(certification); + } + + if (!found) + { + for (Iterator it = key.getUserIDs(); it.hasNext();) + { + String id = (String)it.next(); + for (Iterator sIt = key.getSignaturesForID(id); sIt.hasNext();) + { + if (certification == sIt.next()) + { + found = true; + returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); + } + } + } + + if (!found) + { + for (Iterator it = key.getUserAttributes(); it.hasNext();) + { + PGPUserAttributeSubpacketVector id = (PGPUserAttributeSubpacketVector)it.next(); + for (Iterator sIt = key.getSignaturesForUserAttribute(id); sIt.hasNext();) + { + if (certification == sIt.next()) + { + found = true; + returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); + } + } + } + } + } + + return returnKey; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java new file mode 100644 index 000000000..3baeda631 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyEncryptedData.java @@ -0,0 +1,262 @@ +package org.spongycastle.openpgp; + +import java.io.EOFException; +import java.io.InputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.InputStreamPacket; +import org.spongycastle.bcpg.PublicKeyEncSessionPacket; +import org.spongycastle.bcpg.SymmetricEncIntegrityPacket; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.util.io.TeeInputStream; + +/** + * A public key encrypted data object. + */ +public class PGPPublicKeyEncryptedData + extends PGPEncryptedData +{ + PublicKeyEncSessionPacket keyData; + + PGPPublicKeyEncryptedData( + PublicKeyEncSessionPacket keyData, + InputStreamPacket encData) + { + super(encData); + + this.keyData = keyData; + } + + private boolean confirmCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + return (sessionInfo[sessionInfo.length - 2] == (byte)(check >> 8)) + && (sessionInfo[sessionInfo.length - 1] == (byte)(check)); + } + + /** + * Return the keyID for the key used to encrypt the data. + * + * @return long + */ + public long getKeyID() + { + return keyData.getKeyID(); + } + + /** + * Return the algorithm code for the symmetric algorithm used to encrypt the data. + * + * @return integer algorithm code + * @deprecated use the method taking a PublicKeyDataDecryptorFactory + */ + public int getSymmetricAlgorithm( + PGPPrivateKey privKey, + String provider) + throws PGPException, NoSuchProviderException + { + return getSymmetricAlgorithm(privKey, PGPUtil.getProvider(provider)); + } + + /** + * + * @deprecated use the method taking a PublicKeyDataDecryptorFactory + */ + public int getSymmetricAlgorithm( + PGPPrivateKey privKey, + Provider provider) + throws PGPException, NoSuchProviderException + { + return getSymmetricAlgorithm(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(provider).setContentProvider(provider).build(privKey)); + } + + /** + * Return the symmetric key algorithm required to decrypt the data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data. + * @return the integer encryption algorithm code. + * @throws PGPException if the session data cannot be recovered. + */ + public int getSymmetricAlgorithm( + PublicKeyDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] plain = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey()); + + return plain[0]; + } + + /** + * Return the decrypted data stream for the packet. + * + * @param privKey private key to use + * @param provider provider to use for private key and symmetric key decryption. + * @return InputStream + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method that takes a PublicKeyDataDecryptorFactory + */ + public InputStream getDataStream( + PGPPrivateKey privKey, + String provider) + throws PGPException, NoSuchProviderException + { + return getDataStream(privKey, provider, provider); + } + + /** + * + * @param privKey + * @param provider + * @return + * @throws PGPException + * @deprecated use method that takes a PublicKeyDataDecryptorFactory + */ + public InputStream getDataStream( + PGPPrivateKey privKey, + Provider provider) + throws PGPException + { + return getDataStream(privKey, provider, provider); + } + + /** + * Return the decrypted data stream for the packet. + * + * @param privKey private key to use. + * @param asymProvider asymetric provider to use with private key. + * @param provider provider to use for symmetric algorithm. + * @return InputStream + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method that takes a PublicKeyDataDecryptorFactory + */ + public InputStream getDataStream( + PGPPrivateKey privKey, + String asymProvider, + String provider) + throws PGPException, NoSuchProviderException + { + return getDataStream(privKey, PGPUtil.getProvider(asymProvider), PGPUtil.getProvider(provider)); + } + + /** + * @deprecated use method that takes a PublicKeyDataDecryptorFactory + */ + public InputStream getDataStream( + PGPPrivateKey privKey, + Provider asymProvider, + Provider provider) + throws PGPException + { + return getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(asymProvider).setContentProvider(provider).build(privKey)); + } + + /** + * Open an input stream which will provide the decrypted data protected by this object. + * + * @param dataDecryptorFactory decryptor factory to use to recover the session data and provide the stream. + * @return the resulting input stream + * @throws PGPException if the session data cannot be recovered or the stream cannot be created. + */ + public InputStream getDataStream( + PublicKeyDataDecryptorFactory dataDecryptorFactory) + throws PGPException + { + byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey()); + + if (!confirmCheckSum(sessionData)) + { + throw new PGPKeyValidationException("key checksum failed"); + } + + if (sessionData[0] != SymmetricKeyAlgorithmTags.NULL) + { + try + { + boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket; + byte[] sessionKey = new byte[sessionData.length - 3]; + + System.arraycopy(sessionData, 1, sessionKey, 0, sessionKey.length); + + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionData[0] & 0xff, sessionKey); + + encStream = new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); + + if (withIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + integrityCalculator = dataDecryptor.getIntegrityCalculator(); + + encStream = new TeeInputStream(truncStream, integrityCalculator.getOutputStream()); + } + + byte[] iv = new byte[dataDecryptor.getBlockSize()]; + + for (int i = 0; i != iv.length; i++) + { + int ch = encStream.read(); + + if (ch < 0) + { + throw new EOFException("unexpected end of stream."); + } + + iv[i] = (byte)ch; + } + + int v1 = encStream.read(); + int v2 = encStream.read(); + + if (v1 < 0 || v2 < 0) + { + throw new EOFException("unexpected end of stream."); + } + + // + // some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + // + /* + * Commented out in the light of the oracle attack. + if (iv[iv.length - 2] != (byte)v1 && v1 != 0) + { + throw new PGPDataValidationException("data check failed."); + } + + if (iv[iv.length - 1] != (byte)v2 && v2 != 0) + { + throw new PGPDataValidationException("data check failed."); + } + */ + + return encStream; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception starting decryption", e); + } + } + else + { + return encData.getInputStream(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRing.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRing.java new file mode 100644 index 000000000..2358c207f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRing.java @@ -0,0 +1,273 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; + +/** + * Class to hold a single master public key and its subkeys. + * <p> + * Often PGP keyring files consist of multiple master keys, if you are trying to process + * or construct one of these you should use the PGPPublicKeyRingCollection class. + */ +public class PGPPublicKeyRing + extends PGPKeyRing +{ + List keys; + + /** + * @deprecated use version that takes a KeyFingerPrintCalculator + */ + public PGPPublicKeyRing( + byte[] encoding) + throws IOException + { + this(new ByteArrayInputStream(encoding), new JcaKeyFingerprintCalculator()); + } + + public PGPPublicKeyRing( + byte[] encoding, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException + { + this(new ByteArrayInputStream(encoding), fingerPrintCalculator); + } + + /** + * @param pubKeys + */ + PGPPublicKeyRing( + List pubKeys) + { + this.keys = pubKeys; + } + + /** + * @deprecated use version that takes a KeyFingerPrintCalculator + */ + public PGPPublicKeyRing( + InputStream in) + throws IOException + { + this(in, new JcaKeyFingerprintCalculator()); + } + + public PGPPublicKeyRing( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException + { + this.keys = new ArrayList(); + + BCPGInputStream pIn = wrap(in); + + int initialTag = pIn.nextPacketTag(); + if (initialTag != PacketTags.PUBLIC_KEY && initialTag != PacketTags.PUBLIC_SUBKEY) + { + throw new IOException( + "public key ring doesn't start with public key tag: " + + "tag 0x" + Integer.toHexString(initialTag)); + } + + PublicKeyPacket pubPk = (PublicKeyPacket)pIn.readPacket(); + TrustPacket trustPk = readOptionalTrustPacket(pIn); + + // direct signatures and revocations + List keySigs = readSignaturesAndTrust(pIn); + + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + readUserIDs(pIn, ids, idTrusts, idSigs); + + try + { + keys.add(new PGPPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs, fingerPrintCalculator)); + + // Read subkeys + while (pIn.nextPacketTag() == PacketTags.PUBLIC_SUBKEY) + { + keys.add(readSubkey(pIn, fingerPrintCalculator)); + } + } + catch (PGPException e) + { + throw new IOException("processing exception: " + e.toString()); + } + } + + /** + * Return the first public key in the ring. + * + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey() + { + return (PGPPublicKey)keys.get(0); + } + + /** + * Return the public key referred to by the passed in keyID if it + * is present. + * + * @param keyID + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey( + long keyID) + { + for (int i = 0; i != keys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + if (keyID == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator containing all the public keys. + * + * @return Iterator + */ + public Iterator getPublicKeys() + { + return Collections.unmodifiableList(keys).iterator(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + for (int i = 0; i != keys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + k.encode(outStream); + } + } + + /** + * Returns a new key ring with the public key passed in + * either added or replacing an existing one. + * + * @param pubRing the public key ring to be modified + * @param pubKey the public key to be inserted. + * @return a new keyRing + */ + public static PGPPublicKeyRing insertPublicKey( + PGPPublicKeyRing pubRing, + PGPPublicKey pubKey) + { + List keys = new ArrayList(pubRing.keys); + boolean found = false; + boolean masterFound = false; + + for (int i = 0; i != keys.size();i++) + { + PGPPublicKey key = (PGPPublicKey)keys.get(i); + + if (key.getKeyID() == pubKey.getKeyID()) + { + found = true; + keys.set(i, pubKey); + } + if (key.isMasterKey()) + { + masterFound = true; + } + } + + if (!found) + { + if (pubKey.isMasterKey()) + { + if (masterFound) + { + throw new IllegalArgumentException("cannot add a master key to a ring that already has one"); + } + + keys.add(0, pubKey); + } + else + { + keys.add(pubKey); + } + } + + return new PGPPublicKeyRing(keys); + } + + /** + * Returns a new key ring with the public key passed in + * removed from the key ring. + * + * @param pubRing the public key ring to be modified + * @param pubKey the public key to be removed. + * @return a new keyRing, null if pubKey is not found. + */ + public static PGPPublicKeyRing removePublicKey( + PGPPublicKeyRing pubRing, + PGPPublicKey pubKey) + { + List keys = new ArrayList(pubRing.keys); + boolean found = false; + + for (int i = 0; i < keys.size();i++) + { + PGPPublicKey key = (PGPPublicKey)keys.get(i); + + if (key.getKeyID() == pubKey.getKeyID()) + { + found = true; + keys.remove(i); + } + } + + if (!found) + { + return null; + } + + return new PGPPublicKeyRing(keys); + } + + static PGPPublicKey readSubkey(BCPGInputStream in, KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + PublicKeyPacket pk = (PublicKeyPacket)in.readPacket(); + TrustPacket kTrust = readOptionalTrustPacket(in); + + // PGP 8 actually leaves out the signature. + List sigList = readSignaturesAndTrust(in); + + return new PGPPublicKey(pk, kTrust, sigList, fingerPrintCalculator); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRingCollection.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRingCollection.java new file mode 100644 index 000000000..3e35c5b5b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPPublicKeyRingCollection.java @@ -0,0 +1,369 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.util.Strings; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Often a PGP key ring file is made up of a succession of master/sub-key key rings. + * If you want to read an entire public key file in one hit this is the class for you. + */ +public class PGPPublicKeyRingCollection +{ + private Map pubRings = new HashMap(); + private List order = new ArrayList(); + + private PGPPublicKeyRingCollection( + Map pubRings, + List order) + { + this.pubRings = pubRings; + this.order = order; + } + + public PGPPublicKeyRingCollection( + byte[] encoding) + throws IOException, PGPException + { + this(new ByteArrayInputStream(encoding)); + } + + /** + * Build a PGPPublicKeyRingCollection from the passed in input stream. + * + * @param in input stream containing data + * @throws IOException if a problem parsing the base stream occurs + * @throws PGPException if an object is encountered which isn't a PGPPublicKeyRing + */ + public PGPPublicKeyRingCollection( + InputStream in) + throws IOException, PGPException + { + PGPObjectFactory pgpFact = new PGPObjectFactory(in); + Object obj; + + while ((obj = pgpFact.nextObject()) != null) + { + if (!(obj instanceof PGPPublicKeyRing)) + { + throw new PGPException(obj.getClass().getName() + " found where PGPPublicKeyRing expected"); + } + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)obj; + Long key = new Long(pgpPub.getPublicKey().getKeyID()); + + pubRings.put(key, pgpPub); + order.add(key); + } + } + + public PGPPublicKeyRingCollection( + Collection collection) + throws IOException, PGPException + { + Iterator it = collection.iterator(); + + while (it.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)it.next(); + + Long key = new Long(pgpPub.getPublicKey().getKeyID()); + + pubRings.put(key, pgpPub); + order.add(key); + } + } + + /** + * Return the number of rings in this collection. + * + * @return size of the collection + */ + public int size() + { + return order.size(); + } + + /** + * return the public key rings making up this collection. + */ + public Iterator getKeyRings() + { + return pubRings.values().iterator(); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * + * @param userID the user ID to be matched. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID) + throws PGPException + { + return getKeyRings(userID, false, false); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * <p> + * + * @param userID the user ID to be matched. + * @param matchPartial if true userID need only be a substring of an actual ID string to match. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID, + boolean matchPartial) + throws PGPException + { + return getKeyRings(userID, matchPartial, false); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * <p> + * + * @param userID the user ID to be matched. + * @param matchPartial if true userID need only be a substring of an actual ID string to match. + * @param ignoreCase if true case is ignored in user ID comparisons. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID, + boolean matchPartial, + boolean ignoreCase) + throws PGPException + { + Iterator it = this.getKeyRings(); + List rings = new ArrayList(); + + if (ignoreCase) + { + userID = Strings.toLowerCase(userID); + } + + while (it.hasNext()) + { + PGPPublicKeyRing pubRing = (PGPPublicKeyRing)it.next(); + Iterator uIt = pubRing.getPublicKey().getUserIDs(); + + while (uIt.hasNext()) + { + String next = (String)uIt.next(); + if (ignoreCase) + { + next = Strings.toLowerCase(next); + } + + if (matchPartial) + { + if (next.indexOf(userID) > -1) + { + rings.add(pubRing); + } + } + else + { + if (next.equals(userID)) + { + rings.add(pubRing); + } + } + } + } + + return rings.iterator(); + } + + /** + * Return the PGP public key associated with the given key id. + * + * @param keyID + * @return the PGP public key + * @throws PGPException + */ + public PGPPublicKey getPublicKey( + long keyID) + throws PGPException + { + Iterator it = this.getKeyRings(); + + while (it.hasNext()) + { + PGPPublicKeyRing pubRing = (PGPPublicKeyRing)it.next(); + PGPPublicKey pub = pubRing.getPublicKey(keyID); + + if (pub != null) + { + return pub; + } + } + + return null; + } + + /** + * Return the public key ring which contains the key referred to by keyID. + * + * @param keyID key ID to match against + * @return the public key ring + * @throws PGPException + */ + public PGPPublicKeyRing getPublicKeyRing( + long keyID) + throws PGPException + { + Long id = new Long(keyID); + + if (pubRings.containsKey(id)) + { + return (PGPPublicKeyRing)pubRings.get(id); + } + + Iterator it = this.getKeyRings(); + + while (it.hasNext()) + { + PGPPublicKeyRing pubRing = (PGPPublicKeyRing)it.next(); + PGPPublicKey pub = pubRing.getPublicKey(keyID); + + if (pub != null) + { + return pubRing; + } + } + + return null; + } + + /** + * Return true if a key matching the passed in key ID is present, false otherwise. + * + * @param keyID key ID to look for. + * @return true if keyID present, false otherwise. + */ + public boolean contains(long keyID) + throws PGPException + { + return getPublicKey(keyID) != null; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + Iterator it = order.iterator(); + while (it.hasNext()) + { + PGPPublicKeyRing sr = (PGPPublicKeyRing)pubRings.get(it.next()); + + sr.encode(out); + } + } + + + /** + * Return a new collection object containing the contents of the passed in collection and + * the passed in public key ring. + * + * @param ringCollection the collection the ring to be added to. + * @param publicKeyRing the key ring to be added. + * @return a new collection merging the current one with the passed in ring. + * @exception IllegalArgumentException if the keyID for the passed in ring is already present. + */ + public static PGPPublicKeyRingCollection addPublicKeyRing( + PGPPublicKeyRingCollection ringCollection, + PGPPublicKeyRing publicKeyRing) + { + Long key = new Long(publicKeyRing.getPublicKey().getKeyID()); + + if (ringCollection.pubRings.containsKey(key)) + { + throw new IllegalArgumentException("Collection already contains a key with a keyID for the passed in ring."); + } + + Map newPubRings = new HashMap(ringCollection.pubRings); + List newOrder = new ArrayList(ringCollection.order); + + newPubRings.put(key, publicKeyRing); + newOrder.add(key); + + return new PGPPublicKeyRingCollection(newPubRings, newOrder); + } + + /** + * Return a new collection object containing the contents of this collection with + * the passed in public key ring removed. + * + * @param ringCollection the collection the ring to be removed from. + * @param publicKeyRing the key ring to be removed. + * @return a new collection not containing the passed in ring. + * @exception IllegalArgumentException if the keyID for the passed in ring not present. + */ + public static PGPPublicKeyRingCollection removePublicKeyRing( + PGPPublicKeyRingCollection ringCollection, + PGPPublicKeyRing publicKeyRing) + { + Long key = new Long(publicKeyRing.getPublicKey().getKeyID()); + + if (!ringCollection.pubRings.containsKey(key)) + { + throw new IllegalArgumentException("Collection does not contain a key with a keyID for the passed in ring."); + } + + Map newPubRings = new HashMap(ringCollection.pubRings); + List newOrder = new ArrayList(ringCollection.order); + + newPubRings.remove(key); + + for (int i = 0; i < newOrder.size(); i++) + { + Long r = (Long)newOrder.get(i); + + if (r.longValue() == key.longValue()) + { + newOrder.remove(i); + break; + } + } + + return new PGPPublicKeyRingCollection(newPubRings, newOrder); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java new file mode 100644 index 000000000..d705e5ce6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java @@ -0,0 +1,937 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGObject; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SecretKeyPacket; +import org.spongycastle.bcpg.SecretSubkeyPacket; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserIDPacket; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * general class to handle a PGP secret key object. + */ +public class PGPSecretKey +{ + SecretKeyPacket secret; + PGPPublicKey pub; + + PGPSecretKey( + SecretKeyPacket secret, + PGPPublicKey pub) + { + this.secret = secret; + this.pub = pub; + } + + PGPSecretKey( + PGPPrivateKey privKey, + PGPPublicKey pubKey, + PGPDigestCalculator checksumCalculator, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(privKey, pubKey, checksumCalculator, false, keyEncryptor); + } + + PGPSecretKey( + PGPPrivateKey privKey, + PGPPublicKey pubKey, + PGPDigestCalculator checksumCalculator, + boolean isMasterKey, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this.pub = pubKey; + + BCPGObject secKey = (BCPGObject)privKey.getPrivateKeyDataPacket(); + + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pOut.writeObject(secKey); + + byte[] keyData = bOut.toByteArray(); + + pOut.write(checksum(checksumCalculator, keyData, keyData.length)); + + int encAlgorithm = keyEncryptor.getAlgorithm(); + + if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL) + { + keyData = bOut.toByteArray(); // include checksum + + byte[] encData = keyEncryptor.encryptKeyData(keyData, 0, keyData.length); + byte[] iv = keyEncryptor.getCipherIV(); + + S2K s2k = keyEncryptor.getS2K(); + + int s2kUsage; + + if (checksumCalculator != null) + { + if (checksumCalculator.getAlgorithm() != HashAlgorithmTags.SHA1) + { + throw new PGPException("only SHA1 supported for key checksum calculations."); + } + s2kUsage = SecretKeyPacket.USAGE_SHA1; + } + else + { + s2kUsage = SecretKeyPacket.USAGE_CHECKSUM; + } + + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + } + else + { + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray()); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray()); + } + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception encrypting key", e); + } + } + + /** + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + int encAlgorithm, + char[] passPhrase, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPcks, unhashedPcks, rand, provider); + } + + /** + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + int encAlgorithm, + char[] passPhrase, + boolean useSHA1, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, useSHA1, hashedPcks, unhashedPcks, rand, PGPUtil.getProvider(provider)); + } + + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(certificationLevel, keyPair, id, null, hashedPcks, unhashedPcks, certificationSignerBuilder, keyEncryptor); + } + + /** + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + int encAlgorithm, + char[] passPhrase, + boolean useSHA1, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + Provider provider) + throws PGPException + { + this(keyPair.getPrivateKey(), certifiedPublicKey(certificationLevel, keyPair, id, hashedPcks, unhashedPcks, new JcaPGPContentSignerBuilder(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1).setProvider(provider)), convertSHA1Flag(useSHA1), true, new JcePBESecretKeyEncryptorBuilder(encAlgorithm, new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1)).setProvider(provider).setSecureRandom(rand).build(passPhrase)); + } + + private static PGPDigestCalculator convertSHA1Flag(boolean useSHA1) + throws PGPException + { + return useSHA1 ? new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1) : null; + } + + public PGPSecretKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPDigestCalculator checksumCalculator, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(keyPair.getPrivateKey(), certifiedPublicKey(certificationLevel, keyPair, id, hashedPcks, unhashedPcks, certificationSignerBuilder), checksumCalculator, true, keyEncryptor); + } + + private static PGPPublicKey certifiedPublicKey( + int certificationLevel, + PGPKeyPair keyPair, + String id, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder) + throws PGPException + { + PGPSignatureGenerator sGen; + + try + { + sGen = new PGPSignatureGenerator(certificationSignerBuilder); + } + catch (Exception e) + { + throw new PGPException("creating signature generator: " + e, e); + } + + // + // generate the certification + // + sGen.init(certificationLevel, keyPair.getPrivateKey()); + + sGen.setHashedSubpackets(hashedPcks); + sGen.setUnhashedSubpackets(unhashedPcks); + + try + { + PGPSignature certification = sGen.generateCertification(id, keyPair.getPublicKey()); + + return PGPPublicKey.addCertification(keyPair.getPublicKey(), id, certification); + } + catch (Exception e) + { + throw new PGPException("exception doing certification: " + e, e); + } + } + + /** + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPSecretKey( + int certificationLevel, + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time, + String id, + int encAlgorithm, + char[] passPhrase, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, new PGPKeyPair(algorithm,pubKey, privKey, time), id, encAlgorithm, passPhrase, hashedPcks, unhashedPcks, rand, provider); + } + + /** + * @deprecated use method taking PBESecretKeyEncryptor + */ + public PGPSecretKey( + int certificationLevel, + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time, + String id, + int encAlgorithm, + char[] passPhrase, + boolean useSHA1, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, new PGPKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSHA1, hashedPcks, unhashedPcks, rand, provider); + } + + /** + * @deprecated use method taking PGPKeyPair + */ + public PGPSecretKey( + int certificationLevel, + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time, + String id, + PGPDigestCalculator checksumCalculator, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + this(certificationLevel, new PGPKeyPair(algorithm, pubKey, privKey, time), id, checksumCalculator, hashedPcks, unhashedPcks, certificationSignerBuilder, keyEncryptor); + } + + /** + * @deprecated use method taking PGPKeyPair + */ + public PGPSecretKey( + int certificationLevel, + int algorithm, + PublicKey pubKey, + PrivateKey privKey, + Date time, + String id, + PGPSignatureSubpacketVector hashedPcks, + PGPSignatureSubpacketVector unhashedPcks, + PGPContentSignerBuilder certificationSignerBuilder, + PBESecretKeyEncryptor keyEncryptor) + throws PGPException, NoSuchProviderException + { + this(certificationLevel, new PGPKeyPair(algorithm, pubKey, privKey, time), id, null, hashedPcks, unhashedPcks, certificationSignerBuilder, keyEncryptor); + } + + /** + * Return true if this key has an algorithm type that makes it suitable to use for signing. + * <p> + * Note: with version 4 keys KeyFlags subpackets should also be considered when present for + * determining the preferred use of the key. + * + * @return true if this key algorithm is suitable for use with signing. + */ + public boolean isSigningKey() + { + int algorithm = pub.getAlgorithm(); + + return ((algorithm == PGPPublicKey.RSA_GENERAL) || (algorithm == PGPPublicKey.RSA_SIGN) + || (algorithm == PGPPublicKey.DSA) || (algorithm == PGPPublicKey.ECDSA) || (algorithm == PGPPublicKey.ELGAMAL_GENERAL)); + } + + /** + * Return true if this is a master key. + * @return true if a master key. + */ + public boolean isMasterKey() + { + return pub.isMasterKey(); + } + + /** + * Detect if the Secret Key's Private Key is empty or not + * + * @return boolean whether or not the private key is empty + */ + public boolean isPrivateKeyEmpty() + { + byte[] secKeyData = secret.getSecretKeyData(); + + return (secKeyData == null || secKeyData.length < 1); + } + + /** + * return the algorithm the key is encrypted with. + * + * @return the algorithm used to encrypt the secret key. + */ + public int getKeyEncryptionAlgorithm() + { + return secret.getEncAlgorithm(); + } + + /** + * Return the keyID of the public key associated with this key. + * + * @return the keyID associated with this key. + */ + public long getKeyID() + { + return pub.getKeyID(); + } + + /** + * Return the public key associated with this key. + * + * @return the public key for this key. + */ + public PGPPublicKey getPublicKey() + { + return pub; + } + + /** + * Return any userIDs associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserIDs() + { + return pub.getUserIDs(); + } + + /** + * Return any user attribute vectors associated with the key. + * + * @return an iterator of Strings. + */ + public Iterator getUserAttributes() + { + return pub.getUserAttributes(); + } + + private byte[] extractKeyData( + PBESecretKeyDecryptor decryptorFactory) + throws PGPException + { + byte[] encData = secret.getSecretKeyData(); + byte[] data = null; + + if (secret.getEncAlgorithm() != SymmetricKeyAlgorithmTags.NULL) + { + try + { + if (secret.getPublicKeyPacket().getVersion() == 4) + { + byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); + + data = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, secret.getIV(), encData, 0, encData.length); + + boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1; + byte[] check = checksum(useSHA1 ? decryptorFactory.getChecksumCalculator(HashAlgorithmTags.SHA1) : null, data, (useSHA1) ? data.length - 20 : data.length - 2); + + for (int i = 0; i != check.length; i++) + { + if (check[i] != data[data.length - check.length + i]) + { + throw new PGPException("checksum mismatch at " + i + " of " + check.length); + } + } + } + else // version 2 or 3, RSA only. + { + byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); + + data = new byte[encData.length]; + + byte[] iv = new byte[secret.getIV().length]; + + System.arraycopy(secret.getIV(), 0, iv, 0, iv.length); + + // + // read in the four numbers + // + int pos = 0; + + for (int i = 0; i != 4; i++) + { + int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + byte[] tmp = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, iv, encData, pos + 2, encLen); + System.arraycopy(tmp, 0, data, pos + 2, tmp.length); + pos += 2 + encLen; + + if (i != 3) + { + System.arraycopy(encData, pos - iv.length, iv, 0, iv.length); + } + } + + // + // verify and copy checksum + // + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); + int calcCs = 0; + for (int j = 0; j < data.length - 2; j++) + { + calcCs += data[j] & 0xff; + } + + calcCs &= 0xffff; + if (calcCs != cs) + { + throw new PGPException("checksum mismatch: passphrase wrong, expected " + + Integer.toHexString(cs) + + " found " + Integer.toHexString(calcCs)); + } + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception decrypting key", e); + } + } + else + { + data = encData; + } + + return data; + } + + /** + * Extract a PGPPrivate key from the SecretKey's encrypted contents. + * + * @param passPhrase + * @param provider + * @return PGPPrivateKey + * @throws PGPException + * @throws NoSuchProviderException + * @deprecated use method that takes a PBESecretKeyDecryptor + */ + public PGPPrivateKey extractPrivateKey( + char[] passPhrase, + String provider) + throws PGPException, NoSuchProviderException + { + return extractPrivateKey(passPhrase, PGPUtil.getProvider(provider)); + } + + /** + * Extract a PGPPrivate key from the SecretKey's encrypted contents. + * + * @param passPhrase + * @param provider + * @return PGPPrivateKey + * @throws PGPException + * @deprecated use method that takes a PBESecretKeyDecryptor + */ + public PGPPrivateKey extractPrivateKey( + char[] passPhrase, + Provider provider) + throws PGPException + { + return extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build()).setProvider(provider).build(passPhrase)); + } + + /** + * Extract a PGPPrivate key from the SecretKey's encrypted contents. + * + * @param decryptorFactory factory to use to generate a decryptor for the passed in secretKey. + * @return PGPPrivateKey the unencrypted private key. + * @throws PGPException on failure. + */ + public PGPPrivateKey extractPrivateKey( + PBESecretKeyDecryptor decryptorFactory) + throws PGPException + { + if (isPrivateKeyEmpty()) + { + return null; + } + + PublicKeyPacket pubPk = secret.getPublicKeyPacket(); + + try + { + byte[] data = extractKeyData(decryptorFactory); + BCPGInputStream in = new BCPGInputStream(new ByteArrayInputStream(data)); + + + switch (pubPk.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: + RSASecretBCPGKey rsaPriv = new RSASecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, rsaPriv); + case PGPPublicKey.DSA: + DSASecretBCPGKey dsaPriv = new DSASecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, dsaPriv); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalSecretBCPGKey elPriv = new ElGamalSecretBCPGKey(in); + + return new PGPPrivateKey(this.getKeyID(), pubPk, elPriv); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception constructing key", e); + } + } + + private static byte[] checksum(PGPDigestCalculator digCalc, byte[] bytes, int length) + throws PGPException + { + if (digCalc != null) + { + OutputStream dOut = digCalc.getOutputStream(); + + try + { + dOut.write(bytes, 0, length); + + dOut.close(); + } + catch (Exception e) + { + throw new PGPException("checksum digest calculation failed: " + e.getMessage(), e); + } + return digCalc.getDigest(); + } + else + { + int checksum = 0; + + for (int i = 0; i != length; i++) + { + checksum += bytes[i] & 0xff; + } + + byte[] check = new byte[2]; + + check[0] = (byte)(checksum >> 8); + check[1] = (byte)checksum; + + return check; + } + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(secret); + if (pub.trustPk != null) + { + out.writePacket(pub.trustPk); + } + + if (pub.subSigs == null) // is not a sub key + { + for (int i = 0; i != pub.keySigs.size(); i++) + { + ((PGPSignature)pub.keySigs.get(i)).encode(out); + } + + for (int i = 0; i != pub.ids.size(); i++) + { + if (pub.ids.get(i) instanceof String) + { + String id = (String)pub.ids.get(i); + + out.writePacket(new UserIDPacket(id)); + } + else + { + PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)pub.ids.get(i); + + out.writePacket(new UserAttributePacket(v.toSubpacketArray())); + } + + if (pub.idTrusts.get(i) != null) + { + out.writePacket((ContainedPacket)pub.idTrusts.get(i)); + } + + List sigs = (ArrayList)pub.idSigs.get(i); + + for (int j = 0; j != sigs.size(); j++) + { + ((PGPSignature)sigs.get(j)).encode(out); + } + } + } + else + { + for (int j = 0; j != pub.subSigs.size(); j++) + { + ((PGPSignature)pub.subSigs.get(j)).encode(out); + } + } + } + + /** + * Return a copy of the passed in secret key, encrypted using a new + * password and the passed in algorithm. + * + * @param key the PGPSecretKey to be copied. + * @param oldPassPhrase the current password for key. + * @param newPassPhrase the new password for the key. + * @param newEncAlgorithm the algorithm to be used for the encryption. + * @param rand source of randomness. + * @param provider name of the provider to use + * @deprecated use method taking PBESecretKeyDecryptor and PBESecretKeyEncryptor + */ + public static PGPSecretKey copyWithNewPassword( + PGPSecretKey key, + char[] oldPassPhrase, + char[] newPassPhrase, + int newEncAlgorithm, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + return copyWithNewPassword(key, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand, PGPUtil.getProvider(provider)); + } + + /** + * Return a copy of the passed in secret key, encrypted using a new + * password and the passed in algorithm. + * + * @param key the PGPSecretKey to be copied. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. + */ + public static PGPSecretKey copyWithNewPassword( + PGPSecretKey key, + PBESecretKeyDecryptor oldKeyDecryptor, + PBESecretKeyEncryptor newKeyEncryptor) + throws PGPException + { + if (key.isPrivateKeyEmpty()) + { + throw new PGPException("no private key in this SecretKey - public key present only."); + } + + byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor); + int s2kUsage = key.secret.getS2KUsage(); + byte[] iv = null; + S2K s2k = null; + byte[] keyData; + int newEncAlgorithm = SymmetricKeyAlgorithmTags.NULL; + + if (newKeyEncryptor == null || newKeyEncryptor.getAlgorithm() == SymmetricKeyAlgorithmTags.NULL) + { + s2kUsage = SecretKeyPacket.USAGE_NONE; + if (key.secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1) // SHA-1 hash, need to rewrite checksum + { + keyData = new byte[rawKeyData.length - 18]; + + System.arraycopy(rawKeyData, 0, keyData, 0, keyData.length - 2); + + byte[] check = checksum(null, keyData, keyData.length - 2); + + keyData[keyData.length - 2] = check[0]; + keyData[keyData.length - 1] = check[1]; + } + else + { + keyData = rawKeyData; + } + } + else + { + if (key.secret.getPublicKeyPacket().getVersion() < 4) + { + // Version 2 or 3 - RSA Keys only + + byte[] encKey = newKeyEncryptor.getKey(); + keyData = new byte[rawKeyData.length]; + + if (newKeyEncryptor.getHashAlgorithm() != HashAlgorithmTags.MD5) + { + throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor."); + } + + // + // process 4 numbers + // + int pos = 0; + for (int i = 0; i != 4; i++) + { + int encLen = (((rawKeyData[pos] << 8) | (rawKeyData[pos + 1] & 0xff)) + 7) / 8; + + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + byte[] tmp; + if (i == 0) + { + tmp = newKeyEncryptor.encryptKeyData(encKey, rawKeyData, pos + 2, encLen); + iv = newKeyEncryptor.getCipherIV(); + + } + else + { + byte[] tmpIv = new byte[iv.length]; + + System.arraycopy(keyData, pos - iv.length, tmpIv, 0, tmpIv.length); + tmp = newKeyEncryptor.encryptKeyData(encKey, tmpIv, rawKeyData, pos + 2, encLen); + } + + System.arraycopy(tmp, 0, keyData, pos + 2, tmp.length); + pos += 2 + encLen; + } + + // + // copy in checksum. + // + keyData[pos] = rawKeyData[pos]; + keyData[pos + 1] = rawKeyData[pos + 1]; + + s2k = newKeyEncryptor.getS2K(); + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } + else + { + keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length); + + iv = newKeyEncryptor.getCipherIV(); + + s2k = newKeyEncryptor.getS2K(); + + newEncAlgorithm = newKeyEncryptor.getAlgorithm(); + } + } + + SecretKeyPacket secret; + if (key.secret instanceof SecretSubkeyPacket) + { + secret = new SecretSubkeyPacket(key.secret.getPublicKeyPacket(), + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + else + { + secret = new SecretKeyPacket(key.secret.getPublicKeyPacket(), + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + + return new PGPSecretKey(secret, key.pub); + } + + /** + * Return a copy of the passed in secret key, encrypted using a new + * password and the passed in algorithm. + * + * @param key the PGPSecretKey to be copied. + * @param oldPassPhrase the current password for key. + * @param newPassPhrase the new password for the key. + * @param newEncAlgorithm the algorithm to be used for the encryption. + * @param rand source of randomness. + * @param provider the provider to use + * @deprecated use method taking PBESecretKeyDecryptor and PBESecretKeyEncryptor + */ + public static PGPSecretKey copyWithNewPassword( + PGPSecretKey key, + char[] oldPassPhrase, + char[] newPassPhrase, + int newEncAlgorithm, + SecureRandom rand, + Provider provider) + throws PGPException + { + return copyWithNewPassword(key, new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build()).setProvider(provider).build(oldPassPhrase), new JcePBESecretKeyEncryptorBuilder(newEncAlgorithm).setProvider(provider).setSecureRandom(rand).build(newPassPhrase)); + } + + /** + * Replace the passed the public key on the passed in secret key. + * + * @param secretKey secret key to change + * @param publicKey new public key. + * @return a new secret key. + * @throws IllegalArgumentException if keyIDs do not match. + */ + public static PGPSecretKey replacePublicKey(PGPSecretKey secretKey, PGPPublicKey publicKey) + { + if (publicKey.getKeyID() != secretKey.getKeyID()) + { + throw new IllegalArgumentException("keyIDs do not match"); + } + + return new PGPSecretKey(secretKey.secret, publicKey); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRing.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRing.java new file mode 100644 index 000000000..c182a1f1e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRing.java @@ -0,0 +1,480 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.PublicSubkeyPacket; +import org.spongycastle.bcpg.SecretKeyPacket; +import org.spongycastle.bcpg.SecretSubkeyPacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; + +/** + * Class to hold a single master secret key and its subkeys. + * <p> + * Often PGP keyring files consist of multiple master keys, if you are trying to process + * or construct one of these you should use the PGPSecretKeyRingCollection class. + */ +public class PGPSecretKeyRing + extends PGPKeyRing +{ + List keys; + List extraPubKeys; + + PGPSecretKeyRing(List keys) + { + this(keys, new ArrayList()); + } + + private PGPSecretKeyRing(List keys, List extraPubKeys) + { + this.keys = keys; + this.extraPubKeys = extraPubKeys; + } + + /** + * @deprecated use version that takes KeyFingerprintCalculator + */ + public PGPSecretKeyRing( + byte[] encoding) + throws IOException, PGPException + { + this(new ByteArrayInputStream(encoding)); + } + + public PGPSecretKeyRing( + byte[] encoding, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + this(new ByteArrayInputStream(encoding), fingerPrintCalculator); + } + + /** + * @deprecated use version that takes KeyFingerprintCalculator + */ + public PGPSecretKeyRing( + InputStream in) + throws IOException, PGPException + { + this(in, new JcaKeyFingerprintCalculator()); + } + + public PGPSecretKeyRing( + InputStream in, + KeyFingerPrintCalculator fingerPrintCalculator) + throws IOException, PGPException + { + this.keys = new ArrayList(); + this.extraPubKeys = new ArrayList(); + + BCPGInputStream pIn = wrap(in); + + int initialTag = pIn.nextPacketTag(); + if (initialTag != PacketTags.SECRET_KEY && initialTag != PacketTags.SECRET_SUBKEY) + { + throw new IOException( + "secret key ring doesn't start with secret key tag: " + + "tag 0x" + Integer.toHexString(initialTag)); + } + + SecretKeyPacket secret = (SecretKeyPacket)pIn.readPacket(); + + // + // ignore GPG comment packets if found. + // + while (pIn.nextPacketTag() == PacketTags.EXPERIMENTAL_2) + { + pIn.readPacket(); + } + + TrustPacket trust = readOptionalTrustPacket(pIn); + + // revocation and direct signatures + List keySigs = readSignaturesAndTrust(pIn); + + List ids = new ArrayList(); + List idTrusts = new ArrayList(); + List idSigs = new ArrayList(); + readUserIDs(pIn, ids, idTrusts, idSigs); + + keys.add(new PGPSecretKey(secret, new PGPPublicKey(secret.getPublicKeyPacket(), trust, keySigs, ids, idTrusts, idSigs, fingerPrintCalculator))); + + + // Read subkeys + while (pIn.nextPacketTag() == PacketTags.SECRET_SUBKEY + || pIn.nextPacketTag() == PacketTags.PUBLIC_SUBKEY) + { + if (pIn.nextPacketTag() == PacketTags.SECRET_SUBKEY) + { + SecretSubkeyPacket sub = (SecretSubkeyPacket)pIn.readPacket(); + + // + // ignore GPG comment packets if found. + // + while (pIn.nextPacketTag() == PacketTags.EXPERIMENTAL_2) + { + pIn.readPacket(); + } + + TrustPacket subTrust = readOptionalTrustPacket(pIn); + List sigList = readSignaturesAndTrust(pIn); + + keys.add(new PGPSecretKey(sub, new PGPPublicKey(sub.getPublicKeyPacket(), subTrust, sigList, fingerPrintCalculator))); + } + else + { + PublicSubkeyPacket sub = (PublicSubkeyPacket)pIn.readPacket(); + + TrustPacket subTrust = readOptionalTrustPacket(pIn); + List sigList = readSignaturesAndTrust(pIn); + + extraPubKeys.add(new PGPPublicKey(sub, subTrust, sigList, fingerPrintCalculator)); + } + } + } + + /** + * Return the public key for the master key. + * + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey() + { + return ((PGPSecretKey)keys.get(0)).getPublicKey(); + } + + /** + * Return the public key referred to by the passed in keyID if it + * is present. + * + * @param keyID + * @return PGPPublicKey + */ + public PGPPublicKey getPublicKey( + long keyID) + { + PGPSecretKey key = getSecretKey(keyID); + if (key != null) + { + return key.getPublicKey(); + } + + for (int i = 0; i != extraPubKeys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)keys.get(i); + + if (keyID == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator containing all the public keys. + * + * @return Iterator + */ + public Iterator getPublicKeys() + { + List pubKeys = new ArrayList(); + + for (Iterator it = getSecretKeys(); it.hasNext();) + { + pubKeys.add(((PGPSecretKey)it.next()).getPublicKey()); + } + + pubKeys.addAll(extraPubKeys); + + return Collections.unmodifiableList(pubKeys).iterator(); + } + + /** + * Return the master private key. + * + * @return PGPSecretKey + */ + public PGPSecretKey getSecretKey() + { + return ((PGPSecretKey)keys.get(0)); + } + + /** + * Return an iterator containing all the secret keys. + * + * @return Iterator + */ + public Iterator getSecretKeys() + { + return Collections.unmodifiableList(keys).iterator(); + } + + public PGPSecretKey getSecretKey( + long keyId) + { + for (int i = 0; i != keys.size(); i++) + { + PGPSecretKey k = (PGPSecretKey)keys.get(i); + + if (keyId == k.getKeyID()) + { + return k; + } + } + + return null; + } + + /** + * Return an iterator of the public keys in the secret key ring that + * have no matching private key. At the moment only personal certificate data + * appears in this fashion. + * + * @return iterator of unattached, or extra, public keys. + */ + public Iterator getExtraPublicKeys() + { + return extraPubKeys.iterator(); + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + for (int i = 0; i != keys.size(); i++) + { + PGPSecretKey k = (PGPSecretKey)keys.get(i); + + k.encode(outStream); + } + for (int i = 0; i != extraPubKeys.size(); i++) + { + PGPPublicKey k = (PGPPublicKey)extraPubKeys.get(i); + + k.encode(outStream); + } + } + + /** + * Replace the public key set on the secret ring with the corresponding key off the public ring. + * + * @param secretRing secret ring to be changed. + * @param publicRing public ring containing the new public key set. + */ + public static PGPSecretKeyRing replacePublicKeys(PGPSecretKeyRing secretRing, PGPPublicKeyRing publicRing) + { + List newList = new ArrayList(secretRing.keys.size()); + + for (Iterator it = secretRing.keys.iterator(); it.hasNext();) + { + PGPSecretKey sk = (PGPSecretKey)it.next(); + PGPPublicKey pk = publicRing.getPublicKey(sk.getKeyID()); + + newList.add(PGPSecretKey.replacePublicKey(sk, pk)); + } + + return new PGPSecretKeyRing(newList); + } + + /** + * Return a copy of the passed in secret key ring, with the master key and sub keys encrypted + * using a new password and the passed in algorithm. + * + * @param ring the PGPSecretKeyRing to be copied. + * @param oldPassPhrase the current password for key. + * @param newPassPhrase the new password for the key. + * @param newEncAlgorithm the algorithm to be used for the encryption. + * @param rand source of randomness. + * @param provider name of the provider to use + * @deprecated use version taking PBESecretKeyEncryptor/PBESecretKeyDecryptor + */ + public static PGPSecretKeyRing copyWithNewPassword( + PGPSecretKeyRing ring, + char[] oldPassPhrase, + char[] newPassPhrase, + int newEncAlgorithm, + SecureRandom rand, + String provider) + throws PGPException, NoSuchProviderException + { + return copyWithNewPassword(ring, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand, PGPUtil.getProvider(provider)); + } + + /** + * Return a copy of the passed in secret key ring, with the master key and sub keys encrypted + * using a new password and the passed in algorithm. + * + * @param ring the PGPSecretKeyRing to be copied. + * @param oldPassPhrase the current password for key. + * @param newPassPhrase the new password for the key. + * @param newEncAlgorithm the algorithm to be used for the encryption. + * @param rand source of randomness. + * @param provider provider to use + * @deprecated use version taking PBESecretKeyEncryptor/PBESecretKeyDecryptor + */ + public static PGPSecretKeyRing copyWithNewPassword( + PGPSecretKeyRing ring, + char[] oldPassPhrase, + char[] newPassPhrase, + int newEncAlgorithm, + SecureRandom rand, + Provider provider) + throws PGPException + { + List newKeys = new ArrayList(ring.keys.size()); + + for (Iterator keys = ring.getSecretKeys(); keys.hasNext();) + { + newKeys.add(PGPSecretKey.copyWithNewPassword((PGPSecretKey)keys.next(), oldPassPhrase, newPassPhrase, newEncAlgorithm, rand, provider)); + } + + return new PGPSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /** + * Return a copy of the passed in secret key ring, with the private keys (where present) associated with the master key and sub keys + * are encrypted using a new password and the passed in algorithm. + * + * @param ring the PGPSecretKeyRing to be copied. + * @param oldKeyDecryptor the current decryptor based on the current password for key. + * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material. + * @return the updated key ring. + */ + public static PGPSecretKeyRing copyWithNewPassword( + PGPSecretKeyRing ring, + PBESecretKeyDecryptor oldKeyDecryptor, + PBESecretKeyEncryptor newKeyEncryptor) + throws PGPException + { + List newKeys = new ArrayList(ring.keys.size()); + + for (Iterator keys = ring.getSecretKeys(); keys.hasNext();) + { + PGPSecretKey key = (PGPSecretKey)keys.next(); + + if (key.isPrivateKeyEmpty()) + { + newKeys.add(key); + } + else + { + newKeys.add(PGPSecretKey.copyWithNewPassword(key, oldKeyDecryptor, newKeyEncryptor)); + } + } + + return new PGPSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /** + * Returns a new key ring with the secret key passed in either added or + * replacing an existing one with the same key ID. + * + * @param secRing the secret key ring to be modified. + * @param secKey the secret key to be added. + * @return a new secret key ring. + */ + public static PGPSecretKeyRing insertSecretKey( + PGPSecretKeyRing secRing, + PGPSecretKey secKey) + { + List keys = new ArrayList(secRing.keys); + boolean found = false; + boolean masterFound = false; + + for (int i = 0; i != keys.size();i++) + { + PGPSecretKey key = (PGPSecretKey)keys.get(i); + + if (key.getKeyID() == secKey.getKeyID()) + { + found = true; + keys.set(i, secKey); + } + if (key.isMasterKey()) + { + masterFound = true; + } + } + + if (!found) + { + if (secKey.isMasterKey()) + { + if (masterFound) + { + throw new IllegalArgumentException("cannot add a master key to a ring that already has one"); + } + + keys.add(0, secKey); + } + else + { + keys.add(secKey); + } + } + + return new PGPSecretKeyRing(keys, secRing.extraPubKeys); + } + + /** + * Returns a new key ring with the secret key passed in removed from the + * key ring. + * + * @param secRing the secret key ring to be modified. + * @param secKey the secret key to be removed. + * @return a new secret key ring, or null if secKey is not found. + */ + public static PGPSecretKeyRing removeSecretKey( + PGPSecretKeyRing secRing, + PGPSecretKey secKey) + { + List keys = new ArrayList(secRing.keys); + boolean found = false; + + for (int i = 0; i < keys.size();i++) + { + PGPSecretKey key = (PGPSecretKey)keys.get(i); + + if (key.getKeyID() == secKey.getKeyID()) + { + found = true; + keys.remove(i); + } + } + + if (!found) + { + return null; + } + + return new PGPSecretKeyRing(keys, secRing.extraPubKeys); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRingCollection.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRingCollection.java new file mode 100644 index 000000000..a8f09fc5d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKeyRingCollection.java @@ -0,0 +1,367 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.util.Strings; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Often a PGP key ring file is made up of a succession of master/sub-key key rings. + * If you want to read an entire secret key file in one hit this is the class for you. + */ +public class PGPSecretKeyRingCollection +{ + private Map secretRings = new HashMap(); + private List order = new ArrayList(); + + private PGPSecretKeyRingCollection( + Map secretRings, + List order) + { + this.secretRings = secretRings; + this.order = order; + } + + public PGPSecretKeyRingCollection( + byte[] encoding) + throws IOException, PGPException + { + this(new ByteArrayInputStream(encoding)); + } + + /** + * Build a PGPSecretKeyRingCollection from the passed in input stream. + * + * @param in input stream containing data + * @throws IOException if a problem parsinh the base stream occurs + * @throws PGPException if an object is encountered which isn't a PGPSecretKeyRing + */ + public PGPSecretKeyRingCollection( + InputStream in) + throws IOException, PGPException + { + PGPObjectFactory pgpFact = new PGPObjectFactory(in); + Object obj; + + while ((obj = pgpFact.nextObject()) != null) + { + if (!(obj instanceof PGPSecretKeyRing)) + { + throw new PGPException(obj.getClass().getName() + " found where PGPSecretKeyRing expected"); + } + + PGPSecretKeyRing pgpSecret = (PGPSecretKeyRing)obj; + Long key = new Long(pgpSecret.getPublicKey().getKeyID()); + + secretRings.put(key, pgpSecret); + order.add(key); + } + } + + public PGPSecretKeyRingCollection( + Collection collection) + throws IOException, PGPException + { + Iterator it = collection.iterator(); + + while (it.hasNext()) + { + PGPSecretKeyRing pgpSecret = (PGPSecretKeyRing)it.next(); + Long key = new Long(pgpSecret.getPublicKey().getKeyID()); + + secretRings.put(key, pgpSecret); + order.add(key); + } + } + + /** + * Return the number of rings in this collection. + * + * @return size of the collection + */ + public int size() + { + return order.size(); + } + + /** + * return the secret key rings making up this collection. + */ + public Iterator getKeyRings() + { + return secretRings.values().iterator(); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * + * @param userID the user ID to be matched. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID) + throws PGPException + { + return getKeyRings(userID, false, false); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * <p> + * + * @param userID the user ID to be matched. + * @param matchPartial if true userID need only be a substring of an actual ID string to match. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID, + boolean matchPartial) + throws PGPException + { + return getKeyRings(userID, matchPartial, false); + } + + /** + * Return an iterator of the key rings associated with the passed in userID. + * <p> + * + * @param userID the user ID to be matched. + * @param matchPartial if true userID need only be a substring of an actual ID string to match. + * @param ignoreCase if true case is ignored in user ID comparisons. + * @return an iterator (possibly empty) of key rings which matched. + * @throws PGPException + */ + public Iterator getKeyRings( + String userID, + boolean matchPartial, + boolean ignoreCase) + throws PGPException + { + Iterator it = this.getKeyRings(); + List rings = new ArrayList(); + + if (ignoreCase) + { + userID = Strings.toLowerCase(userID); + } + + while (it.hasNext()) + { + PGPSecretKeyRing secRing = (PGPSecretKeyRing)it.next(); + Iterator uIt = secRing.getSecretKey().getUserIDs(); + + while (uIt.hasNext()) + { + String next = (String)uIt.next(); + if (ignoreCase) + { + next = Strings.toLowerCase(next); + } + + if (matchPartial) + { + if (next.indexOf(userID) > -1) + { + rings.add(secRing); + } + } + else + { + if (next.equals(userID)) + { + rings.add(secRing); + } + } + } + } + + return rings.iterator(); + } + + /** + * Return the PGP secret key associated with the given key id. + * + * @param keyID + * @return the secret key + * @throws PGPException + */ + public PGPSecretKey getSecretKey( + long keyID) + throws PGPException + { + Iterator it = this.getKeyRings(); + + while (it.hasNext()) + { + PGPSecretKeyRing secRing = (PGPSecretKeyRing)it.next(); + PGPSecretKey sec = secRing.getSecretKey(keyID); + + if (sec != null) + { + return sec; + } + } + + return null; + } + + /** + * Return the secret key ring which contains the key referred to by keyID. + * + * @param keyID + * @return the secret key ring + * @throws PGPException + */ + public PGPSecretKeyRing getSecretKeyRing( + long keyID) + throws PGPException + { + Long id = new Long(keyID); + + if (secretRings.containsKey(id)) + { + return (PGPSecretKeyRing)secretRings.get(id); + } + + Iterator it = this.getKeyRings(); + + while (it.hasNext()) + { + PGPSecretKeyRing secretRing = (PGPSecretKeyRing)it.next(); + PGPSecretKey secret = secretRing.getSecretKey(keyID); + + if (secret != null) + { + return secretRing; + } + } + + return null; + } + + /** + * Return true if a key matching the passed in key ID is present, false otherwise. + * + * @param keyID key ID to look for. + * @return true if keyID present, false otherwise. + */ + public boolean contains(long keyID) + throws PGPException + { + return getSecretKey(keyID) != null; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + Iterator it = order.iterator(); + while (it.hasNext()) + { + PGPSecretKeyRing sr = (PGPSecretKeyRing)secretRings.get(it.next()); + + sr.encode(out); + } + } + + /** + * Return a new collection object containing the contents of the passed in collection and + * the passed in secret key ring. + * + * @param ringCollection the collection the ring to be added to. + * @param secretKeyRing the key ring to be added. + * @return a new collection merging the current one with the passed in ring. + * @exception IllegalArgumentException if the keyID for the passed in ring is already present. + */ + public static PGPSecretKeyRingCollection addSecretKeyRing( + PGPSecretKeyRingCollection ringCollection, + PGPSecretKeyRing secretKeyRing) + { + Long key = new Long(secretKeyRing.getPublicKey().getKeyID()); + + if (ringCollection.secretRings.containsKey(key)) + { + throw new IllegalArgumentException("Collection already contains a key with a keyID for the passed in ring."); + } + + Map newSecretRings = new HashMap(ringCollection.secretRings); + List newOrder = new ArrayList(ringCollection.order); + + newSecretRings.put(key, secretKeyRing); + newOrder.add(key); + + return new PGPSecretKeyRingCollection(newSecretRings, newOrder); + } + + /** + * Return a new collection object containing the contents of this collection with + * the passed in secret key ring removed. + * + * @param ringCollection the collection the ring to be removed from. + * @param secretKeyRing the key ring to be removed. + * @return a new collection merging the current one with the passed in ring. + * @exception IllegalArgumentException if the keyID for the passed in ring is not present. + */ + public static PGPSecretKeyRingCollection removeSecretKeyRing( + PGPSecretKeyRingCollection ringCollection, + PGPSecretKeyRing secretKeyRing) + { + Long key = new Long(secretKeyRing.getPublicKey().getKeyID()); + + if (!ringCollection.secretRings.containsKey(key)) + { + throw new IllegalArgumentException("Collection does not contain a key with a keyID for the passed in ring."); + } + + Map newSecretRings = new HashMap(ringCollection.secretRings); + List newOrder = new ArrayList(ringCollection.order); + + newSecretRings.remove(key); + + for (int i = 0; i < newOrder.size(); i++) + { + Long r = (Long)newOrder.get(i); + + if (r.longValue() == key.longValue()) + { + newOrder.remove(i); + break; + } + } + + return new PGPSecretKeyRingCollection(newSecretRings, newOrder); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignature.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignature.java new file mode 100644 index 000000000..19a041fe5 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignature.java @@ -0,0 +1,564 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.TrustPacket; +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.Strings; + +/** + *A PGP signature object. + */ +public class PGPSignature +{ + public static final int BINARY_DOCUMENT = 0x00; + public static final int CANONICAL_TEXT_DOCUMENT = 0x01; + public static final int STAND_ALONE = 0x02; + + public static final int DEFAULT_CERTIFICATION = 0x10; + public static final int NO_CERTIFICATION = 0x11; + public static final int CASUAL_CERTIFICATION = 0x12; + public static final int POSITIVE_CERTIFICATION = 0x13; + + public static final int SUBKEY_BINDING = 0x18; + public static final int PRIMARYKEY_BINDING = 0x19; + public static final int DIRECT_KEY = 0x1f; + public static final int KEY_REVOCATION = 0x20; + public static final int SUBKEY_REVOCATION = 0x28; + public static final int CERTIFICATION_REVOCATION = 0x30; + public static final int TIMESTAMP = 0x40; + + private SignaturePacket sigPck; + private int signatureType; + private TrustPacket trustPck; + private PGPContentVerifier verifier; + private byte lastb; + private OutputStream sigOut; + + PGPSignature( + BCPGInputStream pIn) + throws IOException, PGPException + { + this((SignaturePacket)pIn.readPacket()); + } + + PGPSignature( + SignaturePacket sigPacket) + throws PGPException + { + sigPck = sigPacket; + signatureType = sigPck.getSignatureType(); + trustPck = null; + } + + PGPSignature( + SignaturePacket sigPacket, + TrustPacket trustPacket) + throws PGPException + { + this(sigPacket); + + this.trustPck = trustPacket; + } + + /** + * Return the OpenPGP version number for this signature. + * + * @return signature version number. + */ + public int getVersion() + { + return sigPck.getVersion(); + } + + /** + * Return the key algorithm associated with this signature. + * @return signature key algorithm. + */ + public int getKeyAlgorithm() + { + return sigPck.getKeyAlgorithm(); + } + + /** + * Return the hash algorithm associated with this signature. + * @return signature hash algorithm. + */ + public int getHashAlgorithm() + { + return sigPck.getHashAlgorithm(); + } + + /** + * @deprecated use init(PGPContentVerifierBuilderProvider, PGPPublicKey) + */ + public void initVerify( + PGPPublicKey pubKey, + String provider) + throws NoSuchProviderException, PGPException + { + initVerify(pubKey, PGPUtil.getProvider(provider)); + } + + /** + * @deprecated use init(PGPContentVerifierBuilderProvider, PGPPublicKey) + */ + public void initVerify( + PGPPublicKey pubKey, + Provider provider) + throws PGPException + { + init(new JcaPGPContentVerifierBuilderProvider().setProvider(provider), pubKey); + } + + public void init(PGPContentVerifierBuilderProvider verifierBuilderProvider, PGPPublicKey pubKey) + throws PGPException + { + PGPContentVerifierBuilder verifierBuilder = verifierBuilderProvider.get(sigPck.getKeyAlgorithm(), sigPck.getHashAlgorithm()); + + verifier = verifierBuilder.build(pubKey); + + lastb = 0; + sigOut = verifier.getOutputStream(); + } + + public void update( + byte b) + throws SignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] bytes) + throws SignatureException + { + this.update(bytes, 0, bytes.length); + } + + public void update( + byte[] bytes, + int off, + int length) + throws SignatureException + { + if (signatureType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + this.update(bytes[i]); + } + } + else + { + blockUpdate(bytes, off, length); + } + } + + private void byteUpdate(byte b) + throws SignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { // TODO: we really should get rid of signature exception next.... + throw new SignatureException(e.getMessage()); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws SignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } + + public boolean verify() + throws PGPException, SignatureException + { + try + { + sigOut.write(this.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new SignatureException(e.getMessage()); + } + + return verifier.verify(this.getSignature()); + } + + + private void updateWithIdData(int header, byte[] idBytes) + throws SignatureException + { + this.update((byte)header); + this.update((byte)(idBytes.length >> 24)); + this.update((byte)(idBytes.length >> 16)); + this.update((byte)(idBytes.length >> 8)); + this.update((byte)(idBytes.length)); + this.update(idBytes); + } + + private void updateWithPublicKey(PGPPublicKey key) + throws PGPException, SignatureException + { + byte[] keyBytes = getEncodedPublicKey(key); + + this.update((byte)0x99); + this.update((byte)(keyBytes.length >> 8)); + this.update((byte)(keyBytes.length)); + this.update(keyBytes); + } + + /** + * Verify the signature as certifying the passed in public key as associated + * with the passed in user attributes. + * + * @param userAttributes user attributes the key was stored under + * @param key the key to be verified. + * @return true if the signature matches, false otherwise. + * @throws PGPException + * @throws SignatureException + */ + public boolean verifyCertification( + PGPUserAttributeSubpacketVector userAttributes, + PGPPublicKey key) + throws PGPException, SignatureException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(key); + + // + // hash in the userAttributes + // + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + UserAttributeSubpacket[] packets = userAttributes.toSubpacketArray(); + for (int i = 0; i != packets.length; i++) + { + packets[i].encode(bOut); + } + updateWithIdData(0xd1, bOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("cannot encode subpacket array", e); + } + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + /** + * Verify the signature as certifying the passed in public key as associated + * with the passed in id. + * + * @param id id the key was stored under + * @param key the key to be verified. + * @return true if the signature matches, false otherwise. + * @throws PGPException + * @throws SignatureException + */ + public boolean verifyCertification( + String id, + PGPPublicKey key) + throws PGPException, SignatureException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(key); + + // + // hash in the id + // + updateWithIdData(0xb4, Strings.toUTF8ByteArray(id)); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + /** + * Verify a certification for the passed in key against the passed in + * master key. + * + * @param masterKey the key we are verifying against. + * @param pubKey the key we are verifying. + * @return true if the certification is valid, false otherwise. + * @throws SignatureException + * @throws PGPException + */ + public boolean verifyCertification( + PGPPublicKey masterKey, + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + updateWithPublicKey(masterKey); + updateWithPublicKey(pubKey); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + private void addTrailer() + throws SignatureException + { + try + { + sigOut.write(sigPck.getSignatureTrailer()); + + sigOut.close(); + } + catch (IOException e) + { + throw new SignatureException(e.getMessage()); + } + } + + /** + * Verify a key certification, such as a revocation, for the passed in key. + * + * @param pubKey the key we are checking. + * @return true if the certification is valid, false otherwise. + * @throws SignatureException + * @throws PGPException + */ + public boolean verifyCertification( + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + if (verifier == null) + { + throw new PGPException("PGPSignature not initialised - call init()."); + } + + if (this.getSignatureType() != KEY_REVOCATION + && this.getSignatureType() != SUBKEY_REVOCATION) + { + throw new PGPException("signature is not a key signature"); + } + + updateWithPublicKey(pubKey); + + addTrailer(); + + return verifier.verify(this.getSignature()); + } + + public int getSignatureType() + { + return sigPck.getSignatureType(); + } + + /** + * Return the id of the key that created the signature. + * @return keyID of the signatures corresponding key. + */ + public long getKeyID() + { + return sigPck.getKeyID(); + } + + /** + * Return the creation time of the signature. + * + * @return the signature creation time. + */ + public Date getCreationTime() + { + return new Date(sigPck.getCreationTime()); + } + + public byte[] getSignatureTrailer() + { + return sigPck.getSignatureTrailer(); + } + + /** + * Return true if the signature has either hashed or unhashed subpackets. + * + * @return true if either hashed or unhashed subpackets are present, false otherwise. + */ + public boolean hasSubpackets() + { + return sigPck.getHashedSubPackets() != null || sigPck.getUnhashedSubPackets() != null; + } + + public PGPSignatureSubpacketVector getHashedSubPackets() + { + return createSubpacketVector(sigPck.getHashedSubPackets()); + } + + public PGPSignatureSubpacketVector getUnhashedSubPackets() + { + return createSubpacketVector(sigPck.getUnhashedSubPackets()); + } + + private PGPSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks) + { + if (pcks != null) + { + return new PGPSignatureSubpacketVector(pcks); + } + + return null; + } + + public byte[] getSignature() + throws PGPException + { + MPInteger[] sigValues = sigPck.getSignature(); + byte[] signature; + + if (sigValues != null) + { + if (sigValues.length == 1) // an RSA signature + { + signature = BigIntegers.asUnsignedByteArray(sigValues[0].getValue()); + } + else + { + try + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new ASN1Integer(sigValues[0].getValue())); + v.add(new ASN1Integer(sigValues[1].getValue())); + + signature = new DERSequence(v).getEncoded(); + } + catch (IOException e) + { + throw new PGPException("exception encoding DSA sig.", e); + } + } + } + else + { + signature = sigPck.getSignatureBytes(); + } + + return signature; + } + + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + this.encode(bOut); + + return bOut.toByteArray(); + } + + public void encode( + OutputStream outStream) + throws IOException + { + BCPGOutputStream out; + + if (outStream instanceof BCPGOutputStream) + { + out = (BCPGOutputStream)outStream; + } + else + { + out = new BCPGOutputStream(outStream); + } + + out.writePacket(sigPck); + if (trustPck != null) + { + out.writePacket(trustPck); + } + } + + private byte[] getEncodedPublicKey( + PGPPublicKey pubKey) + throws PGPException + { + byte[] keyBytes; + + try + { + keyBytes = pubKey.publicPk.getEncodedContents(); + } + catch (IOException e) + { + throw new PGPException("exception preparing key.", e); + } + + return keyBytes; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureGenerator.java new file mode 100644 index 000000000..6383f0a8c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureGenerator.java @@ -0,0 +1,579 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.util.Strings; + +/** + * Generator for PGP Signatures. + */ +public class PGPSignatureGenerator +{ + private SignatureSubpacket[] unhashed = new SignatureSubpacket[0]; + private SignatureSubpacket[] hashed = new SignatureSubpacket[0]; + private OutputStream sigOut; + private PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSigner contentSigner; + private int sigType; + private byte lastb; + private int providedKeyAlgorithm = -1; + + /** + * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + * + * @param keyAlgorithm keyAlgorithm to use for signing + * @param hashAlgorithm algorithm to use for digest + * @param provider provider to use for digest algorithm + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use method taking a PGPContentSignerBuilder + */ + public PGPSignatureGenerator( + int keyAlgorithm, + int hashAlgorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException, PGPException + { + this(keyAlgorithm, provider, hashAlgorithm, provider); + } + + /** + * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + * + * @deprecated use method taking a PGPContentSignerBuilder + */ + public PGPSignatureGenerator( + int keyAlgorithm, + int hashAlgorithm, + Provider provider) + throws NoSuchAlgorithmException, PGPException + { + this(keyAlgorithm, provider, hashAlgorithm, provider); + } + + /** + * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + * + * @param keyAlgorithm keyAlgorithm to use for signing + * @param sigProvider provider to use for signature generation + * @param hashAlgorithm algorithm to use for digest + * @param digProvider provider to use for digest algorithm + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use method taking a PGPContentSignerBuilder + */ + public PGPSignatureGenerator( + int keyAlgorithm, + String sigProvider, + int hashAlgorithm, + String digProvider) + throws NoSuchAlgorithmException, NoSuchProviderException, PGPException + { + this(keyAlgorithm, PGPUtil.getProvider(sigProvider), hashAlgorithm, PGPUtil.getProvider(digProvider)); + } + + /** + * + * @param keyAlgorithm + * @param sigProvider + * @param hashAlgorithm + * @param digProvider + * @throws NoSuchAlgorithmException + * @throws PGPException + * @deprecated use constructor taking PGPContentSignerBuilder. + */ + public PGPSignatureGenerator( + int keyAlgorithm, + Provider sigProvider, + int hashAlgorithm, + Provider digProvider) + throws NoSuchAlgorithmException, PGPException + { + this.providedKeyAlgorithm = keyAlgorithm; + this.contentSignerBuilder = new JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm).setProvider(sigProvider).setDigestProvider(digProvider); + } + + /** + * Create a signature generator built on the passed in contentSignerBuilder. + * + * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures. + */ + public PGPSignatureGenerator( + PGPContentSignerBuilder contentSignerBuilder) + { + this.contentSignerBuilder = contentSignerBuilder; + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + * @deprecated use init() method + */ + public void initSign( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + */ + public void init( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @param random + * @throws PGPException + * @deprecated random parameter now ignored. + */ + public void initSign( + int signatureType, + PGPPrivateKey key, + SecureRandom random) + throws PGPException + { + initSign(signatureType, key); + } + + public void update( + byte b) + throws SignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] b) + throws SignatureException + { + this.update(b, 0, b.length); + } + + public void update( + byte[] b, + int off, + int len) + throws SignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + this.update(b[i]); + } + } + else + { + blockUpdate(b, off, len); + } + } + + private void byteUpdate(byte b) + throws SignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { // TODO: we really should get rid of signature exception next.... + throw new SignatureException(e.getMessage()); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws SignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } + + public void setHashedSubpackets( + PGPSignatureSubpacketVector hashedPcks) + { + if (hashedPcks == null) + { + hashed = new SignatureSubpacket[0]; + return; + } + + hashed = hashedPcks.toSubpacketArray(); + } + + public void setUnhashedSubpackets( + PGPSignatureSubpacketVector unhashedPcks) + { + if (unhashedPcks == null) + { + unhashed = new SignatureSubpacket[0]; + return; + } + + unhashed = unhashedPcks.toSubpacketArray(); + } + + /** + * Return the one pass header associated with the current signature. + * + * @param isNested + * @return PGPOnePassSignature + * @throws PGPException + */ + public PGPOnePassSignature generateOnePassVersion( + boolean isNested) + throws PGPException + { + return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested)); + } + + /** + * Return a signature object containing the current signature state. + * + * @return PGPSignature + * @throws PGPException + * @throws SignatureException + */ + public PGPSignature generate() + throws PGPException, SignatureException + { + MPInteger[] sigValues; + int version = 4; + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + SignatureSubpacket[] hPkts, unhPkts; + + if (!packetPresent(hashed, SignatureSubpacketTags.CREATION_TIME)) + { + hPkts = insertSubpacket(hashed, new SignatureCreationTime(false, new Date())); + } + else + { + hPkts = hashed; + } + + if (!packetPresent(hashed, SignatureSubpacketTags.ISSUER_KEY_ID) && !packetPresent(unhashed, SignatureSubpacketTags.ISSUER_KEY_ID)) + { + unhPkts = insertSubpacket(unhashed, new IssuerKeyID(false, contentSigner.getKeyID())); + } + else + { + unhPkts = unhashed; + } + + try + { + sOut.write((byte)version); + sOut.write((byte)sigType); + sOut.write((byte)contentSigner.getKeyAlgorithm()); + sOut.write((byte)contentSigner.getHashAlgorithm()); + + ByteArrayOutputStream hOut = new ByteArrayOutputStream(); + + for (int i = 0; i != hPkts.length; i++) + { + hPkts[i].encode(hOut); + } + + byte[] data = hOut.toByteArray(); + + sOut.write((byte)(data.length >> 8)); + sOut.write((byte)data.length); + sOut.write(data); + } + catch (IOException e) + { + throw new PGPException("exception encoding hashed data.", e); + } + + byte[] hData = sOut.toByteArray(); + + sOut.write((byte)version); + sOut.write((byte)0xff); + sOut.write((byte)(hData.length >> 24)); + sOut.write((byte)(hData.length >> 16)); + sOut.write((byte)(hData.length >> 8)); + sOut.write((byte)(hData.length)); + + byte[] trailer = sOut.toByteArray(); + + blockUpdate(trailer, 0, trailer.length); + + if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN + || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature + { + sigValues = new MPInteger[1]; + sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature())); + } + else + { + sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature()); + } + + byte[] digest = contentSigner.getDigest(); + byte[] fingerPrint = new byte[2]; + + fingerPrint[0] = digest[0]; + fingerPrint[1] = digest[1]; + + return new PGPSignature(new SignaturePacket(sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues)); + } + + /** + * Generate a certification for the passed in id and key. + * + * @param id the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws SignatureException + * @throws PGPException + */ + public PGPSignature generateCertification( + String id, + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the id + // + updateWithIdData(0xb4, Strings.toUTF8ByteArray(id)); + + return this.generate(); + } + + /** + * Generate a certification for the passed in userAttributes + * @param userAttributes the id we are certifying against the public key. + * @param pubKey the key we are certifying against the id. + * @return the certification. + * @throws SignatureException + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPUserAttributeSubpacketVector userAttributes, + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + updateWithPublicKey(pubKey); + + // + // hash in the attributes + // + try + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + UserAttributeSubpacket[] packets = userAttributes.toSubpacketArray(); + for (int i = 0; i != packets.length; i++) + { + packets[i].encode(bOut); + } + updateWithIdData(0xd1, bOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("cannot encode subpacket array", e); + } + + return this.generate(); + } + + /** + * Generate a certification for the passed in key against the passed in + * master key. + * + * @param masterKey the key we are certifying against. + * @param pubKey the key we are certifying. + * @return the certification. + * @throws SignatureException + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey masterKey, + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + updateWithPublicKey(masterKey); + updateWithPublicKey(pubKey); + + return this.generate(); + } + + /** + * Generate a certification, such as a revocation, for the passed in key. + * + * @param pubKey the key we are certifying. + * @return the certification. + * @throws SignatureException + * @throws PGPException + */ + public PGPSignature generateCertification( + PGPPublicKey pubKey) + throws SignatureException, PGPException + { + if ((sigType == PGPSignature.SUBKEY_REVOCATION || sigType == PGPSignature.SUBKEY_BINDING) && !pubKey.isMasterKey()) + { + throw new IllegalArgumentException("certifications involving subkey requires public key of revoking key as well."); + } + + updateWithPublicKey(pubKey); + + return this.generate(); + } + + private byte[] getEncodedPublicKey( + PGPPublicKey pubKey) + throws PGPException + { + byte[] keyBytes; + + try + { + keyBytes = pubKey.publicPk.getEncodedContents(); + } + catch (IOException e) + { + throw new PGPException("exception preparing key.", e); + } + + return keyBytes; + } + + private boolean packetPresent( + SignatureSubpacket[] packets, + int type) + { + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + return true; + } + } + + return false; + } + + private SignatureSubpacket[] insertSubpacket( + SignatureSubpacket[] packets, + SignatureSubpacket subpacket) + { + SignatureSubpacket[] tmp = new SignatureSubpacket[packets.length + 1]; + + tmp[0] = subpacket; + System.arraycopy(packets, 0, tmp, 1, packets.length); + + return tmp; + } + + private void updateWithIdData(int header, byte[] idBytes) + throws SignatureException + { + this.update((byte)header); + this.update((byte)(idBytes.length >> 24)); + this.update((byte)(idBytes.length >> 16)); + this.update((byte)(idBytes.length >> 8)); + this.update((byte)(idBytes.length)); + this.update(idBytes); + } + + private void updateWithPublicKey(PGPPublicKey key) + throws PGPException, SignatureException + { + byte[] keyBytes = getEncodedPublicKey(key); + + this.update((byte)0x99); + this.update((byte)(keyBytes.length >> 8)); + this.update((byte)(keyBytes.length)); + this.update(keyBytes); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureList.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureList.java new file mode 100644 index 000000000..a7f6b8cf3 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureList.java @@ -0,0 +1,40 @@ +package org.spongycastle.openpgp; + +/** + * A list of PGP signatures - normally in the signature block after literal data. + */ +public class PGPSignatureList +{ + PGPSignature[] sigs; + + public PGPSignatureList( + PGPSignature[] sigs) + { + this.sigs = new PGPSignature[sigs.length]; + + System.arraycopy(sigs, 0, this.sigs, 0, sigs.length); + } + + public PGPSignatureList( + PGPSignature sig) + { + this.sigs = new PGPSignature[1]; + this.sigs[0] = sig; + } + + public PGPSignature get( + int index) + { + return sigs[index]; + } + + public int size() + { + return sigs.length; + } + + public boolean isEmpty() + { + return (sigs.length == 0); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketGenerator.java new file mode 100644 index 000000000..f88b733a2 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketGenerator.java @@ -0,0 +1,197 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.EmbeddedSignature; +import org.spongycastle.bcpg.sig.Exportable; +import org.spongycastle.bcpg.sig.Features; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.KeyExpirationTime; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.bcpg.sig.PreferredAlgorithms; +import org.spongycastle.bcpg.sig.PrimaryUserID; +import org.spongycastle.bcpg.sig.Revocable; +import org.spongycastle.bcpg.sig.RevocationKey; +import org.spongycastle.bcpg.sig.RevocationKeyTags; +import org.spongycastle.bcpg.sig.RevocationReason; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.bcpg.sig.SignatureExpirationTime; +import org.spongycastle.bcpg.sig.SignerUserID; +import org.spongycastle.bcpg.sig.TrustSignature; + +/** + * Generator for signature subpackets. + */ +public class PGPSignatureSubpacketGenerator +{ + List list = new ArrayList(); + + public PGPSignatureSubpacketGenerator() + { + } + + public void setRevocable(boolean isCritical, boolean isRevocable) + { + list.add(new Revocable(isCritical, isRevocable)); + } + + public void setExportable(boolean isCritical, boolean isExportable) + { + list.add(new Exportable(isCritical, isExportable)); + } + + public void setFeature(boolean isCritical, byte feature) + { + list.add(new Features(isCritical, feature)); + } + + /** + * Add a TrustSignature packet to the signature. The values for depth and trust are + * largely installation dependent but there are some guidelines in RFC 4880 - + * 5.2.3.13. + * + * @param isCritical true if the packet is critical. + * @param depth depth level. + * @param trustAmount trust amount. + */ + public void setTrust(boolean isCritical, int depth, int trustAmount) + { + list.add(new TrustSignature(isCritical, depth, trustAmount)); + } + + /** + * Set the number of seconds a key is valid for after the time of its creation. A + * value of zero means the key never expires. + * + * @param isCritical true if should be treated as critical, false otherwise. + * @param seconds + */ + public void setKeyExpirationTime(boolean isCritical, long seconds) + { + list.add(new KeyExpirationTime(isCritical, seconds)); + } + + /** + * Set the number of seconds a signature is valid for after the time of its creation. + * A value of zero means the signature never expires. + * + * @param isCritical true if should be treated as critical, false otherwise. + * @param seconds + */ + public void setSignatureExpirationTime(boolean isCritical, long seconds) + { + list.add(new SignatureExpirationTime(isCritical, seconds)); + } + + /** + * Set the creation time for the signature. + * <p> + * Note: this overrides the generation of a creation time when the signature is + * generated. + */ + public void setSignatureCreationTime(boolean isCritical, Date date) + { + list.add(new SignatureCreationTime(isCritical, date)); + } + + public void setPreferredHashAlgorithms(boolean isCritical, int[] algorithms) + { + list.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_HASH_ALGS, isCritical, + algorithms)); + } + + public void setPreferredSymmetricAlgorithms(boolean isCritical, int[] algorithms) + { + list.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_SYM_ALGS, isCritical, + algorithms)); + } + + public void setPreferredCompressionAlgorithms(boolean isCritical, int[] algorithms) + { + list.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_COMP_ALGS, isCritical, + algorithms)); + } + + public void setKeyFlags(boolean isCritical, int flags) + { + list.add(new KeyFlags(isCritical, flags)); + } + + public void setSignerUserID(boolean isCritical, String userID) + { + if (userID == null) + { + throw new IllegalArgumentException("attempt to set null SignerUserID"); + } + + list.add(new SignerUserID(isCritical, userID)); + } + + public void setEmbeddedSignature(boolean isCritical, PGPSignature pgpSignature) + throws IOException + { + byte[] sig = pgpSignature.getEncoded(); + byte[] data; + + if (sig.length - 1 > 256) + { + data = new byte[sig.length - 3]; + } + else + { + data = new byte[sig.length - 2]; + } + + System.arraycopy(sig, sig.length - data.length, data, 0, data.length); + + list.add(new EmbeddedSignature(isCritical, data)); + } + + public void setPrimaryUserID(boolean isCritical, boolean isPrimaryUserID) + { + list.add(new PrimaryUserID(isCritical, isPrimaryUserID)); + } + + public void setNotationData(boolean isCritical, boolean isHumanReadable, String notationName, + String notationValue) + { + list.add(new NotationData(isCritical, isHumanReadable, notationName, notationValue)); + } + + /** + * Sets revocation reason sub packet + */ + public void setRevocationReason(boolean isCritical, byte reason, String description) + { + list.add(new RevocationReason(isCritical, reason, description)); + } + + /** + * Sets revocation key sub packet + */ + public void setRevocationKey(boolean isCritical, int keyAlgorithm, byte[] fingerprint) + { + list.add(new RevocationKey(isCritical, RevocationKeyTags.CLASS_DEFAULT, keyAlgorithm, + fingerprint)); + } + + /** + * Sets issuer key sub packe + */ + public void setIssuerKeyID(boolean isCritical, long keyID) + { + list.add(new IssuerKeyID(isCritical, keyID)); + } + + public PGPSignatureSubpacketVector generate() + { + return new PGPSignatureSubpacketVector( + (SignatureSubpacket[])list.toArray(new SignatureSubpacket[list.size()])); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketVector.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketVector.java new file mode 100644 index 000000000..738828997 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSignatureSubpacketVector.java @@ -0,0 +1,277 @@ +package org.spongycastle.openpgp; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.Features; +import org.spongycastle.bcpg.sig.IssuerKeyID; +import org.spongycastle.bcpg.sig.KeyExpirationTime; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.bcpg.sig.PreferredAlgorithms; +import org.spongycastle.bcpg.sig.PrimaryUserID; +import org.spongycastle.bcpg.sig.SignatureCreationTime; +import org.spongycastle.bcpg.sig.SignatureExpirationTime; +import org.spongycastle.bcpg.sig.SignerUserID; + +/** + * Container for a list of signature subpackets. + */ +public class PGPSignatureSubpacketVector +{ + SignatureSubpacket[] packets; + + PGPSignatureSubpacketVector( + SignatureSubpacket[] packets) + { + this.packets = packets; + } + + public SignatureSubpacket getSubpacket( + int type) + { + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + return packets[i]; + } + } + + return null; + } + + /** + * Return true if a particular subpacket type exists. + * + * @param type type to look for. + * @return true if present, false otherwise. + */ + public boolean hasSubpacket( + int type) + { + return getSubpacket(type) != null; + } + + /** + * Return all signature subpackets of the passed in type. + * @param type subpacket type code + * @return an array of zero or more matching subpackets. + */ + public SignatureSubpacket[] getSubpackets( + int type) + { + List list = new ArrayList(); + + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + list.add(packets[i]); + } + } + + return (SignatureSubpacket[])list.toArray(new SignatureSubpacket[]{}); + } + + public NotationData[] getNotationDataOccurences() + { + SignatureSubpacket[] notations = getSubpackets(SignatureSubpacketTags.NOTATION_DATA); + NotationData[] vals = new NotationData[notations.length]; + for (int i = 0; i < notations.length; i++) + { + vals[i] = (NotationData)notations[i]; + } + + return vals; + } + + public long getIssuerKeyID() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.ISSUER_KEY_ID); + + if (p == null) + { + return 0; + } + + return ((IssuerKeyID)p).getKeyID(); + } + + public Date getSignatureCreationTime() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.CREATION_TIME); + + if (p == null) + { + return null; + } + + return ((SignatureCreationTime)p).getTime(); + } + + /** + * Return the number of seconds a signature is valid for after its creation date. A value of zero means + * the signature never expires. + * + * @return seconds a signature is valid for. + */ + public long getSignatureExpirationTime() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.EXPIRE_TIME); + + if (p == null) + { + return 0; + } + + return ((SignatureExpirationTime)p).getTime(); + } + + /** + * Return the number of seconds a key is valid for after its creation date. A value of zero means + * the key never expires. + * + * @return seconds a key is valid for. + */ + public long getKeyExpirationTime() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.KEY_EXPIRE_TIME); + + if (p == null) + { + return 0; + } + + return ((KeyExpirationTime)p).getTime(); + } + + public int[] getPreferredHashAlgorithms() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_HASH_ALGS); + + if (p == null) + { + return null; + } + + return ((PreferredAlgorithms)p).getPreferences(); + } + + public int[] getPreferredSymmetricAlgorithms() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_SYM_ALGS); + + if (p == null) + { + return null; + } + + return ((PreferredAlgorithms)p).getPreferences(); + } + + public int[] getPreferredCompressionAlgorithms() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.PREFERRED_COMP_ALGS); + + if (p == null) + { + return null; + } + + return ((PreferredAlgorithms)p).getPreferences(); + } + + public int getKeyFlags() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.KEY_FLAGS); + + if (p == null) + { + return 0; + } + + return ((KeyFlags)p).getFlags(); + } + + public String getSignerUserID() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.SIGNER_USER_ID); + + if (p == null) + { + return null; + } + + return ((SignerUserID)p).getID(); + } + + public boolean isPrimaryUserID() + { + PrimaryUserID primaryId = (PrimaryUserID)this.getSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID); + + if (primaryId != null) + { + return primaryId.isPrimaryUserID(); + } + + return false; + } + + public int[] getCriticalTags() + { + int count = 0; + + for (int i = 0; i != packets.length; i++) + { + if (packets[i].isCritical()) + { + count++; + } + } + + int[] list = new int[count]; + + count = 0; + + for (int i = 0; i != packets.length; i++) + { + if (packets[i].isCritical()) + { + list[count++] = packets[i].getType(); + } + } + + return list; + } + + public Features getFeatures() + { + SignatureSubpacket p = this.getSubpacket(SignatureSubpacketTags.FEATURES); + + if (p == null) + { + return null; + } + + return new Features(p.isCritical(), p.getData()); + } + + /** + * Return the number of packets this vector contains. + * + * @return size of the packet vector. + */ + public int size() + { + return packets.length; + } + + SignatureSubpacket[] toSubpacketArray() + { + return packets; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVector.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVector.java new file mode 100644 index 000000000..26d6c7368 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVector.java @@ -0,0 +1,93 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.UserAttributeSubpacketTags; +import org.spongycastle.bcpg.attr.ImageAttribute; + +/** + * Container for a list of user attribute subpackets. + */ +public class PGPUserAttributeSubpacketVector +{ + UserAttributeSubpacket[] packets; + + PGPUserAttributeSubpacketVector( + UserAttributeSubpacket[] packets) + { + this.packets = packets; + } + + public UserAttributeSubpacket getSubpacket( + int type) + { + for (int i = 0; i != packets.length; i++) + { + if (packets[i].getType() == type) + { + return packets[i]; + } + } + + return null; + } + + public ImageAttribute getImageAttribute() + { + UserAttributeSubpacket p = this.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE); + + if (p == null) + { + return null; + } + + return (ImageAttribute)p; + } + + UserAttributeSubpacket[] toSubpacketArray() + { + return packets; + } + + public boolean equals( + Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof PGPUserAttributeSubpacketVector) + { + PGPUserAttributeSubpacketVector other = (PGPUserAttributeSubpacketVector)o; + + if (other.packets.length != packets.length) + { + return false; + } + + for (int i = 0; i != packets.length; i++) + { + if (!other.packets[i].equals(packets[i])) + { + return false; + } + } + + return true; + } + + return false; + } + + public int hashCode() + { + int code = 0; + + for (int i = 0; i != packets.length; i++) + { + code ^= packets[i].hashCode(); + } + + return code; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVectorGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVectorGenerator.java new file mode 100644 index 000000000..66af84b33 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUserAttributeSubpacketVectorGenerator.java @@ -0,0 +1,27 @@ +package org.spongycastle.openpgp; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.attr.ImageAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class PGPUserAttributeSubpacketVectorGenerator +{ + private List list = new ArrayList(); + + public void setImageAttribute(int imageType, byte[] imageData) + { + if (imageData == null) + { + throw new IllegalArgumentException("attempt to set null image"); + } + + list.add(new ImageAttribute(imageType, imageData)); + } + + public PGPUserAttributeSubpacketVector generate() + { + return new PGPUserAttributeSubpacketVector((UserAttributeSubpacket[])list.toArray(new UserAttributeSubpacket[list.size()])); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUtil.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUtil.java new file mode 100644 index 000000000..31f4ed772 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPUtil.java @@ -0,0 +1,376 @@ +package org.spongycastle.openpgp; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; + +import org.spongycastle.asn1.ASN1InputStream; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.DERInteger; +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.util.encoders.Base64; + +/** + * Basic utility class + */ +public class PGPUtil + implements HashAlgorithmTags +{ + private static String defProvider = "SC"; + + /** + * Return the provider that will be used by factory classes in situations + * where a provider must be determined on the fly. + * + * @return String + */ + public static String getDefaultProvider() + { + return defProvider; + } + + /** + * Set the provider to be used by the package when it is necessary to + * find one on the fly. + * + * @param provider + */ + public static void setDefaultProvider( + String provider) + { + defProvider = provider; + } + + static MPInteger[] dsaSigToMpi( + byte[] encoding) + throws PGPException + { + ASN1InputStream aIn = new ASN1InputStream(encoding); + + DERInteger i1; + DERInteger i2; + + try + { + ASN1Sequence s = (ASN1Sequence)aIn.readObject(); + + i1 = (DERInteger)s.getObjectAt(0); + i2 = (DERInteger)s.getObjectAt(1); + } + catch (IOException e) + { + throw new PGPException("exception encoding signature", e); + } + + MPInteger[] values = new MPInteger[2]; + + values[0] = new MPInteger(i1.getValue()); + values[1] = new MPInteger(i2.getValue()); + + return values; + } + + static String getDigestName( + int hashAlgorithm) + throws PGPException + { + switch (hashAlgorithm) + { + case HashAlgorithmTags.SHA1: + return "SHA1"; + case HashAlgorithmTags.MD2: + return "MD2"; + case HashAlgorithmTags.MD5: + return "MD5"; + case HashAlgorithmTags.RIPEMD160: + return "RIPEMD160"; + case HashAlgorithmTags.SHA256: + return "SHA256"; + case HashAlgorithmTags.SHA384: + return "SHA384"; + case HashAlgorithmTags.SHA512: + return "SHA512"; + case HashAlgorithmTags.SHA224: + return "SHA224"; + default: + throw new PGPException("unknown hash algorithm tag in getDigestName: " + hashAlgorithm); + } + } + + static String getSignatureName( + int keyAlgorithm, + int hashAlgorithm) + throws PGPException + { + String encAlg; + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTags.DSA: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases. + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + encAlg = "ElGamal"; + break; + default: + throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return getDigestName(hashAlgorithm) + "with" + encAlg; + } + + public static byte[] makeRandomKey( + int algorithm, + SecureRandom random) + throws PGPException + { + int keySize = 0; + + switch (algorithm) + { + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + keySize = 192; + break; + case SymmetricKeyAlgorithmTags.IDEA: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.CAST5: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.BLOWFISH: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.SAFER: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.DES: + keySize = 64; + break; + case SymmetricKeyAlgorithmTags.AES_128: + keySize = 128; + break; + case SymmetricKeyAlgorithmTags.AES_192: + keySize = 192; + break; + case SymmetricKeyAlgorithmTags.AES_256: + keySize = 256; + break; + case SymmetricKeyAlgorithmTags.TWOFISH: + keySize = 256; + break; + default: + throw new PGPException("unknown symmetric algorithm: " + algorithm); + } + + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + random.nextBytes(keyBytes); + + return keyBytes; + } + + /** + * write out the passed in file as a literal data packet. + * + * @param out + * @param fileType the LiteralData type for the file. + * @param file + * + * @throws IOException + */ + public static void writeFileToLiteralData( + OutputStream out, + char fileType, + File file) + throws IOException + { + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + OutputStream pOut = lData.open(out, fileType, file.getName(), file.length(), new Date(file.lastModified())); + pipeFileContents(file, pOut, 4096); + } + + /** + * write out the passed in file as a literal data packet in partial packet format. + * + * @param out + * @param fileType the LiteralData type for the file. + * @param file + * @param buffer buffer to be used to chunk the file into partial packets. + * + * @throws IOException + */ + public static void writeFileToLiteralData( + OutputStream out, + char fileType, + File file, + byte[] buffer) + throws IOException + { + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + OutputStream pOut = lData.open(out, fileType, file.getName(), new Date(file.lastModified()), buffer); + pipeFileContents(file, pOut, buffer.length); + } + + private static void pipeFileContents(File file, OutputStream pOut, int bufSize) throws IOException + { + FileInputStream in = new FileInputStream(file); + byte[] buf = new byte[bufSize]; + + int len; + while ((len = in.read(buf)) > 0) + { + pOut.write(buf, 0, len); + } + + pOut.close(); + in.close(); + } + + private static final int READ_AHEAD = 60; + + private static boolean isPossiblyBase64( + int ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/') + || (ch == '\r') || (ch == '\n'); + } + + /** + * Return either an ArmoredInputStream or a BCPGInputStream based on + * whether the initial characters of the stream are binary PGP encodings or not. + * + * @param in the stream to be wrapped + * @return a BCPGInputStream + * @throws IOException + */ + public static InputStream getDecoderStream( + InputStream in) + throws IOException + { + if (!in.markSupported()) + { + in = new BufferedInputStreamExt(in); + } + + in.mark(READ_AHEAD); + + int ch = in.read(); + + + if ((ch & 0x80) != 0) + { + in.reset(); + + return in; + } + else + { + if (!isPossiblyBase64(ch)) + { + in.reset(); + + return new ArmoredInputStream(in); + } + + byte[] buf = new byte[READ_AHEAD]; + int count = 1; + int index = 1; + + buf[0] = (byte)ch; + while (count != READ_AHEAD && (ch = in.read()) >= 0) + { + if (!isPossiblyBase64(ch)) + { + in.reset(); + + return new ArmoredInputStream(in); + } + + if (ch != '\n' && ch != '\r') + { + buf[index++] = (byte)ch; + } + + count++; + } + + in.reset(); + + // + // nothing but new lines, little else, assume regular armoring + // + if (count < 4) + { + return new ArmoredInputStream(in); + } + + // + // test our non-blank data + // + byte[] firstBlock = new byte[8]; + + System.arraycopy(buf, 0, firstBlock, 0, firstBlock.length); + + byte[] decoded = Base64.decode(firstBlock); + + // + // it's a base64 PGP block. + // + if ((decoded[0] & 0x80) != 0) + { + return new ArmoredInputStream(in, false); + } + + return new ArmoredInputStream(in); + } + } + + static Provider getProvider(String providerName) + throws NoSuchProviderException + { + Provider prov = Security.getProvider(providerName); + + if (prov == null) + { + throw new NoSuchProviderException("provider " + providerName + " not found."); + } + + return prov; + } + + static class BufferedInputStreamExt extends BufferedInputStream + { + BufferedInputStreamExt(InputStream input) + { + super(input); + } + + public synchronized int available() throws IOException + { + int result = super.available(); + if (result < 0) + { + result = Integer.MAX_VALUE; + } + return result; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPV3SignatureGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPV3SignatureGenerator.java new file mode 100644 index 000000000..c8b837b58 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPV3SignatureGenerator.java @@ -0,0 +1,286 @@ +package org.spongycastle.openpgp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.OnePassSignaturePacket; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignaturePacket; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; + +/** + * Generator for old style PGP V3 Signatures. + */ +public class PGPV3SignatureGenerator +{ + private byte lastb; + private OutputStream sigOut; + private PGPContentSignerBuilder contentSignerBuilder; + private PGPContentSigner contentSigner; + private int sigType; + private int providedKeyAlgorithm = -1; + + /** + * Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + * + * @param keyAlgorithm + * @param hashAlgorithm + * @param provider + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws PGPException + * @deprecated use constructor taking PGPContentSignerBuilder. + */ + public PGPV3SignatureGenerator( + int keyAlgorithm, + int hashAlgorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException, PGPException + { + this(keyAlgorithm, hashAlgorithm, PGPUtil.getProvider(provider)); + } + + /** + * + * @param keyAlgorithm + * @param hashAlgorithm + * @param provider + * @throws NoSuchAlgorithmException + * @throws PGPException + * @deprecated use constructor taking PGPContentSignerBuilder. + */ + public PGPV3SignatureGenerator( + int keyAlgorithm, + int hashAlgorithm, + Provider provider) + throws NoSuchAlgorithmException, PGPException + { + this.providedKeyAlgorithm = keyAlgorithm; + this.contentSignerBuilder = new JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm).setProvider(provider); + } + + /** + * Create a signature generator built on the passed in contentSignerBuilder. + * + * @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures. + */ + public PGPV3SignatureGenerator( + PGPContentSignerBuilder contentSignerBuilder) + { + this.contentSignerBuilder = contentSignerBuilder; + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + */ + public void init( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + contentSigner = contentSignerBuilder.build(signatureType, key); + sigOut = contentSigner.getOutputStream(); + sigType = contentSigner.getType(); + lastb = 0; + + if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm()) + { + throw new PGPException("key algorithm mismatch"); + } + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @param random + * @throws PGPException + * @deprecated random now ignored - set random in PGPContentSignerBuilder + */ + public void initSign( + int signatureType, + PGPPrivateKey key, + SecureRandom random) + throws PGPException + { + init(signatureType, key); + } + + /** + * Initialise the generator for signing. + * + * @param signatureType + * @param key + * @throws PGPException + * @deprecated use init() + */ + public void initSign( + int signatureType, + PGPPrivateKey key) + throws PGPException + { + init(signatureType, key); + } + + public void update( + byte b) + throws SignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + if (b == '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + else if (b == '\n') + { + if (lastb != '\r') + { + byteUpdate((byte)'\r'); + byteUpdate((byte)'\n'); + } + } + else + { + byteUpdate(b); + } + + lastb = b; + } + else + { + byteUpdate(b); + } + } + + public void update( + byte[] b) + throws SignatureException + { + this.update(b, 0, b.length); + } + + public void update( + byte[] b, + int off, + int len) + throws SignatureException + { + if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + this.update(b[i]); + } + } + else + { + blockUpdate(b, off, len); + } + } + + private void byteUpdate(byte b) + throws SignatureException + { + try + { + sigOut.write(b); + } + catch (IOException e) + { + throw new IllegalStateException("unable to update signature"); + } + } + + private void blockUpdate(byte[] block, int off, int len) + throws SignatureException + { + try + { + sigOut.write(block, off, len); + } + catch (IOException e) + { + throw new IllegalStateException("unable to update signature"); + } + } + + /** + * Return the one pass header associated with the current signature. + * + * @param isNested + * @return PGPOnePassSignature + * @throws PGPException + */ + public PGPOnePassSignature generateOnePassVersion( + boolean isNested) + throws PGPException + { + return new PGPOnePassSignature(new OnePassSignaturePacket(sigType, contentSigner.getHashAlgorithm(), contentSigner.getKeyAlgorithm(), contentSigner.getKeyID(), isNested)); + } + + /** + * Return a V3 signature object containing the current signature state. + * + * @return PGPSignature + * @throws PGPException + * @throws SignatureException + */ + public PGPSignature generate() + throws PGPException, SignatureException + { + long creationTime = new Date().getTime() / 1000; + + ByteArrayOutputStream sOut = new ByteArrayOutputStream(); + + sOut.write(sigType); + sOut.write((byte)(creationTime >> 24)); + sOut.write((byte)(creationTime >> 16)); + sOut.write((byte)(creationTime >> 8)); + sOut.write((byte)creationTime); + + byte[] hData = sOut.toByteArray(); + + blockUpdate(hData, 0, hData.length); + + MPInteger[] sigValues; + if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN + || contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) + // an RSA signature + { + sigValues = new MPInteger[1]; + sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature())); + } + else + { + sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature()); + } + + byte[] digest = contentSigner.getDigest(); + byte[] fingerPrint = new byte[2]; + + fingerPrint[0] = digest[0]; + fingerPrint[1] = digest[1]; + + return new PGPSignature(new SignaturePacket(3, contentSigner.getType(), contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), creationTime * 1000, fingerPrint, sigValues)); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/StreamGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/StreamGenerator.java new file mode 100644 index 000000000..9ee3d4659 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/StreamGenerator.java @@ -0,0 +1,9 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; + +interface StreamGenerator +{ + void close() + throws IOException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/WrappedGeneratorStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/WrappedGeneratorStream.java new file mode 100644 index 000000000..f5360d51b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/WrappedGeneratorStream.java @@ -0,0 +1,46 @@ +package org.spongycastle.openpgp; + +import java.io.IOException; +import java.io.OutputStream; + +class WrappedGeneratorStream + extends OutputStream +{ + private final OutputStream _out; + private final StreamGenerator _sGen; + + public WrappedGeneratorStream(OutputStream out, StreamGenerator sGen) + { + _out = out; + _sGen = sGen; + } + public void write(byte[] bytes) + throws IOException + { + _out.write(bytes); + } + + public void write(byte[] bytes, int offset, int length) + throws IOException + { + _out.write(bytes, offset, length); + } + + public void write(int b) + throws IOException + { + _out.write(b); + } + + public void flush() + throws IOException + { + _out.flush(); + } + + public void close() + throws IOException + { + _sGen.close(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java new file mode 100644 index 000000000..948af4028 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ByteArrayHandler.java @@ -0,0 +1,206 @@ +package org.spongycastle.openpgp.examples; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.util.io.Streams; + +/** + * Simple routine to encrypt and decrypt using a passphrase. + * This service routine provides the basic PGP services between + * byte arrays. + * + * Note: this code plays no attention to -CONSOLE in the file name + * the specification of "_CONSOLE" in the filename. + * It also expects that a single pass phrase will have been used. + * + */ +public class ByteArrayHandler +{ + /** + * decrypt the passed in message stream + * + * @param encrypted The message to be decrypted. + * @param passPhrase Pass phrase (key) + * + * @return Clear text as a byte array. I18N considerations are + * not handled by this routine + * @exception IOException + * @exception PGPException + * @exception NoSuchProviderException + */ + public static byte[] decrypt( + byte[] encrypted, + char[] passPhrase) + throws IOException, PGPException, NoSuchProviderException + { + InputStream in = new ByteArrayInputStream(encrypted); + + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // + // the first object might be a PGP marker packet. + // + if (o instanceof PGPEncryptedDataList) + { + enc = (PGPEncryptedDataList)o; + } + else + { + enc = (PGPEncryptedDataList)pgpF.nextObject(); + } + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(passPhrase)); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + return Streams.readAll(ld.getInputStream()); + } + + /** + * Simple PGP encryptor between byte[]. + * + * @param clearData The test to be encrypted + * @param passPhrase The pass phrase (key). This method assumes that the + * key is a simple pass phrase, and does not yet support + * RSA or more sophisiticated keying. + * @param fileName File name. This is used in the Literal Data Packet (tag 11) + * which is really inly important if the data is to be + * related to a file to be recovered later. Because this + * routine does not know the source of the information, the + * caller can set something here for file name use that + * will be carried. If this routine is being used to + * encrypt SOAP MIME bodies, for example, use the file name from the + * MIME type, if applicable. Or anything else appropriate. + * + * @param armor + * + * @return encrypted data. + * @exception IOException + * @exception PGPException + * @exception NoSuchProviderException + */ + public static byte[] encrypt( + byte[] clearData, + char[] passPhrase, + String fileName, + int algorithm, + boolean armor) + throws IOException, PGPException, NoSuchProviderException + { + if (fileName == null) + { + fileName= PGPLiteralData.CONSOLE; + } + + byte[] compressedData = compress(clearData, fileName, CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream out = bOut; + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(algorithm).setSecureRandom(new SecureRandom()).setProvider("SC")); + encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase).setProvider("SC")); + + OutputStream encOut = encGen.open(out, compressedData.length); + + encOut.write(compressedData); + encOut.close(); + + if (armor) + { + out.close(); + } + + return bOut.toByteArray(); + } + + private static byte[] compress(byte[] clearData, String fileName, int algorithm) throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm); + OutputStream cos = comData.open(bOut); // open it with the final destination + + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + + // we want to generate compressed data. This might be a user option later, + // in which case we would pass in bOut. + OutputStream pOut = lData.open(cos, // the compressed output stream + PGPLiteralData.BINARY, + fileName, // "filename" to store + clearData.length, // length of clear data + new Date() // current time + ); + + pOut.write(clearData); + pOut.close(); + + comData.close(); + + return bOut.toByteArray(); + } + + public static void main(String[] args) throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + String passPhrase = "Dick Beck"; + char[] passArray = passPhrase.toCharArray(); + + byte[] original = "Hello world".getBytes(); + System.out.println("Starting PGP test"); + byte[] encrypted = encrypt(original, passArray, "iway", PGPEncryptedDataGenerator.CAST5, true); + + System.out.println("\nencrypted data = '"+new String(encrypted)+"'"); + byte[] decrypted= decrypt(encrypted,passArray); + + System.out.println("\ndecrypted data = '"+new String(decrypted)+"'"); + + encrypted = encrypt(original, passArray, "iway", PGPEncryptedDataGenerator.AES_256, false); + + System.out.println("\nencrypted data = '"+new String(org.spongycastle.util.encoders.Hex.encode(encrypted))+"'"); + decrypted= decrypt(encrypted, passArray); + + System.out.println("\ndecrypted data = '"+new String(decrypted)+"'"); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java new file mode 100644 index 000000000..4082e2a66 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java @@ -0,0 +1,390 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that creates clear signed files and verifies them. + * <p> + * To sign a file: ClearSignedFileProcessor -s fileName secretKey passPhrase.<br> + * <p> + * To decrypt: ClearSignedFileProcessor -v fileName signatureFile publicKeyFile. + */ +public class ClearSignedFileProcessor +{ + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException + { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException + { + bOut.reset(); + + int ch = lookAhead; + + do + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + while ((ch = fIn.read()) >= 0); + + if (ch < 0) + { + lookAhead = -1; + } + + return lookAhead; + } + + private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException + { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') + { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + /* + * verify a clear text signed file + */ + private static void verifyFile( + InputStream in, + InputStream keyIn, + String resultName) + throws Exception + { + ArmoredInputStream aIn = new ArmoredInputStream(in); + OutputStream out = new BufferedOutputStream(new FileOutputStream(resultName)); + + + + // + // write out signed section using the local line separator. + // note: trailing white space needs to be removed from the end of + // each line RFC 4880 Section 7.1 + // + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, aIn); + byte[] lineSep = getLineSeparator(); + + if (lookAhead != -1 && aIn.isClearText()) + { + byte[] line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line)); + out.write(lineSep); + + while (lookAhead != -1 && aIn.isClearText()) + { + lookAhead = readInputLine(lineOut, lookAhead, aIn); + + line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line)); + out.write(lineSep); + } + } + + out.close(); + + PGPPublicKeyRingCollection pgpRings = new PGPPublicKeyRingCollection(keyIn); + + PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + + PGPPublicKey publicKey = pgpRings.getPublicKey(sig.getKeyID()); + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), publicKey); + + // + // read the input, making sure we ignore the last newline. + // + + InputStream sigIn = new BufferedInputStream(new FileInputStream(resultName)); + + lookAhead = readInputLine(lineOut, sigIn); + + processLine(sig, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + sig.update((byte)'\r'); + sig.update((byte)'\n'); + + processLine(sig, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + sigIn.close(); + + if (sig.verify()) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + private static byte[] getLineSeparator() + { + String nl = System.getProperty("line.separator"); + byte[] nlBytes = new byte[nl.length()]; + + for (int i = 0; i != nlBytes.length; i++) + { + nlBytes[i] = (byte)nl.charAt(i); + } + + return nlBytes; + } + + /* + * create a clear text signed file. + */ + private static void signFile( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + String digestName) + throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException + { + int digest; + + if (digestName.equals("SHA256")) + { + digest = PGPUtil.SHA256; + } + else if (digestName.equals("SHA384")) + { + digest = PGPUtil.SHA384; + } + else if (digestName.equals("SHA512")) + { + digest = PGPUtil.SHA512; + } + else if (digestName.equals("MD5")) + { + digest = PGPUtil.MD5; + } + else if (digestName.equals("RIPEMD160")) + { + digest = PGPUtil.RIPEMD160; + } + else + { + digest = PGPUtil.SHA1; + } + + PGPSecretKey pgpSecKey = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSecKey.getPublicKey().getAlgorithm(), digest).setProvider("SC")); + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + Iterator it = pgpSecKey.getPublicKey().getUserIDs(); + if (it.hasNext()) + { + spGen.setSignerUserID(false, (String)it.next()); + sGen.setHashedSubpackets(spGen.generate()); + } + + InputStream fIn = new BufferedInputStream(new FileInputStream(fileName)); + ArmoredOutputStream aOut = new ArmoredOutputStream(out); + + aOut.beginClearText(digest); + + // + // note the last \n/\r/\r\n in the file is ignored + // + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, fIn); + + processLine(aOut, sGen, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, fIn); + + sGen.update((byte)'\r'); + sGen.update((byte)'\n'); + + processLine(aOut, sGen, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + fIn.close(); + + aOut.endClearText(); + + BCPGOutputStream bOut = new BCPGOutputStream(aOut); + + sGen.generate().encode(bOut); + + aOut.close(); + } + + private static void processLine(PGPSignature sig, byte[] line) + throws SignatureException, IOException + { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sig.update(line, 0, length); + } + } + + private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line) + throws SignatureException, IOException + { + // note: trailing white space needs to be removed from the end of + // each line for signature calculation RFC 4880 Section 7.1 + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sGen.update(line, 0, length); + } + + aOut.write(line, 0, line.length); + } + + private static int getLengthWithoutSeparatorOrTrailingWhitespace(byte[] line) + { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) + { + end--; + } + + return end + 1; + } + + private static boolean isLineEnding(byte b) + { + return b == '\r' || b == '\n'; + } + + private static int getLengthWithoutWhiteSpace(byte[] line) + { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) + { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) + { + return isLineEnding(b) || b == '\t' || b == ' '; + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + InputStream keyIn = PGPUtil.getDecoderStream(new FileInputStream(args[2])); + FileOutputStream out = new FileOutputStream(args[1] + ".asc"); + + if (args.length == 4) + { + signFile(args[1], keyIn, out, args[3].toCharArray(), "SHA1"); + } + else + { + signFile(args[1], keyIn, out, args[3].toCharArray(), args[4]); + } + } + else if (args[0].equals("-v")) + { + if (args[1].indexOf(".asc") < 0) + { + System.err.println("file needs to end in \".asc\""); + System.exit(1); + } + FileInputStream in = new FileInputStream(args[1]); + InputStream keyIn = PGPUtil.getDecoderStream(new FileInputStream(args[2])); + + verifyFile(in, keyIn, args[1].substring(0, args[1].length() - 4)); + } + else + { + System.err.println("usage: ClearSignedFileProcessor [-s file keyfile passPhrase]|[-v sigFile keyFile]"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java new file mode 100644 index 000000000..e67e6f221 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DSAElGamalKeyRingGenerator.java @@ -0,0 +1,139 @@ +package org.spongycastle.openpgp.examples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * A simple utility class that generates a public/secret keyring containing a DSA signing + * key and an El Gamal key for encryption. + * <p> + * usage: DSAElGamalKeyRingGenerator [-a] identity passPhrase + * <p> + * Where identity is the name to be associated with the public key. The keys are placed + * in the files pub.[asc|bpg] and secret.[asc|bpg]. + * <p> + * <b>Note</b>: this example encrypts the secret key using AES_256, many PGP products still + * do not support this, if you are having problems importing keys try changing the algorithm + * id to PGPEncryptedData.CAST5. CAST5 is more widely supported. + */ +public class DSAElGamalKeyRingGenerator +{ + private static void exportKeyPair( + OutputStream secretOut, + OutputStream publicOut, + KeyPair dsaKp, + KeyPair elgKp, + String identity, + char[] passPhrase, + boolean armor) + throws IOException, InvalidKeyException, NoSuchProviderException, SignatureException, PGPException + { + if (armor) + { + secretOut = new ArmoredOutputStream(secretOut); + } + + PGPKeyPair dsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new JcaPGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + identity, sha1Calc, null, null, new JcaPGPContentSignerBuilder(dsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + keyRingGen.addSubKey(elgKeyPair); + + keyRingGen.generateSecretKeyRing().encode(secretOut); + + secretOut.close(); + + if (armor) + { + publicOut = new ArmoredOutputStream(publicOut); + } + + keyRingGen.generatePublicKeyRing().encode(publicOut); + + publicOut.close(); + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length < 2) + { + System.out.println("DSAElGamalKeyRingGenerator [-a] identity passPhrase"); + System.exit(0); + } + + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(1024); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + + if (args[0].equals("-a")) + { + if (args.length < 3) + { + System.out.println("DSAElGamalKeyRingGenerator [-a] identity passPhrase"); + System.exit(0); + } + + FileOutputStream out1 = new FileOutputStream("secret.asc"); + FileOutputStream out2 = new FileOutputStream("pub.asc"); + + exportKeyPair(out1, out2, dsaKp, elgKp, args[1], args[2].toCharArray(), true); + } + else + { + FileOutputStream out1 = new FileOutputStream("secret.bpg"); + FileOutputStream out2 = new FileOutputStream("pub.bpg"); + + exportKeyPair(out1, out2, dsaKp, elgKp, args[0], args[1].toCharArray(), false); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java new file mode 100644 index 000000000..5c25d5e35 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java @@ -0,0 +1,198 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.Security; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that creates seperate signatures for files and verifies them. + * <p> + * To sign a file: DetachedSignatureProcessor -s [-a] fileName secretKey passPhrase.<br> + * If -a is specified the output file will be "ascii-armored". + * <p> + * To decrypt: DetachedSignatureProcessor -v fileName signatureFile publicKeyFile. + * <p> + * Note: this example will silently overwrite files. + * It also expects that a single pass phrase + * will have been used. + */ +public class DetachedSignatureProcessor +{ + private static void verifySignature( + String fileName, + String inputFileName, + String keyFileName) + throws GeneralSecurityException, IOException, PGPException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + + verifySignature(fileName, in, keyIn); + + keyIn.close(); + in.close(); + } + + /* + * verify the signature in in against the file fileName. + */ + private static void verifySignature( + String fileName, + InputStream in, + InputStream keyIn) + throws GeneralSecurityException, IOException, PGPException + { + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpFact = new PGPObjectFactory(in); + PGPSignatureList p3; + + Object o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) + { + PGPCompressedData c1 = (PGPCompressedData)o; + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p3 = (PGPSignatureList)pgpFact.nextObject(); + } + else + { + p3 = (PGPSignatureList)o; + } + + PGPPublicKeyRingCollection pgpPubRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn)); + + + InputStream dIn = new BufferedInputStream(new FileInputStream(fileName)); + + PGPSignature sig = p3.get(0); + PGPPublicKey key = pgpPubRingCollection.getPublicKey(sig.getKeyID()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), key); + + int ch; + while ((ch = dIn.read()) >= 0) + { + sig.update((byte)ch); + } + + dIn.close(); + + if (sig.verify()) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + private static void createSignature( + String inputFileName, + String keyFileName, + String outputFileName, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + + createSignature(inputFileName, keyIn, out, pass, armor); + + out.close(); + keyIn.close(); + } + + private static void createSignature( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPSecretKey pgpSec = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + BCPGOutputStream bOut = new BCPGOutputStream(out); + + InputStream fIn = new BufferedInputStream(new FileInputStream(fileName)); + + int ch; + while ((ch = fIn.read()) >= 0) + { + sGen.update((byte)ch); + } + + fIn.close(); + + sGen.generate().encode(bOut); + + if (armor) + { + out.close(); + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + if (args[1].equals("-a")) + { + createSignature(args[2], args[3], args[2] + ".asc", args[4].toCharArray(), true); + } + else + { + createSignature(args[1], args[2], args[1] + ".bpg", args[3].toCharArray(), false); + } + } + else if (args[0].equals("-v")) + { + verifySignature(args[1], args[2], args[3]); + } + else + { + System.err.println("usage: DetachedSignatureProcessor [-s [-a] file keyfile passPhrase]|[-v file sigFile keyFile]"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java new file mode 100644 index 000000000..8efd01e3d --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/DirectKeySignature.java @@ -0,0 +1,135 @@ +package org.spongycastle.openpgp.examples; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that directly signs a public key and writes the signed key to "SignedKey.asc" in + * the current working directory. + * <p> + * To sign a key: DirectKeySignature secretKeyFile secretKeyPass publicKeyFile(key to be signed) NotationName NotationValue.<br/> + * </p><p> + * To display a NotationData packet from a publicKey previously signed: DirectKeySignature signedPublicKeyFile.<br/> + * </p><p> + * <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * </p> + */ +public class DirectKeySignature +{ + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 1) + { + PGPPublicKeyRing ring = new PGPPublicKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[0])), new JcaKeyFingerprintCalculator()); + PGPPublicKey key = ring.getPublicKey(); + + // iterate through all direct key signautures and look for NotationData subpackets + Iterator iter = key.getSignaturesOfType(PGPSignature.DIRECT_KEY); + while(iter.hasNext()) + { + PGPSignature sig = (PGPSignature)iter.next(); + + System.out.println("Signature date is: " + sig.getHashedSubPackets().getSignatureCreationTime()); + + NotationData[] data = sig.getHashedSubPackets().getNotationDataOccurences();//.getSubpacket(SignatureSubpacketTags.NOTATION_DATA); + + for (int i = 0; i < data.length; i++) + { + System.out.println("Found Notaion named '"+data[i].getNotationName()+"' with content '"+data[i].getNotationValue()+"'."); + } + } + } + else if (args.length == 5) + { + // gather command line arguments + PGPSecretKeyRing secRing = new PGPSecretKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[0])), new JcaKeyFingerprintCalculator()); + String secretKeyPass = args[1]; + PGPPublicKeyRing ring = new PGPPublicKeyRing(PGPUtil.getDecoderStream(new FileInputStream(args[2])), new JcaKeyFingerprintCalculator()); + String notationName = args[3]; + String notationValue = args[4]; + + // create the signed keyRing + PGPPublicKeyRing sRing = new PGPPublicKeyRing(new ByteArrayInputStream(signPublicKey(secRing.getSecretKey(), secretKeyPass, ring.getPublicKey(), notationName, notationValue, true)), new JcaKeyFingerprintCalculator()); + ring = sRing; + + // write the created keyRing to file + ArmoredOutputStream out = new ArmoredOutputStream(new FileOutputStream("SignedKey.asc")); + sRing.encode(out); + out.flush(); + out.close(); + } + else + { + System.err.println("usage: DirectKeySignature secretKeyFile secretKeyPass publicKeyFile(key to be signed) NotationName NotationValue"); + System.err.println("or: DirectKeySignature signedPublicKeyFile"); + + } + } + + private static byte[] signPublicKey(PGPSecretKey secretKey, String secretKeyPass, PGPPublicKey keyToBeSigned, String notationName, String notationValue, boolean armor) throws Exception + { + OutputStream out = new ByteArrayOutputStream(); + + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(secretKeyPass.toCharArray())); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.DIRECT_KEY, pgpPrivKey); + + BCPGOutputStream bOut = new BCPGOutputStream(out); + + sGen.generateOnePassVersion(false).encode(bOut); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + boolean isHumanReadable = true; + + spGen.setNotationData(true, isHumanReadable, notationName, notationValue); + + PGPSignatureSubpacketVector packetVector = spGen.generate(); + sGen.setHashedSubpackets(packetVector); + + bOut.flush(); + + if (armor) + { + out.close(); + } + + return PGPPublicKey.addCertification(keyToBeSigned, sGen.generate()).getEncoded(); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java new file mode 100644 index 000000000..72f97fe82 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedFileProcessor.java @@ -0,0 +1,279 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts public key based + * encryption files. + * <p> + * To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> + * If -a is specified the output file will be "ascii-armored". + * If -i is specified the output file will be have integrity checking added. + * <p> + * To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase. + * <p> + * Note 1: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * Note 2: if an empty file name has been specified in the literal data object contained in the + * encrypted packet a file with the name filename.out will be generated in the current working directory. + */ +public class KeyBasedFileProcessor +{ + private static void decryptFile( + String inputFileName, + String keyFileName, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + decryptFile(in, keyIn, passwd, defaultFileName); + keyIn.close(); + in.close(); + } + + /** + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + InputStream keyIn, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + in = PGPUtil.getDecoderStream(in); + + try + { + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + + Object o = pgpF.nextObject(); + // + // the first object might be a PGP marker packet. + // + if (o instanceof PGPEncryptedDataList) + { + enc = (PGPEncryptedDataList)o; + } + else + { + enc = (PGPEncryptedDataList)pgpF.nextObject(); + } + + // + // find the secret key + // + Iterator it = enc.getEncryptedDataObjects(); + PGPPrivateKey sKey = null; + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(keyIn)); + + while (sKey == null && it.hasNext()) + { + pbe = (PGPPublicKeyEncryptedData)it.next(); + + sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd); + } + + if (sKey == null) + { + throw new IllegalArgumentException("secret key for message not found."); + } + + InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(sKey)); + + PGPObjectFactory plainFact = new PGPObjectFactory(clear); + + Object message = plainFact.nextObject(); + + if (message instanceof PGPCompressedData) + { + PGPCompressedData cData = (PGPCompressedData)message; + PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream()); + + message = pgpFact.nextObject(); + } + + if (message instanceof PGPLiteralData) + { + PGPLiteralData ld = (PGPLiteralData)message; + + String outFileName = ld.getFileName(); + if (outFileName.length() == 0) + { + outFileName = defaultFileName; + } + + InputStream unc = ld.getInputStream(); + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName)); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + } + else if (message instanceof PGPOnePassSignatureList) + { + throw new PGPException("encrypted message contains a signed message - not literal data."); + } + else + { + throw new PGPException("message is not a simple encrypted file - type unknown."); + } + + if (pbe.isIntegrityProtected()) + { + if (!pbe.verify()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + String encKeyFileName, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException, PGPException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName); + encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + PGPPublicKey encKey, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + byte[] bytes = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( + new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("SC")); + + OutputStream cOut = encGen.open(out, bytes.length); + + cOut.write(bytes); + cOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 0) + { + System.err.println("usage: KeyBasedFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + return; + } + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3], false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2], false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out"); + } + else + { + System.err.println("usage: KeyBasedFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java new file mode 100644 index 000000000..8bcc9049c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/KeyBasedLargeFileProcessor.java @@ -0,0 +1,283 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts public key based + * encryption large files. + * <p> + * To encrypt a file: KeyBasedLargeFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> + * If -a is specified the output file will be "ascii-armored". + * If -i is specified the output file will be have integrity checking added. + * <p> + * To decrypt: KeyBasedLargeFileProcessor -d fileName secretKeyFile passPhrase. + * <p> + * Note 1: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * Note 2: this example generates partial packets to encode the file, the output it generates + * will not be readable by older PGP products or products that don't support partial packet + * encoding. + * <p> + * Note 3: if an empty file name has been specified in the literal data object contained in the + * encrypted packet a file with the name filename.out will be generated in the current working directory. + */ +public class KeyBasedLargeFileProcessor +{ + private static void decryptFile( + String inputFileName, + String keyFileName, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + decryptFile(in, keyIn, passwd, defaultFileName); + keyIn.close(); + in.close(); + } + + /** + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + InputStream keyIn, + char[] passwd, + String defaultFileName) + throws IOException, NoSuchProviderException + { + in = PGPUtil.getDecoderStream(in); + + try + { + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + + Object o = pgpF.nextObject(); + // + // the first object might be a PGP marker packet. + // + if (o instanceof PGPEncryptedDataList) + { + enc = (PGPEncryptedDataList)o; + } + else + { + enc = (PGPEncryptedDataList)pgpF.nextObject(); + } + + // + // find the secret key + // + Iterator it = enc.getEncryptedDataObjects(); + PGPPrivateKey sKey = null; + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(keyIn)); + + while (sKey == null && it.hasNext()) + { + pbe = (PGPPublicKeyEncryptedData)it.next(); + + sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd); + } + + if (sKey == null) + { + throw new IllegalArgumentException("secret key for message not found."); + } + + InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(sKey)); + + PGPObjectFactory plainFact = new PGPObjectFactory(clear); + + PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject(); + + InputStream compressedStream = new BufferedInputStream(cData.getDataStream()); + PGPObjectFactory pgpFact = new PGPObjectFactory(compressedStream); + + Object message = pgpFact.nextObject(); + + if (message instanceof PGPLiteralData) + { + PGPLiteralData ld = (PGPLiteralData)message; + + String outFileName = ld.getFileName(); + if (outFileName.length() == 0) + { + outFileName = defaultFileName; + } + + InputStream unc = ld.getInputStream(); + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName)); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + } + else if (message instanceof PGPOnePassSignatureList) + { + throw new PGPException("encrypted message contains a signed message - not literal data."); + } + else + { + throw new PGPException("message is not a simple encrypted file - type unknown."); + } + + if (pbe.isIntegrityProtected()) + { + if (!pbe.verify()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + String encKeyFileName, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException, PGPException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName); + encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + PGPPublicKey encKey, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("SC")); + + OutputStream cOut = cPk.open(out, new byte[1 << 16]); + + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + PGPUtil.writeFileToLiteralData(comData.open(cOut), PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]); + + comData.close(); + + cOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args.length == 0) + { + System.err.println("usage: KeyBasedLargeFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + return; + } + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3], false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2], false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out"); + } + else + { + System.err.println("usage: KeyBasedLargeFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java new file mode 100644 index 000000000..ee0b14d97 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PBEFileProcessor.java @@ -0,0 +1,214 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.util.io.Streams; + +/** + * A simple utility class that encrypts/decrypts password based + * encryption files. + * <p> + * To encrypt a file: PBEFileProcessor -e [-ai] fileName passPhrase.<br> + * If -a is specified the output file will be "ascii-armored".<br> + * If -i is specified the output file will be "integrity protected". + * <p> + * To decrypt: PBEFileProcessor -d fileName passPhrase. + * <p> + * Note: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + */ +public class PBEFileProcessor +{ + private static void decryptFile(String inputFileName, char[] passPhrase) + throws IOException, NoSuchProviderException, PGPException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + decryptFile(in, passPhrase); + in.close(); + } + + /* + * decrypt the passed in message stream + */ + private static void decryptFile( + InputStream in, + char[] passPhrase) + throws IOException, NoSuchProviderException, PGPException + { + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // + // the first object might be a PGP marker packet. + // + if (o instanceof PGPEncryptedDataList) + { + enc = (PGPEncryptedDataList)o; + } + else + { + enc = (PGPEncryptedDataList)pgpF.nextObject(); + } + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(passPhrase)); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + // + // if we're trying to read a file generated by someone other than us + // the data might not be compressed, so we check the return type from + // the factory and behave accordingly. + // + o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) + { + PGPCompressedData cData = (PGPCompressedData)o; + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + o = pgpFact.nextObject(); + } + + PGPLiteralData ld = (PGPLiteralData)o; + InputStream unc = ld.getInputStream(); + + OutputStream fOut = new BufferedOutputStream(new FileOutputStream(ld.getFileName())); + + Streams.pipeAll(unc, fOut); + + fOut.close(); + + if (pbe.isIntegrityProtected()) + { + if (!pbe.verify()) + { + System.err.println("message failed integrity check"); + } + else + { + System.err.println("message integrity check passed"); + } + } + else + { + System.err.println("no message integrity check"); + } + } + + private static void encryptFile( + String outputFileName, + String inputFileName, + char[] passPhrase, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + encryptFile(out, inputFileName, passPhrase, armor, withIntegrityCheck); + out.close(); + } + + private static void encryptFile( + OutputStream out, + String fileName, + char[] passPhrase, + boolean armor, + boolean withIntegrityCheck) + throws IOException, NoSuchProviderException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + try + { + byte[] compressedData = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5) + .setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("SC")); + + encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(passPhrase).setProvider("SC")); + + OutputStream encOut = encGen.open(out, compressedData.length); + + encOut.write(compressedData); + encOut.close(); + + if (armor) + { + out.close(); + } + } + catch (PGPException e) + { + System.err.println(e); + if (e.getUnderlyingException() != null) + { + e.getUnderlyingException().printStackTrace(); + } + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-e")) + { + if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia")) + { + encryptFile(args[2] + ".asc", args[2], args[3].toCharArray(), true, (args[1].indexOf('i') > 0)); + } + else if (args[1].equals("-i")) + { + encryptFile(args[2] + ".bpg", args[2], args[3].toCharArray(), false, true); + } + else + { + encryptFile(args[1] + ".bpg", args[1], args[2].toCharArray(), false, false); + } + } + else if (args[0].equals("-d")) + { + decryptFile(args[1], args[2].toCharArray()); + } + else + { + System.err.println("usage: PBEFileProcessor -e [-ai]|-d file passPhrase"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java new file mode 100644 index 000000000..1f075ce97 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PGPExampleUtil.java @@ -0,0 +1,154 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchProviderException; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +class PGPExampleUtil +{ + static byte[] compressFile(String fileName, int algorithm) throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(algorithm); + PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, + new File(fileName)); + comData.close(); + return bOut.toByteArray(); + } + + /** + * Search a secret key ring collection for a secret key corresponding to keyID if it + * exists. + * + * @param pgpSec a secret key ring collection. + * @param keyID keyID we want. + * @param pass passphrase to decrypt secret key with. + * @return the private key. + * @throws PGPException + * @throws NoSuchProviderException + */ + static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) + throws PGPException, NoSuchProviderException + { + PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); + + if (pgpSecKey == null) + { + return null; + } + + return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + } + + static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName)); + PGPPublicKey pubKey = readPublicKey(keyIn); + keyIn.close(); + return pubKey; + } + + /** + * A simple routine that opens a key ring file and loads the first available key + * suitable for encryption. + * + * @param input data stream containing the public key data + * @return the first public key found. + * @throws IOException + * @throws PGPException + */ + static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException + { + PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection( + PGPUtil.getDecoderStream(input)); + + // + // we just loop through the collection till we find a key suitable for encryption, in the real + // world you would probably want to be a bit smarter about this. + // + + Iterator keyRingIter = pgpPub.getKeyRings(); + while (keyRingIter.hasNext()) + { + PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next(); + + Iterator keyIter = keyRing.getPublicKeys(); + while (keyIter.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)keyIter.next(); + + if (key.isEncryptionKey()) + { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find encryption key in key ring."); + } + + static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName)); + PGPSecretKey secKey = readSecretKey(keyIn); + keyIn.close(); + return secKey; + } + + /** + * A simple routine that opens a key ring file and loads the first available key + * suitable for signature generation. + * + * @param input stream to read the secret key ring collection from. + * @return a secret key. + * @throws IOException on a problem with using the input stream. + * @throws PGPException if there is an issue parsing the input stream. + */ + static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException + { + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( + PGPUtil.getDecoderStream(input)); + + // + // we just loop through the collection till we find a key suitable for encryption, in the real + // world you would probably want to be a bit smarter about this. + // + + Iterator keyRingIter = pgpSec.getKeyRings(); + while (keyRingIter.hasNext()) + { + PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next(); + + Iterator keyIter = keyRing.getSecretKeys(); + while (keyIter.hasNext()) + { + PGPSecretKey key = (PGPSecretKey)keyIter.next(); + + if (key.isSigningKey()) + { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find signing key in key ring."); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java new file mode 100644 index 000000000..5439502b2 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/PubringDump.java @@ -0,0 +1,102 @@ +package org.spongycastle.openpgp.examples; + +import java.io.*; + +import java.security.Security; +import java.util.Iterator; + +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; + +import org.spongycastle.util.encoders.Hex; + +/** + * Basic class which just lists the contents of the public key file passed + * as an argument. If the file contains more than one "key ring" they are + * listed in the order found. + */ +public class PubringDump +{ + public static String getAlgorithm( + int algId) + { + switch (algId) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + return "RSA_GENERAL"; + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + return "RSA_ENCRYPT"; + case PublicKeyAlgorithmTags.RSA_SIGN: + return "RSA_SIGN"; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + return "ELGAMAL_ENCRYPT"; + case PublicKeyAlgorithmTags.DSA: + return "DSA"; + case PublicKeyAlgorithmTags.EC: + return "EC"; + case PublicKeyAlgorithmTags.ECDSA: + return "ECDSA"; + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + return "ELGAMAL_GENERAL"; + case PublicKeyAlgorithmTags.DIFFIE_HELLMAN: + return "DIFFIE_HELLMAN"; + } + + return "unknown"; + } + + public static void main(String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key rings + // + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection( + PGPUtil.getDecoderStream(new FileInputStream(args[0]))); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + try + { + pgpPub.getPublicKey(); + } + catch (Exception e) + { + e.printStackTrace(); + continue; + } + + Iterator it = pgpPub.getPublicKeys(); + boolean first = true; + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (first) + { + System.out.println("Key ID: " + Long.toHexString(pgpKey.getKeyID())); + first = false; + } + else + { + System.out.println("Key ID: " + Long.toHexString(pgpKey.getKeyID()) + " (subkey)"); + } + System.out.println(" Algorithm: " + getAlgorithm(pgpKey.getAlgorithm())); + System.out.println(" Fingerprint: " + new String(Hex.encode(pgpKey.getFingerprint()))); + } + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java new file mode 100644 index 000000000..99042fd38 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/RSAKeyPairGenerator.java @@ -0,0 +1,114 @@ +package org.spongycastle.openpgp.examples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; + +/** + * A simple utility class that generates a RSA PGPPublicKey/PGPSecretKey pair. + * <p> + * usage: RSAKeyPairGenerator [-a] identity passPhrase + * <p> + * Where identity is the name to be associated with the public key. The keys are placed + * in the files pub.[asc|bpg] and secret.[asc|bpg]. + */ +public class RSAKeyPairGenerator +{ + private static void exportKeyPair( + OutputStream secretOut, + OutputStream publicOut, + PublicKey publicKey, + PrivateKey privateKey, + String identity, + char[] passPhrase, + boolean armor) + throws IOException, InvalidKeyException, NoSuchProviderException, SignatureException, PGPException + { + if (armor) + { + secretOut = new ArmoredOutputStream(secretOut); + } + + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyPair keyPair = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, publicKey, privateKey, new Date()); + PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, identity, sha1Calc, null, null, new JcaPGPContentSignerBuilder(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.CAST5, sha1Calc).setProvider("SC").build(passPhrase)); + + secretKey.encode(secretOut); + + secretOut.close(); + + if (armor) + { + publicOut = new ArmoredOutputStream(publicOut); + } + + PGPPublicKey key = secretKey.getPublicKey(); + + key.encode(publicOut); + + publicOut.close(); + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC"); + + kpg.initialize(1024); + + KeyPair kp = kpg.generateKeyPair(); + + if (args.length < 2) + { + System.out.println("RSAKeyPairGenerator [-a] identity passPhrase"); + System.exit(0); + } + + if (args[0].equals("-a")) + { + if (args.length < 3) + { + System.out.println("RSAKeyPairGenerator [-a] identity passPhrase"); + System.exit(0); + } + + FileOutputStream out1 = new FileOutputStream("secret.asc"); + FileOutputStream out2 = new FileOutputStream("pub.asc"); + + exportKeyPair(out1, out2, kp.getPublic(), kp.getPrivate(), args[1], args[2].toCharArray(), true); + } + else + { + FileOutputStream out1 = new FileOutputStream("secret.bpg"); + FileOutputStream out2 = new FileOutputStream("pub.bpg"); + + exportKeyPair(out1, out2, kp.getPublic(), kp.getPrivate(), args[0], args[1].toCharArray(), false); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java new file mode 100644 index 000000000..ad3311f1c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/examples/SignedFileProcessor.java @@ -0,0 +1,215 @@ +package org.spongycastle.openpgp.examples; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Iterator; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that signs and verifies files. + * <p> + * To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.<br> + * If -a is specified the output file will be "ascii-armored". + * <p> + * To decrypt: SignedFileProcessor -v fileName publicKeyFile. + * <p> + * <b>Note</b>: this example will silently overwrite files, nor does it pay any attention to + * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase + * will have been used. + * <p> + * <b>Note</b>: the example also makes use of PGP compression. If you are having difficulty getting it + * to interoperate with other PGP programs try removing the use of compression first. + */ +public class SignedFileProcessor +{ + /* + * verify the passed in file as being correctly signed. + */ + private static void verifyFile( + InputStream in, + InputStream keyIn) + throws Exception + { + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpFact = new PGPObjectFactory(in); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + int ch; + PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn)); + + PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID()); + FileOutputStream out = new FileOutputStream(p2.getFileName()); + + ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), key); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + out.write(ch); + } + + out.close(); + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (ops.verify(p3.get(0))) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + /** + * Generate an encapsulated signed file. + * + * @param fileName + * @param keyIn + * @param out + * @param pass + * @param armor + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws PGPException + * @throws SignatureException + */ + private static void signFile( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + boolean armor) + throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPSecretKey pgpSec = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + Iterator it = pgpSec.getPublicKey().getUserIDs(); + if (it.hasNext()) + { + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + spGen.setSignerUserID(false, (String)it.next()); + sGen.setHashedSubpackets(spGen.generate()); + } + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZLIB); + + BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out)); + + sGen.generateOnePassVersion(false).encode(bOut); + + File file = new File(fileName); + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, file); + FileInputStream fIn = new FileInputStream(file); + int ch; + + while ((ch = fIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bOut); + + cGen.close(); + + if (armor) + { + out.close(); + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + if (args[1].equals("-a")) + { + FileInputStream keyIn = new FileInputStream(args[3]); + FileOutputStream out = new FileOutputStream(args[2] + ".asc"); + + signFile(args[2], keyIn, out, args[4].toCharArray(), true); + } + else + { + FileInputStream keyIn = new FileInputStream(args[2]); + FileOutputStream out = new FileOutputStream(args[1] + ".bpg"); + + signFile(args[1], keyIn, out, args[3].toCharArray(), false); + } + } + else if (args[0].equals("-v")) + { + FileInputStream in = new FileInputStream(args[1]); + FileInputStream keyIn = new FileInputStream(args[2]); + + verifyFile(in, keyIn); + } + else + { + System.err.println("usage: SignedFileProcessor -v|-s [-a] file keyfile [passPhrase]"); + } + } +}
\ No newline at end of file diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java new file mode 100644 index 000000000..a5a8a2696 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java @@ -0,0 +1,10 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.openpgp.PGPException; + +public interface KeyFingerPrintCalculator +{ + byte[] calculateFingerprint(PublicKeyPacket publicPk) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java new file mode 100644 index 000000000..98af65820 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java @@ -0,0 +1,26 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.bcpg.S2K; +import org.spongycastle.openpgp.PGPException; + +public abstract class PBEDataDecryptorFactory + implements PGPDataDecryptorFactory +{ + private char[] passPhrase; + private PGPDigestCalculatorProvider calculatorProvider; + + protected PBEDataDecryptorFactory(char[] passPhrase, PGPDigestCalculatorProvider calculatorProvider) + { + this.passPhrase = passPhrase; + this.calculatorProvider = calculatorProvider; + } + + public byte[] makeKeyFromPassPhrase(int keyAlgorithm, S2K s2k) + throws PGPException + { + return PGPUtil.makeKeyFromPassPhrase(calculatorProvider, keyAlgorithm, s2k, passPhrase); + } + + public abstract byte[] recoverSessionData(int keyAlgorithm, byte[] key, byte[] seckKeyData) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..a880fcf53 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java @@ -0,0 +1,91 @@ +package org.spongycastle.openpgp.operator; + +import java.security.SecureRandom; + +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket; +import org.spongycastle.openpgp.PGPException; + +public abstract class PBEKeyEncryptionMethodGenerator + extends PGPKeyEncryptionMethodGenerator +{ + private char[] passPhrase; + private PGPDigestCalculator s2kDigestCalculator; + private S2K s2k; + private SecureRandom random; + private int s2kCount; + + protected PBEKeyEncryptionMethodGenerator( + char[] passPhrase, + PGPDigestCalculator s2kDigestCalculator) + { + this(passPhrase, s2kDigestCalculator, 0x60); + } + + protected PBEKeyEncryptionMethodGenerator( + char[] passPhrase, + PGPDigestCalculator s2kDigestCalculator, + int s2kCount) + { + this.passPhrase = passPhrase; + this.s2kDigestCalculator = s2kDigestCalculator; + + if (s2kCount < 0 || s2kCount > 0xff) + { + throw new IllegalArgumentException("s2kCount value outside of range 0 to 255."); + } + + this.s2kCount = s2kCount; + } + + public PBEKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public byte[] getKey(int encAlgorithm) + throws PGPException + { + if (s2k == null) + { + byte[] iv = new byte[8]; + + if (random == null) + { + random = new SecureRandom(); + } + + random.nextBytes(iv); + + s2k = new S2K(s2kDigestCalculator.getAlgorithm(), iv, s2kCount); + } + + return PGPUtil.makeKeyFromPassPhrase(s2kDigestCalculator, encAlgorithm, s2k, passPhrase); + } + + public ContainedPacket generate(int encAlgorithm, byte[] sessionInfo) + throws PGPException + { + byte[] key = getKey(encAlgorithm); + + if (sessionInfo == null) + { + return new SymmetricKeyEncSessionPacket(encAlgorithm, s2k, null); + } + + // + // the passed in session info has the an RSA/ElGamal checksum added to it, for PBE this is not included. + // + byte[] nSessionInfo = new byte[sessionInfo.length - 2]; + + System.arraycopy(sessionInfo, 0, nSessionInfo, 0, nSessionInfo.length); + + return new SymmetricKeyEncSessionPacket(encAlgorithm, s2k, encryptSessionInfo(encAlgorithm, key, nSessionInfo)); + } + + abstract protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] sessionInfo) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java new file mode 100644 index 000000000..2f75702fa --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java @@ -0,0 +1,31 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.bcpg.S2K; +import org.spongycastle.openpgp.PGPException; + +public abstract class PBESecretKeyDecryptor +{ + private char[] passPhrase; + private PGPDigestCalculatorProvider calculatorProvider; + + protected PBESecretKeyDecryptor(char[] passPhrase, PGPDigestCalculatorProvider calculatorProvider) + { + this.passPhrase = passPhrase; + this.calculatorProvider = calculatorProvider; + } + + public PGPDigestCalculator getChecksumCalculator(int hashAlgorithm) + throws PGPException + { + return calculatorProvider.get(hashAlgorithm); + } + + public byte[] makeKeyFromPassPhrase(int keyAlgorithm, S2K s2k) + throws PGPException + { + return PGPUtil.makeKeyFromPassPhrase(calculatorProvider, keyAlgorithm, s2k, passPhrase); + } + + public abstract byte[] recoverKeyData(int encAlgorithm, byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java new file mode 100644 index 000000000..2bd1bf8fe --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java @@ -0,0 +1,104 @@ +package org.spongycastle.openpgp.operator; + +import java.security.SecureRandom; + +import org.spongycastle.bcpg.S2K; +import org.spongycastle.openpgp.PGPException; + +public abstract class PBESecretKeyEncryptor +{ + protected int encAlgorithm; + protected char[] passPhrase; + protected PGPDigestCalculator s2kDigestCalculator; + protected int s2kCount; + protected S2K s2k; + + protected SecureRandom random; + + protected PBESecretKeyEncryptor(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator, SecureRandom random, char[] passPhrase) + { + this(encAlgorithm, s2kDigestCalculator, 0x60, random, passPhrase); + } + + protected PBESecretKeyEncryptor(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator, int s2kCount, SecureRandom random, char[] passPhrase) + { + this.encAlgorithm = encAlgorithm; + this.passPhrase = passPhrase; + this.random = random; + this.s2kDigestCalculator = s2kDigestCalculator; + + if (s2kCount < 0 || s2kCount > 0xff) + { + throw new IllegalArgumentException("s2kCount value outside of range 0 to 255."); + } + + this.s2kCount = s2kCount; + } + + public int getAlgorithm() + { + return encAlgorithm; + } + + public int getHashAlgorithm() + { + if (s2kDigestCalculator != null) + { + return s2kDigestCalculator.getAlgorithm(); + } + + return -1; + } + + public byte[] getKey() + throws PGPException + { + return PGPUtil.makeKeyFromPassPhrase(s2kDigestCalculator, encAlgorithm, s2k, passPhrase); + } + + public S2K getS2K() + { + return s2k; + } + + /** + * Key encryption method invoked for V4 keys and greater. + * + * @param keyData raw key data + * @param keyOff offset into rawe key data + * @param keyLen length of key data to use. + * @return an encryption of the passed in keyData. + * @throws PGPException on error in the underlying encryption process. + */ + public byte[] encryptKeyData(byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + if (s2k == null) + { + byte[] iv = new byte[8]; + + random.nextBytes(iv); + + s2k = new S2K(s2kDigestCalculator.getAlgorithm(), iv, s2kCount); + } + + return encryptKeyData(getKey(), keyData, keyOff, keyLen); + } + + public abstract byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen) + throws PGPException; + + /** + * Encrypt the passed in keyData using the key and the iv provided. + * <p> + * This method is only used for processing version 3 keys. + * </p> + */ + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + throw new PGPException("encryption of version 3 keys not supported."); + } + + public abstract byte[] getCipherIV(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java new file mode 100644 index 000000000..7a3891e62 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java @@ -0,0 +1,20 @@ +package org.spongycastle.openpgp.operator; + +import java.io.OutputStream; + +public interface PGPContentSigner +{ + public OutputStream getOutputStream(); + + byte[] getSignature(); + + byte[] getDigest(); + + int getType(); + + int getHashAlgorithm(); + + int getKeyAlgorithm(); + + long getKeyID(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java new file mode 100644 index 000000000..44f8c8dd6 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java @@ -0,0 +1,10 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; + +public interface PGPContentSignerBuilder +{ + public PGPContentSigner build(final int signatureType, final PGPPrivateKey privateKey) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java new file mode 100644 index 000000000..7ed1aea63 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java @@ -0,0 +1,20 @@ +package org.spongycastle.openpgp.operator; + +import java.io.OutputStream; + +public interface PGPContentVerifier +{ + public OutputStream getOutputStream(); + + int getHashAlgorithm(); + + int getKeyAlgorithm(); + + long getKeyID(); + + /** + * @param expected expected value of the signature on the data. + * @return true if the signature verifies, false otherwise + */ + boolean verify(byte[] expected); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java new file mode 100644 index 000000000..9e8e4a944 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java @@ -0,0 +1,10 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; + +public interface PGPContentVerifierBuilder +{ + public PGPContentVerifier build(final PGPPublicKey publicKey) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java new file mode 100644 index 000000000..44c138bf7 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java @@ -0,0 +1,9 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; + +public interface PGPContentVerifierBuilderProvider +{ + public PGPContentVerifierBuilder get(int keyAlgorithm, int hashAlgorithm) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java new file mode 100644 index 000000000..556e52556 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java @@ -0,0 +1,12 @@ +package org.spongycastle.openpgp.operator; + +import java.io.InputStream; + +public interface PGPDataDecryptor +{ + InputStream getInputStream(InputStream in); + + int getBlockSize(); + + PGPDigestCalculator getIntegrityCalculator(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java new file mode 100644 index 000000000..0d0a41039 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java @@ -0,0 +1,9 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; + +public interface PGPDataDecryptorFactory +{ + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java new file mode 100644 index 000000000..9e87a5651 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java @@ -0,0 +1,5 @@ +package org.spongycastle.openpgp.operator; + +public interface PGPDataDecryptorProvider +{ +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java new file mode 100644 index 000000000..93c1a0a52 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java @@ -0,0 +1,12 @@ +package org.spongycastle.openpgp.operator; + +import java.io.OutputStream; + +public interface PGPDataEncryptor +{ + OutputStream getOutputStream(OutputStream out); + + PGPDigestCalculator getIntegrityCalculator(); + + int getBlockSize(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java new file mode 100644 index 000000000..c68c8d15b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java @@ -0,0 +1,15 @@ +package org.spongycastle.openpgp.operator; + +import java.security.SecureRandom; + +import org.spongycastle.openpgp.PGPException; + +public interface PGPDataEncryptorBuilder +{ + int getAlgorithm(); + + PGPDataEncryptor build(byte[] keyBytes) + throws PGPException; + + SecureRandom getSecureRandom(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java new file mode 100644 index 000000000..32161e08a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java @@ -0,0 +1,35 @@ +package org.spongycastle.openpgp.operator; + +import java.io.OutputStream; + +public interface PGPDigestCalculator +{ + /** + * Return the algorithm number representing the digest implemented by + * this calculator. + * + * @return algorithm number + */ + int getAlgorithm(); + + /** + * Returns a stream that will accept data for the purpose of calculating + * a digest. Use org.spongycastle.util.io.TeeOutputStream if you want to accumulate + * the data on the fly as well. + * + * @return an OutputStream + */ + OutputStream getOutputStream(); + + /** + * Return the digest calculated on what has been written to the calculator's output stream. + * + * @return a digest. + */ + byte[] getDigest(); + + /** + * Reset the underlying digest calculator + */ + void reset(); +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java new file mode 100644 index 000000000..7b74ed960 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java @@ -0,0 +1,9 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; + +public interface PGPDigestCalculatorProvider +{ + PGPDigestCalculator get(int algorithm) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..6aed6e580 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java @@ -0,0 +1,10 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.openpgp.PGPException; + +public abstract class PGPKeyEncryptionMethodGenerator +{ + public abstract ContainedPacket generate(int encAlgorithm, byte[] sessionInfo) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java new file mode 100644 index 000000000..a3e47b18a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java @@ -0,0 +1,216 @@ +package org.spongycastle.openpgp.operator; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.util.Strings; + +/** + * Basic utility class + */ +class PGPUtil + implements HashAlgorithmTags +{ + static byte[] makeKeyFromPassPhrase( + PGPDigestCalculator digestCalculator, + int algorithm, + S2K s2k, + char[] passPhrase) + throws PGPException + { + String algName = null; + int keySize = 0; + + switch (algorithm) + { + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + keySize = 192; + algName = "DES_EDE"; + break; + case SymmetricKeyAlgorithmTags.IDEA: + keySize = 128; + algName = "IDEA"; + break; + case SymmetricKeyAlgorithmTags.CAST5: + keySize = 128; + algName = "CAST5"; + break; + case SymmetricKeyAlgorithmTags.BLOWFISH: + keySize = 128; + algName = "Blowfish"; + break; + case SymmetricKeyAlgorithmTags.SAFER: + keySize = 128; + algName = "SAFER"; + break; + case SymmetricKeyAlgorithmTags.DES: + keySize = 64; + algName = "DES"; + break; + case SymmetricKeyAlgorithmTags.AES_128: + keySize = 128; + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.AES_192: + keySize = 192; + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.AES_256: + keySize = 256; + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.TWOFISH: + keySize = 256; + algName = "Twofish"; + break; + default: + throw new PGPException("unknown symmetric algorithm: " + algorithm); + } + + byte[] pBytes = Strings.toUTF8ByteArray(passPhrase); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + int generatedBytes = 0; + int loopCount = 0; + + if (s2k != null) + { + if (s2k.getHashAlgorithm() != digestCalculator.getAlgorithm()) + { + throw new PGPException("s2k/digestCalculator mismatch"); + } + } + else + { + if (digestCalculator.getAlgorithm() != HashAlgorithmTags.MD5) + { + throw new PGPException("digestCalculator not for MD5"); + } + } + + OutputStream dOut = digestCalculator.getOutputStream(); + + try + { + while (generatedBytes < keyBytes.length) + { + if (s2k != null) + { + for (int i = 0; i != loopCount; i++) + { + dOut.write(0); + } + + byte[] iv = s2k.getIV(); + + switch (s2k.getType()) + { + case S2K.SIMPLE: + dOut.write(pBytes); + break; + case S2K.SALTED: + dOut.write(iv); + dOut.write(pBytes); + break; + case S2K.SALTED_AND_ITERATED: + long count = s2k.getIterationCount(); + dOut.write(iv); + dOut.write(pBytes); + + count -= iv.length + pBytes.length; + + while (count > 0) + { + if (count < iv.length) + { + dOut.write(iv, 0, (int)count); + break; + } + else + { + dOut.write(iv); + count -= iv.length; + } + + if (count < pBytes.length) + { + dOut.write(pBytes, 0, (int)count); + count = 0; + } + else + { + dOut.write(pBytes); + count -= pBytes.length; + } + } + break; + default: + throw new PGPException("unknown S2K type: " + s2k.getType()); + } + } + else + { + for (int i = 0; i != loopCount; i++) + { + dOut.write((byte)0); + } + + dOut.write(pBytes); + } + + dOut.close(); + + byte[] dig = digestCalculator.getDigest(); + + if (dig.length > (keyBytes.length - generatedBytes)) + { + System.arraycopy(dig, 0, keyBytes, generatedBytes, keyBytes.length - generatedBytes); + } + else + { + System.arraycopy(dig, 0, keyBytes, generatedBytes, dig.length); + } + + generatedBytes += dig.length; + + loopCount++; + } + } + catch (IOException e) + { + throw new PGPException("exception calculating digest: " + e.getMessage(), e); + } + + for (int i = 0; i != pBytes.length; i++) + { + pBytes[i] = 0; + } + + return keyBytes; + } + + public static byte[] makeKeyFromPassPhrase( + PGPDigestCalculatorProvider digCalcProvider, + int algorithm, + S2K s2k, + char[] passPhrase) + throws PGPException + { + PGPDigestCalculator digestCalculator; + + if (s2k != null) + { + digestCalculator = digCalcProvider.get(s2k.getHashAlgorithm()); + } + else + { + digestCalculator = digCalcProvider.get(HashAlgorithmTags.MD5); + } + + return makeKeyFromPassPhrase(digestCalculator, algorithm, s2k, passPhrase); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java new file mode 100644 index 000000000..35d2a01d4 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java @@ -0,0 +1,10 @@ +package org.spongycastle.openpgp.operator; + +import org.spongycastle.openpgp.PGPException; + +public interface PublicKeyDataDecryptorFactory + extends PGPDataDecryptorFactory +{ + public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..8030b946e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java @@ -0,0 +1,100 @@ +package org.spongycastle.openpgp.operator; + +import java.io.IOException; +import java.math.BigInteger; + +import org.spongycastle.bcpg.ContainedPacket; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyEncSessionPacket; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; + +public abstract class PublicKeyKeyEncryptionMethodGenerator + extends PGPKeyEncryptionMethodGenerator +{ + private PGPPublicKey pubKey; + + protected PublicKeyKeyEncryptionMethodGenerator( + PGPPublicKey pubKey) + { + this.pubKey = pubKey; + + switch (pubKey.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + break; + case PGPPublicKey.ECDH: + break; + case PGPPublicKey.DSA: + throw new IllegalArgumentException("Can't use DSA for encryption."); + case PGPPublicKey.ECDSA: + throw new IllegalArgumentException("Can't use ECDSA for encryption."); + default: + throw new IllegalArgumentException("unknown asymmetric algorithm: " + pubKey.getAlgorithm()); + } + } + + public byte[][] processSessionInfo( + byte[] encryptedSessionInfo) + throws PGPException + { + byte[][] data; + + switch (pubKey.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + data = new byte[1][]; + + data[0] = convertToEncodedMPI(encryptedSessionInfo); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + byte[] b1 = new byte[encryptedSessionInfo.length / 2]; + byte[] b2 = new byte[encryptedSessionInfo.length / 2]; + + System.arraycopy(encryptedSessionInfo, 0, b1, 0, b1.length); + System.arraycopy(encryptedSessionInfo, b1.length, b2, 0, b2.length); + + data = new byte[2][]; + data[0] = convertToEncodedMPI(b1); + data[1] = convertToEncodedMPI(b2); + break; + case PGPPublicKey.ECDH: + data = new byte[1][]; + + data[0] = encryptedSessionInfo; + break; + default: + throw new PGPException("unknown asymmetric algorithm: " + pubKey.getAlgorithm()); + } + + return data; + } + + private byte[] convertToEncodedMPI(byte[] encryptedSessionInfo) + throws PGPException + { + try + { + return new MPInteger(new BigInteger(1, encryptedSessionInfo)).getEncoded(); + } + catch (IOException e) + { + throw new PGPException("Invalid MPI encoding: " + e.getMessage(), e); + } + } + + public ContainedPacket generate(int encAlgorithm, byte[] sessionInfo) + throws PGPException + { + return new PublicKeyEncSessionPacket(pubKey.getKeyID(), pubKey.getAlgorithm(), processSessionInfo(encryptSessionInfo(pubKey, sessionInfo))); + } + + abstract protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo) + throws PGPException; +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java new file mode 100644 index 000000000..8d76d24e0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java @@ -0,0 +1,116 @@ +package org.spongycastle.openpgp.operator; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.util.encoders.Hex; + +/** + * Calculator for the EC based KDF algorithm described in RFC 6637 + */ +public class RFC6637KDFCalculator +{ + // "Anonymous Sender ", which is the octet sequence + private static final byte[] ANONYMOUS_SENDER = Hex.decode("416E6F6E796D6F75732053656E64657220202020"); + + private final PGPDigestCalculator digCalc; + private final int keyAlgorithm; + + public RFC6637KDFCalculator(PGPDigestCalculator digCalc, int keyAlgorithm) + { + this.digCalc = digCalc; + this.keyAlgorithm = keyAlgorithm; + } + + public byte[] createKey(ASN1ObjectIdentifier curveOID, ECPoint s, byte[] recipientFingerPrint) + throws PGPException + { + try + { + // RFC 6637 - Section 8 + // curve_OID_len = (byte)len(curve_OID); + // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 + // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous + // Sender " || recipient_fingerprint; + // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap + // Compute Z = KDF( S, Z_len, Param ); + ByteArrayOutputStream pOut = new ByteArrayOutputStream(); + + byte[] encOid = curveOID.getEncoded(); + + pOut.write(encOid, 1, encOid.length - 1); + pOut.write(PublicKeyAlgorithmTags.ECDH); + pOut.write(0x03); + pOut.write(0x01); + pOut.write(digCalc.getAlgorithm()); + pOut.write(keyAlgorithm); + pOut.write(ANONYMOUS_SENDER); + pOut.write(recipientFingerPrint); + + return KDF(digCalc, s, getKeyLen(keyAlgorithm), pOut.toByteArray()); + } + catch (IOException e) + { + throw new PGPException("Exception performing KDF: " + e.getMessage(), e); + } + } + + // RFC 6637 - Section 7 + // Implements KDF( X, oBits, Param ); + // Input: point X = (x,y) + // oBits - the desired size of output + // hBits - the size of output of hash function Hash + // Param - octets representing the parameters + // Assumes that oBits <= hBits + // Convert the point X to the octet string, see section 6: + // ZB' = 04 || x || y + // and extract the x portion from ZB' + // ZB = x; + // MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param ); + // return oBits leftmost bits of MB. + private static byte[] KDF(PGPDigestCalculator digCalc, ECPoint s, int keyLen, byte[] param) + throws IOException + { + byte[] ZB = s.getXCoord().getEncoded(); + + OutputStream dOut = digCalc.getOutputStream(); + + dOut.write(0x00); + dOut.write(0x00); + dOut.write(0x00); + dOut.write(0x01); + dOut.write(0x01); + dOut.write(ZB); + dOut.write(param); + + byte[] digest = digCalc.getDigest(); + + byte[] key = new byte[keyLen]; + + System.arraycopy(digest, 0, key, 0, key.length); + + return key; + } + + private static int getKeyLen(int algID) + throws PGPException + { + switch (algID) + { + case SymmetricKeyAlgorithmTags.AES_128: + return 16; + case SymmetricKeyAlgorithmTags.AES_192: + return 24; + case SymmetricKeyAlgorithmTags.AES_256: + return 32; + default: + throw new PGPException("unknown symmetric algorithm ID: " + algID); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java new file mode 100644 index 000000000..5e3102f37 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java @@ -0,0 +1,138 @@ +package org.spongycastle.openpgp.operator.bc; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.digests.MD2Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.digests.TigerDigest; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.AESEngine; +import org.spongycastle.crypto.engines.BlowfishEngine; +import org.spongycastle.crypto.engines.CAST5Engine; +import org.spongycastle.crypto.engines.DESEngine; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.engines.RSABlindedEngine; +import org.spongycastle.crypto.engines.TwofishEngine; +import org.spongycastle.crypto.signers.DSADigestSigner; +import org.spongycastle.crypto.signers.DSASigner; +import org.spongycastle.crypto.signers.RSADigestSigner; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; + +class BcImplProvider +{ + static Digest createDigest(int algorithm) + throws PGPException + { + switch (algorithm) + { + case HashAlgorithmTags.SHA1: + return new SHA1Digest(); + case HashAlgorithmTags.SHA224: + return new SHA224Digest(); + case HashAlgorithmTags.SHA256: + return new SHA256Digest(); + case HashAlgorithmTags.SHA384: + return new SHA384Digest(); + case HashAlgorithmTags.SHA512: + return new SHA512Digest(); + case HashAlgorithmTags.MD2: + return new MD2Digest(); + case HashAlgorithmTags.MD5: + return new MD5Digest(); + case HashAlgorithmTags.RIPEMD160: + return new RIPEMD160Digest(); + case HashAlgorithmTags.TIGER_192: + return new TigerDigest(); + default: + throw new PGPException("cannot recognise digest"); + } + } + + static Signer createSigner(int keyAlgorithm, int hashAlgorithm) + throws PGPException + { + switch(keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + return new RSADigestSigner(createDigest(hashAlgorithm)); + case PublicKeyAlgorithmTags.DSA: + return new DSADigestSigner(new DSASigner(), createDigest(hashAlgorithm)); + default: + throw new PGPException("cannot recognise keyAlgorithm"); + } + } + + static BlockCipher createBlockCipher(int encAlgorithm) + throws PGPException + { + BlockCipher engine; + + switch (encAlgorithm) + { + case SymmetricKeyAlgorithmTags.AES_128: + case SymmetricKeyAlgorithmTags.AES_192: + case SymmetricKeyAlgorithmTags.AES_256: + engine = new AESEngine(); + break; + case SymmetricKeyAlgorithmTags.BLOWFISH: + engine = new BlowfishEngine(); + break; + case SymmetricKeyAlgorithmTags.CAST5: + engine = new CAST5Engine(); + break; + case SymmetricKeyAlgorithmTags.DES: + engine = new DESEngine(); + break; + case SymmetricKeyAlgorithmTags.TWOFISH: + engine = new TwofishEngine(); + break; + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + engine = new DESedeEngine(); + break; + default: + throw new PGPException("cannot recognise cipher"); + } + + return engine; + } + + static AsymmetricBlockCipher createPublicKeyCipher(int encAlgorithm) + throws PGPException + { + AsymmetricBlockCipher c; + + switch (encAlgorithm) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + c = new PKCS1Encoding(new RSABlindedEngine()); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + c = new PKCS1Encoding(new ElGamalEngine()); + break; + case PGPPublicKey.DSA: + throw new PGPException("Can't use DSA for encryption."); + case PGPPublicKey.ECDSA: + throw new PGPException("Can't use ECDSA for encryption."); + default: + throw new PGPException("unknown asymmetric algorithm: " + encAlgorithm); + } + + return c; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java new file mode 100644 index 000000000..9b9b7f26e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java @@ -0,0 +1,68 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.IOException; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.MD5Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; + +public class BcKeyFingerprintCalculator + implements KeyFingerPrintCalculator +{ + public byte[] calculateFingerprint(PublicKeyPacket publicPk) + throws PGPException + { + BCPGKey key = publicPk.getKey(); + Digest digest; + + if (publicPk.getVersion() <= 3) + { + RSAPublicBCPGKey rK = (RSAPublicBCPGKey)key; + + try + { + digest = new MD5Digest(); + + byte[] bytes = new MPInteger(rK.getModulus()).getEncoded(); + digest.update(bytes, 2, bytes.length - 2); + + bytes = new MPInteger(rK.getPublicExponent()).getEncoded(); + digest.update(bytes, 2, bytes.length - 2); + } + catch (IOException e) + { + throw new PGPException("can't encode key components: " + e.getMessage(), e); + } + } + else + { + try + { + byte[] kBytes = publicPk.getEncodedContents(); + + digest = new SHA1Digest(); + + digest.update((byte)0x99); + digest.update((byte)(kBytes.length >> 8)); + digest.update((byte)kBytes.length); + digest.update(kBytes, 0, kBytes.length); + } + catch (IOException e) + { + throw new PGPException("can't encode key components: " + e.getMessage(), e); + } + } + + byte[] digBuf = new byte[digest.getDigestSize()]; + + digest.doFinal(digBuf, 0); + + return digBuf; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java new file mode 100644 index 000000000..e45fee74c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java @@ -0,0 +1,67 @@ +package org.spongycastle.openpgp.operator.bc; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; + +/** + * A decryptor factory for handling PBE decryption operations. + */ +public class BcPBEDataDecryptorFactory + extends PBEDataDecryptorFactory +{ + /** + * Base constructor. + * + * @param pass the passphrase to use as the primary source of key material. + * @param calculatorProvider a digest calculator provider to provide calculators to support the key generation calculation required. + */ + public BcPBEDataDecryptorFactory(char[] pass, BcPGPDigestCalculatorProvider calculatorProvider) + { + super(pass, calculatorProvider); + } + + public byte[] recoverSessionData(int keyAlgorithm, byte[] key, byte[] secKeyData) + throws PGPException + { + try + { + if (secKeyData != null && secKeyData.length > 0) + { + BlockCipher engine = BcImplProvider.createBlockCipher(keyAlgorithm); + BufferedBlockCipher cipher = BcUtil.createSymmetricKeyWrapper(false, engine, key, new byte[engine.getBlockSize()]); + + byte[] out = new byte[secKeyData.length]; + + int len = cipher.processBytes(secKeyData, 0, secKeyData.length, out, 0); + + len += cipher.doFinal(out, len); + + return out; + } + else + { + byte[] keyBytes = new byte[key.length + 1]; + + keyBytes[0] = (byte)keyAlgorithm; + System.arraycopy(key, 0, keyBytes, 1, key.length); + + return keyBytes; + } + } + catch (Exception e) + { + throw new PGPException("Exception recovering session info", e); + } + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + BlockCipher engine = BcImplProvider.createBlockCipher(encAlgorithm); + + return BcUtil.createDataDecryptor(withIntegrityPacket, engine, key); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..8899de920 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java @@ -0,0 +1,97 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +/** + * A BC lightweight method generator for supporting PBE based encryption operations. + */ +public class BcPBEKeyEncryptionMethodGenerator + extends PBEKeyEncryptionMethodGenerator +{ + /** + * Create a PBE encryption method generator using the provided calculator for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kDigestCalculator the digest calculator to use for key calculation. + */ + public BcPBEKeyEncryptionMethodGenerator(char[] passPhrase, PGPDigestCalculator s2kDigestCalculator) + { + super(passPhrase, s2kDigestCalculator); + } + + /** + * Create a PBE encryption method generator using the default SHA-1 digest calculator for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + */ + public BcPBEKeyEncryptionMethodGenerator(char[] passPhrase) + { + this(passPhrase, new SHA1PGPDigestCalculator()); + } + + /** + * Create a PBE encryption method generator using the provided calculator and S2K count for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kDigestCalculator the digest calculator to use for key calculation. + * @param s2kCount the S2K count to use. + */ + public BcPBEKeyEncryptionMethodGenerator(char[] passPhrase, PGPDigestCalculator s2kDigestCalculator, int s2kCount) + { + super(passPhrase, s2kDigestCalculator, s2kCount); + } + + /** + * Create a PBE encryption method generator using the default SHA-1 digest calculator and + * a S2K count other than the default of 0x60 for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kCount the S2K count to use. + */ + public BcPBEKeyEncryptionMethodGenerator(char[] passPhrase, int s2kCount) + { + super(passPhrase, new SHA1PGPDigestCalculator(), s2kCount); + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current generator. + */ + public PBEKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random) + { + super.setSecureRandom(random); + + return this; + } + + protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] sessionInfo) + throws PGPException + { + try + { + BlockCipher engine = BcImplProvider.createBlockCipher(encAlgorithm); + BufferedBlockCipher cipher = BcUtil.createSymmetricKeyWrapper(true, engine, key, new byte[engine.getBlockSize()]); + + byte[] out = new byte[sessionInfo.length]; + + int len = cipher.processBytes(sessionInfo, 0, sessionInfo.length, out, 0); + + len += cipher.doFinal(out, len); + + return out; + } + catch (InvalidCipherTextException e) + { + throw new PGPException("encryption failed: " + e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java new file mode 100644 index 000000000..bf0a0db9a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java @@ -0,0 +1,43 @@ +package org.spongycastle.openpgp.operator.bc; + +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; + +public class BcPBESecretKeyDecryptorBuilder +{ + private PGPDigestCalculatorProvider calculatorProvider; + + public BcPBESecretKeyDecryptorBuilder(PGPDigestCalculatorProvider calculatorProvider) + { + this.calculatorProvider = calculatorProvider; + } + + public PBESecretKeyDecryptor build(char[] passPhrase) + { + return new PBESecretKeyDecryptor(passPhrase, calculatorProvider) + { + public byte[] recoverKeyData(int encAlgorithm, byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + BufferedBlockCipher c = BcUtil.createSymmetricKeyWrapper(false, BcImplProvider.createBlockCipher(encAlgorithm), key, iv); + + byte[] out = new byte[keyLen]; + int outLen = c.processBytes(keyData, keyOff, keyLen, out, 0); + + outLen += c.doFinal(out, outLen); + + return out; + } + catch (InvalidCipherTextException e) + { + throw new PGPException("decryption failed: " + e.getMessage(), e); + } + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java new file mode 100644 index 000000000..aea664ae0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java @@ -0,0 +1,142 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +public class BcPBESecretKeyEncryptorBuilder +{ + private int encAlgorithm; + private PGPDigestCalculator s2kDigestCalculator; + private SecureRandom random; + private int s2kCount = 0x60; + + public BcPBESecretKeyEncryptorBuilder(int encAlgorithm) + { + this(encAlgorithm, new SHA1PGPDigestCalculator()); + } + + /** + * Create an SecretKeyEncryptorBuilder with the S2K count different to the default of 0x60. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kCount iteration count to use for S2K function. + */ + public BcPBESecretKeyEncryptorBuilder(int encAlgorithm, int s2kCount) + { + this(encAlgorithm, new SHA1PGPDigestCalculator(), s2kCount); + } + + /** + * Create a builder which will make encryptors using the passed in digest calculator. If a MD5 calculator is + * passed in the builder will assume the encryptors are for use with version 3 keys. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + */ + public BcPBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator) + { + this(encAlgorithm, s2kDigestCalculator, 0x60); + } + + /** + * Create an SecretKeyEncryptorBuilder with the S2k count different to the default of 0x60, and the S2K digest + * different from SHA-1. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + * @param s2kCount iteration count to use for S2K function. + */ + public BcPBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator, int s2kCount) + { + this.encAlgorithm = encAlgorithm; + this.s2kDigestCalculator = s2kDigestCalculator; + + if (s2kCount < 0 || s2kCount > 0xff) + { + throw new IllegalArgumentException("s2KCount value outside of range 0 to 255."); + } + + this.s2kCount = s2kCount; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current builder. + */ + public BcPBESecretKeyEncryptorBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public PBESecretKeyEncryptor build(char[] passPhrase) + { + if (this.random == null) + { + this.random = new SecureRandom(); + } + + return new PBESecretKeyEncryptor(encAlgorithm, s2kDigestCalculator, s2kCount, this.random, passPhrase) + { + private byte[] iv; + + public byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + return encryptKeyData(key, null, keyData, keyOff, keyLen); + } + + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + BlockCipher engine = BcImplProvider.createBlockCipher(this.encAlgorithm); + + if (iv != null) + { // to deal with V3 key encryption + this.iv = iv; + } + else + { + if (this.random == null) + { + this.random = new SecureRandom(); + } + + this.iv = iv = new byte[engine.getBlockSize()]; + + this.random.nextBytes(iv); + } + + BufferedBlockCipher c = BcUtil.createSymmetricKeyWrapper(true, engine, key, iv); + + byte[] out = new byte[keyLen]; + int outLen = c.processBytes(keyData, keyOff, keyLen, out, 0); + + outLen += c.doFinal(out, outLen); + + return out; + } + catch (InvalidCipherTextException e) + { + throw new PGPException("decryption failed: " + e.getMessage(), e); + } + } + + public byte[] getCipherIV() + { + return iv; + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java new file mode 100644 index 000000000..cd98ef38c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java @@ -0,0 +1,98 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.OutputStream; +import java.security.SecureRandom; + +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.Signer; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.util.io.TeeOutputStream; + +public class BcPGPContentSignerBuilder + implements PGPContentSignerBuilder +{ + private BcPGPDigestCalculatorProvider digestCalculatorProvider = new BcPGPDigestCalculatorProvider(); + private BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + private int hashAlgorithm; + private SecureRandom random; + private int keyAlgorithm; + + public BcPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public BcPGPContentSignerBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public PGPContentSigner build(final int signatureType, final PGPPrivateKey privateKey) + throws PGPException + { + final PGPDigestCalculator digestCalculator = digestCalculatorProvider.get(hashAlgorithm); + final Signer signer = BcImplProvider.createSigner(keyAlgorithm, hashAlgorithm); + + if (random != null) + { + signer.init(true, new ParametersWithRandom(keyConverter.getPrivateKey(privateKey), random)); + } + else + { + signer.init(true, keyConverter.getPrivateKey(privateKey)); + } + + return new PGPContentSigner() + { + public int getType() + { + return signatureType; + } + + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return privateKey.getKeyID(); + } + + public OutputStream getOutputStream() + { + return new TeeOutputStream(new SignerOutputStream(signer), digestCalculator.getOutputStream()); + } + + public byte[] getSignature() + { + try + { + return signer.generateSignature(); + } + catch (CryptoException e) + { // TODO: need a specific runtime exception for PGP operators. + throw new IllegalStateException("unable to create signature"); + } + } + + public byte[] getDigest() + { + return digestCalculator.getDigest(); + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java new file mode 100644 index 000000000..a2cfbf911 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java @@ -0,0 +1,75 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.OutputStream; + +import org.spongycastle.crypto.Signer; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; + +public class BcPGPContentVerifierBuilderProvider + implements PGPContentVerifierBuilderProvider +{ + private BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + public BcPGPContentVerifierBuilderProvider() + { + } + + public PGPContentVerifierBuilder get(int keyAlgorithm, int hashAlgorithm) + throws PGPException + { + return new BcPGPContentVerifierBuilder(keyAlgorithm, hashAlgorithm); + } + + private class BcPGPContentVerifierBuilder + implements PGPContentVerifierBuilder + { + private int hashAlgorithm; + private int keyAlgorithm; + + public BcPGPContentVerifierBuilder(int keyAlgorithm, int hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public PGPContentVerifier build(final PGPPublicKey publicKey) + throws PGPException + { + final Signer signer = BcImplProvider.createSigner(keyAlgorithm, hashAlgorithm); + + signer.init(false, keyConverter.getPublicKey(publicKey)); + + return new PGPContentVerifier() + { + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return publicKey.getKeyID(); + } + + public boolean verify(byte[] expected) + { + return signer.verifySignature(expected); + } + + public OutputStream getOutputStream() + { + return new SignerOutputStream(signer); + } + }; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java new file mode 100644 index 000000000..5c65ce499 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java @@ -0,0 +1,118 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.OutputStream; +import java.security.SecureRandom; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.io.CipherOutputStream; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PGPDataEncryptor; +import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +public class BcPGPDataEncryptorBuilder + implements PGPDataEncryptorBuilder +{ + private SecureRandom random; + private boolean withIntegrityPacket; + private int encAlgorithm; + + public BcPGPDataEncryptorBuilder(int encAlgorithm) + { + this.encAlgorithm = encAlgorithm; + + if (encAlgorithm == 0) + { + throw new IllegalArgumentException("null cipher specified"); + } + } + + /** + * Determine whether or not the resulting encrypted data will be protected using an integrity packet. + * + * @param withIntegrityPacket true if an integrity packet is to be included, false otherwise. + * @return the current builder. + */ + public BcPGPDataEncryptorBuilder setWithIntegrityPacket(boolean withIntegrityPacket) + { + this.withIntegrityPacket = withIntegrityPacket; + + return this; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current builder. + */ + public BcPGPDataEncryptorBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public int getAlgorithm() + { + return encAlgorithm; + } + + public SecureRandom getSecureRandom() + { + if (random == null) + { + random = new SecureRandom(); + } + + return random; + } + + public PGPDataEncryptor build(byte[] keyBytes) + throws PGPException + { + return new MyPGPDataEncryptor(keyBytes); + } + + private class MyPGPDataEncryptor + implements PGPDataEncryptor + { + private final BufferedBlockCipher c; + + MyPGPDataEncryptor(byte[] keyBytes) + throws PGPException + { + BlockCipher engine = BcImplProvider.createBlockCipher(encAlgorithm); + + try + { + c = BcUtil.createStreamCipher(true, engine, withIntegrityPacket, keyBytes); + } + catch (IllegalArgumentException e) + { + throw new PGPException("invalid parameters: " + e.getMessage(), e); + } + } + + public OutputStream getOutputStream(OutputStream out) + { + return new CipherOutputStream(out, c); + } + + public PGPDigestCalculator getIntegrityCalculator() + { + if (withIntegrityPacket) + { + return new SHA1PGPDigestCalculator(); + } + + return null; + } + + public int getBlockSize() + { + return c.getBlockSize(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java new file mode 100644 index 000000000..50d5fc736 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java @@ -0,0 +1,82 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.crypto.Digest; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; + +public class BcPGPDigestCalculatorProvider + implements PGPDigestCalculatorProvider +{ + public PGPDigestCalculator get(final int algorithm) + throws PGPException + { + final Digest dig = BcImplProvider.createDigest(algorithm); + + final DigestOutputStream stream = new DigestOutputStream(dig); + + return new PGPDigestCalculator() + { + public int getAlgorithm() + { + return algorithm; + } + + public OutputStream getOutputStream() + { + return stream; + } + + public byte[] getDigest() + { + return stream.getDigest(); + } + + public void reset() + { + dig.reset(); + } + }; + } + + private class DigestOutputStream + extends OutputStream + { + private Digest dig; + + DigestOutputStream(Digest dig) + { + this.dig = dig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + dig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + dig.update(bytes, 0, bytes.length); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + + byte[] getDigest() + { + byte[] d = new byte[dig.getDigestSize()]; + + dig.doFinal(d, 0); + + return d; + } + } +}
\ No newline at end of file diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java new file mode 100644 index 000000000..309bc8a82 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java @@ -0,0 +1,199 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.util.Date; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSAPublicBCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ECDHPublicBCPGKey; +import org.spongycastle.bcpg.ECDSAPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.DSAParameters; +import org.spongycastle.crypto.params.DSAPrivateKeyParameters; +import org.spongycastle.crypto.params.DSAPublicKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.ElGamalParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.crypto.params.RSAKeyParameters; +import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; + +public class BcPGPKeyConverter +{ + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p/> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @throws PGPException on key creation problem. + */ + public PGPPublicKey getPGPPublicKey(int algorithm, AsymmetricKeyParameter pubKey, Date time) + throws PGPException + { + BCPGKey bcpgKey; + + if (pubKey instanceof RSAKeyParameters) + { + RSAKeyParameters rK = (RSAKeyParameters)pubKey; + + bcpgKey = new RSAPublicBCPGKey(rK.getModulus(), rK.getExponent()); + } + else if (pubKey instanceof DSAPublicKeyParameters) + { + DSAPublicKeyParameters dK = (DSAPublicKeyParameters)pubKey; + DSAParameters dP = dK.getParameters(); + + bcpgKey = new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY()); + } + else if (pubKey instanceof ElGamalPublicKeyParameters) + { + ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters)pubKey; + ElGamalParameters eS = eK.getParameters(); + + bcpgKey = new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY()); + } + else if (pubKey instanceof ECPublicKeyParameters) + { + ECPublicKeyParameters eK = (ECPublicKeyParameters)pubKey; + + if (algorithm == PGPPublicKey.EC) + { // TODO: KDF parameters + bcpgKey = new ECDHPublicBCPGKey(null, eK.getQ(), 0, 0); + } + else + { + bcpgKey = new ECDSAPublicBCPGKey(null, eK.getQ()); + } + } + else + { + throw new PGPException("unknown key class"); + } + + return new PGPPublicKey(new PublicKeyPacket(algorithm, time, bcpgKey), new BcKeyFingerprintCalculator()); + } + + public PGPPrivateKey getPGPPrivateKey(PGPPublicKey pubKey, AsymmetricKeyParameter privKey) + throws PGPException + { + BCPGKey privPk; + + switch (pubKey.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_SIGN: + case PGPPublicKey.RSA_GENERAL: + RSAPrivateCrtKeyParameters rsK = (RSAPrivateCrtKeyParameters)privKey; + + privPk = new RSASecretBCPGKey(rsK.getExponent(), rsK.getP(), rsK.getQ()); + break; + case PGPPublicKey.DSA: + DSAPrivateKeyParameters dsK = (DSAPrivateKeyParameters)privKey; + + privPk = new DSASecretBCPGKey(dsK.getX()); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters)privKey; + + privPk = new ElGamalSecretBCPGKey(esK.getX()); + break; + default: + throw new PGPException("unknown key class"); + } + return new PGPPrivateKey(pubKey.getKeyID(), pubKey.getPublicKeyPacket(), privPk); + } + + public AsymmetricKeyParameter getPublicKey(PGPPublicKey publicKey) + throws PGPException + { + PublicKeyPacket publicPk = publicKey.getPublicKeyPacket(); + + try + { + switch (publicPk.getAlgorithm()) + { + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + RSAPublicBCPGKey rsaK = (RSAPublicBCPGKey)publicPk.getKey(); + + return new RSAKeyParameters(false, rsaK.getModulus(), rsaK.getPublicExponent()); + case PublicKeyAlgorithmTags.DSA: + DSAPublicBCPGKey dsaK = (DSAPublicBCPGKey)publicPk.getKey(); + + return new DSAPublicKeyParameters(dsaK.getY(), new DSAParameters(dsaK.getP(), dsaK.getQ(), dsaK.getG())); + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey)publicPk.getKey(); + + return new ElGamalPublicKeyParameters(elK.getY(), new ElGamalParameters(elK.getP(), elK.getG())); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("exception constructing public key", e); + } + } + + public AsymmetricKeyParameter getPrivateKey(PGPPrivateKey privKey) + throws PGPException + { + PublicKeyPacket pubPk = privKey.getPublicKeyPacket(); + BCPGKey privPk = privKey.getPrivateKeyDataPacket(); + + try + { + switch (pubPk.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: + RSAPublicBCPGKey rsaPub = (RSAPublicBCPGKey)pubPk.getKey(); + RSASecretBCPGKey rsaPriv = (RSASecretBCPGKey)privPk; + + return new RSAPrivateCrtKeyParameters(rsaPriv.getModulus(), rsaPub.getPublicExponent(), rsaPriv.getPrivateExponent(), rsaPriv.getPrimeP(), rsaPriv.getPrimeQ(), rsaPriv.getPrimeExponentP(), rsaPriv.getPrimeExponentQ(), rsaPriv.getCrtCoefficient()); + case PGPPublicKey.DSA: + DSAPublicBCPGKey dsaPub = (DSAPublicBCPGKey)pubPk.getKey(); + DSASecretBCPGKey dsaPriv = (DSASecretBCPGKey)privPk; + + return new DSAPrivateKeyParameters(dsaPriv.getX(), new DSAParameters(dsaPub.getP(), dsaPub.getQ(), dsaPub.getG())); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elPub = (ElGamalPublicBCPGKey)pubPk.getKey(); + ElGamalSecretBCPGKey elPriv = (ElGamalSecretBCPGKey)privPk; + + return new ElGamalPrivateKeyParameters(elPriv.getX(), new ElGamalParameters(elPub.getP(), elPub.getG())); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception constructing key", e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java new file mode 100644 index 000000000..6e182077e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java @@ -0,0 +1,33 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.util.Date; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; + +public class BcPGPKeyPair + extends PGPKeyPair +{ + private static PGPPublicKey getPublicKey(int algorithm, AsymmetricKeyParameter pubKey, Date date) + throws PGPException + { + return new BcPGPKeyConverter().getPGPPublicKey(algorithm, pubKey, date); + } + + private static PGPPrivateKey getPrivateKey(PGPPublicKey pub, AsymmetricKeyParameter privKey) + throws PGPException + { + return new BcPGPKeyConverter().getPGPPrivateKey(pub, privKey); + } + + public BcPGPKeyPair(int algorithm, AsymmetricCipherKeyPair keyPair, Date date) + throws PGPException + { + this.pub = getPublicKey(algorithm, (AsymmetricKeyParameter)keyPair.getPublic(), date); + this.priv = getPrivateKey(this.pub, (AsymmetricKeyParameter)keyPair.getPrivate()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java new file mode 100644 index 000000000..f4844cabb --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java @@ -0,0 +1,100 @@ +package org.spongycastle.openpgp.operator.bc; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedAsymmetricBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; + +/** + * A decryptor factory for handling public key decryption operations. + */ +public class BcPublicKeyDataDecryptorFactory + implements PublicKeyDataDecryptorFactory +{ + private BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + private PGPPrivateKey privKey; + + public BcPublicKeyDataDecryptorFactory(PGPPrivateKey privKey) + { + this.privKey = privKey; + } + + public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) + throws PGPException + { + try + { + AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(keyAlgorithm); + + AsymmetricKeyParameter key = keyConverter.getPrivateKey(privKey); + + BufferedAsymmetricBlockCipher c1 = new BufferedAsymmetricBlockCipher(c); + + c1.init(false, key); + + if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT + || keyAlgorithm == PGPPublicKey.RSA_GENERAL) + { + byte[] bi = secKeyData[0]; + + c1.processBytes(bi, 2, bi.length - 2); + } + else + { + BcPGPKeyConverter converter = new BcPGPKeyConverter(); + ElGamalPrivateKeyParameters parms = (ElGamalPrivateKeyParameters) converter.getPrivateKey(privKey); + int size = (parms.getParameters().getP().bitLength() + 7) / 8; + byte[] tmp = new byte[size]; + + byte[] bi = secKeyData[0]; // encoded MPI + if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... + { + c1.processBytes(bi, 3, bi.length - 3); + } + else + { + System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); + c1.processBytes(tmp, 0, tmp.length); + } + + bi = secKeyData[1]; // encoded MPI + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = 0; + } + + if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... + { + c1.processBytes(bi, 3, bi.length - 3); + } + else + { + System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); + c1.processBytes(tmp, 0, tmp.length); + } + } + + return c1.doFinal(); + } + catch (InvalidCipherTextException e) + { + throw new PGPException("exception encrypting session info: " + e.getMessage(), e); + } + + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + BlockCipher engine = BcImplProvider.createBlockCipher(encAlgorithm); + + return BcUtil.createDataDecryptor(withIntegrityPacket, engine, key); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..224c3733f --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java @@ -0,0 +1,68 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; + +/** + * A method generator for supporting public key based encryption operations. + */ +public class BcPublicKeyKeyEncryptionMethodGenerator + extends PublicKeyKeyEncryptionMethodGenerator +{ + private SecureRandom random; + private BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + /** + * Create a public key encryption method generator with the method to be based on the passed in key. + * + * @param key the public key to use for encryption. + */ + public BcPublicKeyKeyEncryptionMethodGenerator(PGPPublicKey key) + { + super(key); + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current generator. + */ + public BcPublicKeyKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo) + throws PGPException + { + try + { + AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(pubKey.getAlgorithm()); + + AsymmetricKeyParameter key = keyConverter.getPublicKey(pubKey); + + if (random == null) + { + random = new SecureRandom(); + } + + c.init(true, new ParametersWithRandom(key, random)); + + return c.processBlock(sessionInfo, 0, sessionInfo.length); + } + catch (InvalidCipherTextException e) + { + throw new PGPException("exception encrypting session info: " + e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java new file mode 100644 index 000000000..c3be8c832 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java @@ -0,0 +1,75 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.InputStream; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.io.CipherInputStream; +import org.spongycastle.crypto.modes.CFBBlockCipher; +import org.spongycastle.crypto.modes.OpenPGPCFBBlockCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +class BcUtil +{ + static BufferedBlockCipher createStreamCipher(boolean forEncryption, BlockCipher engine, boolean withIntegrityPacket, byte[] key) + { + BufferedBlockCipher c; + + if (withIntegrityPacket) + { + c = new BufferedBlockCipher(new CFBBlockCipher(engine, engine.getBlockSize() * 8)); + } + else + { + c = new BufferedBlockCipher(new OpenPGPCFBBlockCipher(engine)); + } + + KeyParameter keyParameter = new KeyParameter(key); + + if (withIntegrityPacket) + { + c.init(forEncryption, new ParametersWithIV(keyParameter, new byte[engine.getBlockSize()])); + } + else + { + c.init(forEncryption, keyParameter); + } + + return c; + } + + public static PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, BlockCipher engine, byte[] key) + { + final BufferedBlockCipher c = createStreamCipher(false, engine, withIntegrityPacket, key); + + return new PGPDataDecryptor() + { + public InputStream getInputStream(InputStream in) + { + return new CipherInputStream(in, c); + } + + public int getBlockSize() + { + return c.getBlockSize(); + } + + public PGPDigestCalculator getIntegrityCalculator() + { + return new SHA1PGPDigestCalculator(); + } + }; + } + + public static BufferedBlockCipher createSymmetricKeyWrapper(boolean forEncryption, BlockCipher engine, byte[] key, byte[] iv) + { + BufferedBlockCipher c = new BufferedBlockCipher(new CFBBlockCipher(engine, engine.getBlockSize() * 8)); + + c.init(forEncryption, new ParametersWithIV(new KeyParameter(key), iv)); + + return c; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java new file mode 100644 index 000000000..15572a398 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java @@ -0,0 +1,68 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +class SHA1PGPDigestCalculator + implements PGPDigestCalculator +{ + private Digest digest = new SHA1Digest(); + + public int getAlgorithm() + { + return HashAlgorithmTags.SHA1; + } + + public OutputStream getOutputStream() + { + return new DigestOutputStream(digest); + } + + public byte[] getDigest() + { + byte[] d = new byte[digest.getDigestSize()]; + + digest.doFinal(d, 0); + + return d; + } + + public void reset() + { + digest.reset(); + } + + private class DigestOutputStream + extends OutputStream + { + private Digest dig; + + DigestOutputStream(Digest dig) + { + this.dig = dig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + dig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + dig.update(bytes, 0, bytes.length); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java new file mode 100644 index 000000000..cdc2d7e3e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java @@ -0,0 +1,35 @@ +package org.spongycastle.openpgp.operator.bc; + +import java.io.IOException; +import java.io.OutputStream; + +import org.spongycastle.crypto.Signer; + +class SignerOutputStream + extends OutputStream +{ + private Signer sig; + + SignerOutputStream(Signer sig) + { + this.sig = sig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + sig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + sig.update(bytes, 0, bytes.length); + } + + public void write(int b) + throws IOException + { + sig.update((byte)b); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java new file mode 100644 index 000000000..719c319d1 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java @@ -0,0 +1,72 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; + +public class JcaKeyFingerprintCalculator + implements KeyFingerPrintCalculator +{ + public byte[] calculateFingerprint(PublicKeyPacket publicPk) + throws PGPException + { + BCPGKey key = publicPk.getKey(); + + if (publicPk.getVersion() <= 3) + { + RSAPublicBCPGKey rK = (RSAPublicBCPGKey)key; + + try + { + MessageDigest digest = MessageDigest.getInstance("MD5"); + + byte[] bytes = new MPInteger(rK.getModulus()).getEncoded(); + digest.update(bytes, 2, bytes.length - 2); + + bytes = new MPInteger(rK.getPublicExponent()).getEncoded(); + digest.update(bytes, 2, bytes.length - 2); + + return digest.digest(); + } + catch (NoSuchAlgorithmException e) + { + throw new PGPException("can't find MD5", e); + } + catch (IOException e) + { + throw new PGPException("can't encode key components: " + e.getMessage(), e); + } + } + else + { + try + { + byte[] kBytes = publicPk.getEncodedContents(); + + MessageDigest digest = MessageDigest.getInstance("SHA1"); + + digest.update((byte)0x99); + digest.update((byte)(kBytes.length >> 8)); + digest.update((byte)kBytes.length); + digest.update(kBytes); + + return digest.digest(); + } + catch (NoSuchAlgorithmException e) + { + throw new PGPException("can't find SHA1", e); + } + catch (IOException e) + { + throw new PGPException("can't encode key components: " + e.getMessage(), e); + } + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java new file mode 100644 index 000000000..e57032780 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java @@ -0,0 +1,156 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.util.io.TeeOutputStream; + +public class JcaPGPContentSignerBuilder + implements PGPContentSignerBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + private int hashAlgorithm; + private SecureRandom random; + private int keyAlgorithm; + + public JcaPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public JcaPGPContentSignerBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public JcaPGPContentSignerBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + keyConverter.setProvider(provider); + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public JcaPGPContentSignerBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + keyConverter.setProvider(providerName); + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public JcaPGPContentSignerBuilder setDigestProvider(Provider provider) + { + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public JcaPGPContentSignerBuilder setDigestProvider(String providerName) + { + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public PGPContentSigner build(final int signatureType, PGPPrivateKey privateKey) + throws PGPException + { + if (privateKey instanceof JcaPGPPrivateKey) + { + return build(signatureType, privateKey.getKeyID(), ((JcaPGPPrivateKey)privateKey).getPrivateKey()); + } + else + { + return build(signatureType, privateKey.getKeyID(), keyConverter.getPrivateKey(privateKey)); + } + } + + public PGPContentSigner build(final int signatureType, final long keyID, final PrivateKey privateKey) + throws PGPException + { + final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm); + final Signature signature = helper.createSignature(keyAlgorithm, hashAlgorithm); + + try + { + if (random != null) + { + signature.initSign(privateKey, random); + } + else + { + signature.initSign(privateKey); + } + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key.", e); + } + + return new PGPContentSigner() + { + public int getType() + { + return signatureType; + } + + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return keyID; + } + + public OutputStream getOutputStream() + { + return new TeeOutputStream(new SignatureOutputStream(signature), digestCalculator.getOutputStream()); + } + + public byte[] getSignature() + { + try + { + return signature.sign(); + } + catch (SignatureException e) + { // TODO: need a specific runtime exception for PGP operators. + throw new IllegalStateException("unable to create signature"); + } + } + + public byte[] getDigest() + { + return digestCalculator.getDigest(); + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java new file mode 100644 index 000000000..c933c7447 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java @@ -0,0 +1,112 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.Signature; +import java.security.SignatureException; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPContentVerifier; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilder; +import org.spongycastle.openpgp.operator.PGPContentVerifierBuilderProvider; + +public class JcaPGPContentVerifierBuilderProvider + implements PGPContentVerifierBuilderProvider +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + + public JcaPGPContentVerifierBuilderProvider() + { + } + + public JcaPGPContentVerifierBuilderProvider setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + keyConverter.setProvider(provider); + + return this; + } + + public JcaPGPContentVerifierBuilderProvider setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + keyConverter.setProvider(providerName); + + return this; + } + + public PGPContentVerifierBuilder get(int keyAlgorithm, int hashAlgorithm) + throws PGPException + { + return new JcaPGPContentVerifierBuilder(keyAlgorithm, hashAlgorithm); + } + + private class JcaPGPContentVerifierBuilder + implements PGPContentVerifierBuilder + { + private int hashAlgorithm; + private int keyAlgorithm; + + public JcaPGPContentVerifierBuilder(int keyAlgorithm, int hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public PGPContentVerifier build(final PGPPublicKey publicKey) + throws PGPException + { + final Signature signature = helper.createSignature(keyAlgorithm, hashAlgorithm); + + try + { + signature.initVerify(keyConverter.getPublicKey(publicKey)); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key.", e); + } + + return new PGPContentVerifier() + { + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return publicKey.getKeyID(); + } + + public boolean verify(byte[] expected) + { + try + { + return signature.verify(expected); + } + catch (SignatureException e) + { // TODO: need a specific runtime exception for PGP operators. + throw new IllegalStateException("unable to verify signature"); + } + } + + public OutputStream getOutputStream() + { + return new SignatureOutputStream(signature); + } + }; + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java new file mode 100644 index 000000000..a70919cb8 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java @@ -0,0 +1,119 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.Provider; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; + +public class JcaPGPDigestCalculatorProviderBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + + public JcaPGPDigestCalculatorProviderBuilder() + { + } + + public JcaPGPDigestCalculatorProviderBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaPGPDigestCalculatorProviderBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PGPDigestCalculatorProvider build() + throws PGPException + { + return new PGPDigestCalculatorProvider() + { + public PGPDigestCalculator get(final int algorithm) + throws PGPException + { + final DigestOutputStream stream; + final MessageDigest dig; + + try + { + dig = helper.createDigest(algorithm); + + stream = new DigestOutputStream(dig); + } + catch (GeneralSecurityException e) + { + throw new PGPException("exception on setup: " + e, e); + } + + return new PGPDigestCalculator() + { + public int getAlgorithm() + { + return algorithm; + } + + public OutputStream getOutputStream() + { + return stream; + } + + public byte[] getDigest() + { + return stream.getDigest(); + } + + public void reset() + { + dig.reset(); + } + }; + } + }; + } + + private class DigestOutputStream + extends OutputStream + { + private MessageDigest dig; + + DigestOutputStream(MessageDigest dig) + { + this.dig = dig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + dig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + dig.update(bytes); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + + byte[] getDigest() + { + return dig.digest(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java new file mode 100644 index 000000000..2f6e3c031 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java @@ -0,0 +1,373 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Date; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSAPublicBCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ECDHPublicBCPGKey; +import org.spongycastle.bcpg.ECDSAPublicBCPGKey; +import org.spongycastle.bcpg.ECSecretBCPGKey; +import org.spongycastle.bcpg.ElGamalPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.spec.ECNamedCurveSpec; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.jce.spec.ElGamalPrivateKeySpec; +import org.spongycastle.jce.spec.ElGamalPublicKeySpec; +import org.spongycastle.openpgp.PGPAlgorithmParameters; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKdfParameters; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; + +public class JcaPGPKeyConverter +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private KeyFingerPrintCalculator fingerPrintCalculator = new JcaKeyFingerprintCalculator(); + + public JcaPGPKeyConverter setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaPGPKeyConverter setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PublicKey getPublicKey(PGPPublicKey publicKey) + throws PGPException + { + KeyFactory fact; + + PublicKeyPacket publicPk = publicKey.getPublicKeyPacket(); + + try + { + switch (publicPk.getAlgorithm()) + { + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + RSAPublicBCPGKey rsaK = (RSAPublicBCPGKey)publicPk.getKey(); + RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsaK.getModulus(), rsaK.getPublicExponent()); + + fact = helper.createKeyFactory("RSA"); + + return fact.generatePublic(rsaSpec); + case PublicKeyAlgorithmTags.DSA: + DSAPublicBCPGKey dsaK = (DSAPublicBCPGKey)publicPk.getKey(); + DSAPublicKeySpec dsaSpec = new DSAPublicKeySpec(dsaK.getY(), dsaK.getP(), dsaK.getQ(), dsaK.getG()); + + fact = helper.createKeyFactory("DSA"); + + return fact.generatePublic(dsaSpec); + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey)publicPk.getKey(); + ElGamalPublicKeySpec elSpec = new ElGamalPublicKeySpec(elK.getY(), new ElGamalParameterSpec(elK.getP(), elK.getG())); + + fact = helper.createKeyFactory("ElGamal"); + + return fact.generatePublic(elSpec); + case PublicKeyAlgorithmTags.EC: + ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey(); + ECPublicKeySpec ecDhSpec = new ECPublicKeySpec( + new java.security.spec.ECPoint(ecdhK.getPoint().getAffineXCoord().toBigInteger(), ecdhK.getPoint().getAffineYCoord().toBigInteger()), + convertX9Parameters(ecdhK.getCurveOID(), NISTNamedCurves.getByOID(ecdhK.getCurveOID()))); + fact = helper.createKeyFactory("ECDH"); + + return fact.generatePublic(ecDhSpec); + case PublicKeyAlgorithmTags.ECDSA: + ECDSAPublicBCPGKey ecdsaK = (ECDSAPublicBCPGKey)publicPk.getKey(); + ECPublicKeySpec ecDsaSpec = new ECPublicKeySpec( + new java.security.spec.ECPoint(ecdsaK.getPoint().getAffineXCoord().toBigInteger(), ecdsaK.getPoint().getAffineYCoord().toBigInteger()), + convertX9Parameters(ecdsaK.getCurveOID(), NISTNamedCurves.getByOID(ecdsaK.getCurveOID()))); + fact = helper.createKeyFactory("ECDSA"); + + return fact.generatePublic(ecDsaSpec); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("exception constructing public key", e); + } + } + + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p/> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param algorithmParameters additional parameters to be stored against the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @throws PGPException on key creation problem. + */ + public PGPPublicKey getPGPPublicKey(int algorithm, PGPAlgorithmParameters algorithmParameters, PublicKey pubKey, Date time) + throws PGPException + { + BCPGKey bcpgKey; + + if (pubKey instanceof RSAPublicKey) + { + RSAPublicKey rK = (RSAPublicKey)pubKey; + + bcpgKey = new RSAPublicBCPGKey(rK.getModulus(), rK.getPublicExponent()); + } + else if (pubKey instanceof DSAPublicKey) + { + DSAPublicKey dK = (DSAPublicKey)pubKey; + DSAParams dP = dK.getParams(); + + bcpgKey = new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY()); + } + else if (pubKey instanceof ElGamalPublicKey) + { + ElGamalPublicKey eK = (ElGamalPublicKey)pubKey; + ElGamalParameterSpec eS = eK.getParameters(); + + bcpgKey = new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY()); + } + else if (pubKey instanceof ECPublicKey) + { + SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()); + + // TODO: should probably match curve by comparison as well + ASN1ObjectIdentifier curveOid = ASN1ObjectIdentifier.getInstance(keyInfo.getAlgorithm().getParameters()); + + X9ECParameters params = NISTNamedCurves.getByOID(curveOid); + + ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes()); + X9ECPoint derQ = new X9ECPoint(params.getCurve(), key); + + if (algorithm == PGPPublicKey.EC) + { + PGPKdfParameters kdfParams = (PGPKdfParameters)algorithmParameters; + if (kdfParams == null) + { + // We default to these as they are specified as mandatory in RFC 6631. + kdfParams = new PGPKdfParameters(HashAlgorithmTags.SHA256, SymmetricKeyAlgorithmTags.AES_128); + } + bcpgKey = new ECDHPublicBCPGKey(curveOid, derQ.getPoint(), kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm()); + } + else + { + bcpgKey = new ECDSAPublicBCPGKey(curveOid, derQ.getPoint()); + } + } + else + { + throw new PGPException("unknown key class"); + } + + return new PGPPublicKey(new PublicKeyPacket(algorithm, time, bcpgKey), fingerPrintCalculator); + } + + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p/> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @throws PGPException on key creation problem. + */ + public PGPPublicKey getPGPPublicKey(int algorithm, PublicKey pubKey, Date time) + throws PGPException + { + return getPGPPublicKey(algorithm, null, pubKey, time); + } + + public PrivateKey getPrivateKey(PGPPrivateKey privKey) + throws PGPException + { + if (privKey instanceof JcaPGPPrivateKey) + { + return ((JcaPGPPrivateKey)privKey).getPrivateKey(); + } + + PublicKeyPacket pubPk = privKey.getPublicKeyPacket(); + BCPGKey privPk = privKey.getPrivateKeyDataPacket(); + + try + { + KeyFactory fact; + + switch (pubPk.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: + RSAPublicBCPGKey rsaPub = (RSAPublicBCPGKey)pubPk.getKey(); + RSASecretBCPGKey rsaPriv = (RSASecretBCPGKey)privPk; + RSAPrivateCrtKeySpec rsaPrivSpec = new RSAPrivateCrtKeySpec( + rsaPriv.getModulus(), + rsaPub.getPublicExponent(), + rsaPriv.getPrivateExponent(), + rsaPriv.getPrimeP(), + rsaPriv.getPrimeQ(), + rsaPriv.getPrimeExponentP(), + rsaPriv.getPrimeExponentQ(), + rsaPriv.getCrtCoefficient()); + + fact = helper.createKeyFactory("RSA"); + + return fact.generatePrivate(rsaPrivSpec); + case PGPPublicKey.DSA: + DSAPublicBCPGKey dsaPub = (DSAPublicBCPGKey)pubPk.getKey(); + DSASecretBCPGKey dsaPriv = (DSASecretBCPGKey)privPk; + DSAPrivateKeySpec dsaPrivSpec = + new DSAPrivateKeySpec(dsaPriv.getX(), dsaPub.getP(), dsaPub.getQ(), dsaPub.getG()); + + fact = helper.createKeyFactory("DSA"); + + return fact.generatePrivate(dsaPrivSpec); + case PublicKeyAlgorithmTags.ECDH: + ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey(); + ECSecretBCPGKey ecdhK = (ECSecretBCPGKey)privPk; + ECPrivateKeySpec ecDhSpec = new ECPrivateKeySpec( + ecdhK.getX(), + convertX9Parameters(ecdhPub.getCurveOID(), NISTNamedCurves.getByOID(ecdhPub.getCurveOID()))); + fact = helper.createKeyFactory("ECDH"); + + return fact.generatePrivate(ecDhSpec); + case PublicKeyAlgorithmTags.ECDSA: + ECDSAPublicBCPGKey ecdsaPub = (ECDSAPublicBCPGKey)pubPk.getKey(); + ECSecretBCPGKey ecdsaK = (ECSecretBCPGKey)privPk; + ECPrivateKeySpec ecDsaSpec = new ECPrivateKeySpec( + ecdsaK.getX(), + convertX9Parameters(ecdsaPub.getCurveOID(), NISTNamedCurves.getByOID(ecdsaPub.getCurveOID()))); + fact = helper.createKeyFactory("ECDSA"); + + return fact.generatePrivate(ecDsaSpec); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elPub = (ElGamalPublicBCPGKey)pubPk.getKey(); + ElGamalSecretBCPGKey elPriv = (ElGamalSecretBCPGKey)privPk; + ElGamalPrivateKeySpec elSpec = new ElGamalPrivateKeySpec(elPriv.getX(), new ElGamalParameterSpec(elPub.getP(), elPub.getG())); + + fact = helper.createKeyFactory("ElGamal"); + + return fact.generatePrivate(elSpec); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception constructing key", e); + } + } + + /** + * Convert a PrivateKey into a PGPPrivateKey. + * + * @param pub the corresponding PGPPublicKey to privKey. + * @param privKey the private key for the key in pub. + * @return a PGPPrivateKey + * @throws PGPException + */ + public PGPPrivateKey getPGPPrivateKey(PGPPublicKey pub, PrivateKey privKey) + throws PGPException + { + BCPGKey privPk; + + switch (pub.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_SIGN: + case PGPPublicKey.RSA_GENERAL: + RSAPrivateCrtKey rsK = (RSAPrivateCrtKey)privKey; + + privPk = new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ()); + break; + case PGPPublicKey.DSA: + DSAPrivateKey dsK = (DSAPrivateKey)privKey; + + privPk = new DSASecretBCPGKey(dsK.getX()); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPrivateKey esK = (ElGamalPrivateKey)privKey; + + privPk = new ElGamalSecretBCPGKey(esK.getX()); + break; + case PGPPublicKey.EC: + case PGPPublicKey.ECDSA: + ECPrivateKey ecK = (ECPrivateKey)privKey; + + privPk = new ECSecretBCPGKey(ecK.getS()); + break; + default: + throw new PGPException("unknown key class"); + } + + return new PGPPrivateKey(pub.getKeyID(), pub.getPublicKeyPacket(), privPk); + } + + private ECParameterSpec convertX9Parameters(ASN1ObjectIdentifier curveOid, X9ECParameters curveParameters) + { + return new ECNamedCurveSpec(curveOid.getId(), + curveParameters.getCurve(), + curveParameters.getG(), + curveParameters.getN(), + curveParameters.getH(), + curveParameters.getSeed()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java new file mode 100644 index 000000000..b32d00d74 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java @@ -0,0 +1,48 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Date; + +import org.spongycastle.openpgp.PGPAlgorithmParameters; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; + +public class JcaPGPKeyPair + extends PGPKeyPair +{ + private static PGPPublicKey getPublicKey(int algorithm, PublicKey pubKey, Date date) + throws PGPException + { + return new JcaPGPKeyConverter().getPGPPublicKey(algorithm, pubKey, date); + } + + private static PGPPublicKey getPublicKey(int algorithm, PGPAlgorithmParameters algorithmParameters, PublicKey pubKey, Date date) + throws PGPException + { + return new JcaPGPKeyConverter().getPGPPublicKey(algorithm, algorithmParameters, pubKey, date); + } + + private static PGPPrivateKey getPrivateKey(PGPPublicKey pub, PrivateKey privKey) + throws PGPException + { + return new JcaPGPKeyConverter().getPGPPrivateKey(pub, privKey); + } + + public JcaPGPKeyPair(int algorithm, KeyPair keyPair, Date date) + throws PGPException + { + this.pub = getPublicKey(algorithm, keyPair.getPublic(), date); + this.priv = getPrivateKey(this.pub, keyPair.getPrivate()); + } + + public JcaPGPKeyPair(int algorithm, PGPAlgorithmParameters parameters, KeyPair keyPair, Date date) + throws PGPException + { + this.pub = getPublicKey(algorithm, parameters, keyPair.getPublic(), date); + this.priv = getPrivateKey(this.pub, keyPair.getPrivate()); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java new file mode 100644 index 000000000..a22f4561e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java @@ -0,0 +1,34 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.PrivateKey; + +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; + +/** + * A JCA PrivateKey carrier. Use this one if you're dealing with a hardware adapter. + */ +public class JcaPGPPrivateKey + extends PGPPrivateKey +{ + private final PrivateKey privateKey; + + public JcaPGPPrivateKey(long keyID, PrivateKey privateKey) + { + super(keyID, null, null); + + this.privateKey = privateKey; + } + + public JcaPGPPrivateKey(PGPPublicKey pubKey, PrivateKey privateKey) + { + super(pubKey.getKeyID(), pubKey.getPublicKeyPacket(), null); + + this.privateKey = privateKey; + } + + public PrivateKey getPrivateKey() + { + return privateKey; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java new file mode 100644 index 000000000..7f18d34b8 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java @@ -0,0 +1,99 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; + +public class JcePBEDataDecryptorFactoryBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private PGPDigestCalculatorProvider calculatorProvider; + + /** + * Base constructor. + * + * @param calculatorProvider a digest calculator provider to provide calculators to support the key generation calculation required. + */ + public JcePBEDataDecryptorFactoryBuilder(PGPDigestCalculatorProvider calculatorProvider) + { + this.calculatorProvider = calculatorProvider; + } + + /** + * Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param provider provider object for cryptographic primitives. + * @return the current builder. + */ + public JcePBEDataDecryptorFactoryBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + /** + * Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param providerName the name of the provider to reference for cryptographic primitives. + * @return the current builder. + */ + public JcePBEDataDecryptorFactoryBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PBEDataDecryptorFactory build(char[] passPhrase) + { + return new PBEDataDecryptorFactory(passPhrase, calculatorProvider) + { + public byte[] recoverSessionData(int keyAlgorithm, byte[] key, byte[] secKeyData) + throws PGPException + { + try + { + if (secKeyData != null && secKeyData.length > 0) + { + String cipherName = PGPUtil.getSymmetricCipherName(keyAlgorithm); + Cipher keyCipher = helper.createCipher(cipherName + "/CFB/NoPadding"); + + keyCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, cipherName), new IvParameterSpec(new byte[keyCipher.getBlockSize()])); + + return keyCipher.doFinal(secKeyData); + } + else + { + byte[] keyBytes = new byte[key.length + 1]; + + keyBytes[0] = (byte)keyAlgorithm; + System.arraycopy(key, 0, keyBytes, 1, key.length); + + return keyBytes; + } + } + catch (Exception e) + { + throw new PGPException("Exception recovering session info", e); + } + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + return helper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..1cb79f769 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java @@ -0,0 +1,132 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +/** + * JCE based generator for password based encryption (PBE) data protection methods. + */ +public class JcePBEKeyEncryptionMethodGenerator + extends PBEKeyEncryptionMethodGenerator +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + + /** + * Create a PBE encryption method generator using the provided calculator for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kDigestCalculator the digest calculator to use for key calculation. + */ + public JcePBEKeyEncryptionMethodGenerator(char[] passPhrase, PGPDigestCalculator s2kDigestCalculator) + { + super(passPhrase, s2kDigestCalculator); + } + + /** + * Create a PBE encryption method generator using the default SHA-1 digest calculator for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + */ + public JcePBEKeyEncryptionMethodGenerator(char[] passPhrase) + { + this(passPhrase, new SHA1PGPDigestCalculator()); + } + + /** + * Create a PBE encryption method generator using the provided calculator and S2K count for key calculation. + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kDigestCalculator the digest calculator to use for key calculation. + * @param s2kCount the S2K count to use. + */ + public JcePBEKeyEncryptionMethodGenerator(char[] passPhrase, PGPDigestCalculator s2kDigestCalculator, int s2kCount) + { + super(passPhrase, s2kDigestCalculator, s2kCount); + } + + /** + * Create a PBE encryption method generator using the default SHA-1 digest calculator and + * a S2K count other than the default of 0x60 for key calculation + * + * @param passPhrase the passphrase to use as the primary source of key material. + * @param s2kCount the S2K count to use. + */ + public JcePBEKeyEncryptionMethodGenerator(char[] passPhrase, int s2kCount) + { + super(passPhrase, new SHA1PGPDigestCalculator(), s2kCount); + } + + public JcePBEKeyEncryptionMethodGenerator setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcePBEKeyEncryptionMethodGenerator setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current generator. + */ + public PBEKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random) + { + super.setSecureRandom(random); + + return this; + } + + protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] sessionInfo) + throws PGPException + { + try + { + String cName = PGPUtil.getSymmetricCipherName(encAlgorithm); + Cipher c = helper.createCipher(cName + "/CFB/NoPadding"); + SecretKey sKey = new SecretKeySpec(key, PGPUtil.getSymmetricCipherName(encAlgorithm)); + + c.init(Cipher.ENCRYPT_MODE, sKey, new IvParameterSpec(new byte[c.getBlockSize()])); + + return c.doFinal(sessionInfo, 0, sessionInfo.length); + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidAlgorithmParameterException e) + { + throw new PGPException("IV invalid: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("key invalid: " + e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java new file mode 100644 index 000000000..e62e4674e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java @@ -0,0 +1,100 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Provider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.spec.IvParameterSpec; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; + +public class JcePBESecretKeyDecryptorBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private PGPDigestCalculatorProvider calculatorProvider; + + private JcaPGPDigestCalculatorProviderBuilder calculatorProviderBuilder; + + public JcePBESecretKeyDecryptorBuilder() + { + this.calculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + } + + public JcePBESecretKeyDecryptorBuilder(PGPDigestCalculatorProvider calculatorProvider) + { + this.calculatorProvider = calculatorProvider; + } + + public JcePBESecretKeyDecryptorBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + if (calculatorProviderBuilder != null) + { + calculatorProviderBuilder.setProvider(provider); + } + + return this; + } + + public JcePBESecretKeyDecryptorBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + if (calculatorProviderBuilder != null) + { + calculatorProviderBuilder.setProvider(providerName); + } + + return this; + } + + public PBESecretKeyDecryptor build(char[] passPhrase) + throws PGPException + { + if (calculatorProvider == null) + { + calculatorProvider = calculatorProviderBuilder.build(); + } + + return new PBESecretKeyDecryptor(passPhrase, calculatorProvider) + { + public byte[] recoverKeyData(int encAlgorithm, byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + Cipher c = helper.createCipher(PGPUtil.getSymmetricCipherName(encAlgorithm) + "/CFB/NoPadding"); + + c.init(Cipher.DECRYPT_MODE, PGPUtil.makeSymmetricKey(encAlgorithm, key), new IvParameterSpec(iv)); + + return c.doFinal(keyData, keyOff, keyLen); + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidAlgorithmParameterException e) + { + throw new PGPException("invalid parameter: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key: " + e.getMessage(), e); + } + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java new file mode 100644 index 000000000..7b18790ab --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java @@ -0,0 +1,180 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.spec.IvParameterSpec; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +public class JcePBESecretKeyEncryptorBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private int encAlgorithm; + private PGPDigestCalculator s2kDigestCalculator; + private SecureRandom random; + private int s2kCount = 0x60; + + public JcePBESecretKeyEncryptorBuilder(int encAlgorithm) + { + this(encAlgorithm, new SHA1PGPDigestCalculator()); + } + + /** + * Create a SecretKeyEncryptorBuilder with the S2K count different to the default of 0x60. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kCount iteration count to use for S2K function. + */ + public JcePBESecretKeyEncryptorBuilder(int encAlgorithm, int s2kCount) + { + this(encAlgorithm, new SHA1PGPDigestCalculator(), s2kCount); + } + + /** + * Create a builder which will make encryptors using the passed in digest calculator. If a MD5 calculator is + * passed in the builder will assume the encryptors are for use with version 3 keys. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + */ + public JcePBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator) + { + this(encAlgorithm, s2kDigestCalculator, 0x60); + } + + /** + * Create an SecretKeyEncryptorBuilder with the S2k count different to the default of 0x60, and the S2K digest + * different from SHA-1. + * + * @param encAlgorithm encryption algorithm to use. + * @param s2kDigestCalculator digest calculator to use. + * @param s2kCount iteration count to use for S2K function. + */ + public JcePBESecretKeyEncryptorBuilder(int encAlgorithm, PGPDigestCalculator s2kDigestCalculator, int s2kCount) + { + this.encAlgorithm = encAlgorithm; + this.s2kDigestCalculator = s2kDigestCalculator; + + if (s2kCount < 0 || s2kCount > 0xff) + { + throw new IllegalArgumentException("s2KCount value outside of range 0 to 255."); + } + + this.s2kCount = s2kCount; + } + + public JcePBESecretKeyEncryptorBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcePBESecretKeyEncryptorBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current builder. + */ + public JcePBESecretKeyEncryptorBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public PBESecretKeyEncryptor build(char[] passPhrase) + { + if (random == null) + { + random = new SecureRandom(); + } + + return new PBESecretKeyEncryptor(encAlgorithm, s2kDigestCalculator, s2kCount, random, passPhrase) + { + private Cipher c; + private byte[] iv; + + public byte[] encryptKeyData(byte[] key, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + c = helper.createCipher(PGPUtil.getSymmetricCipherName(this.encAlgorithm) + "/CFB/NoPadding"); + + c.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(this.encAlgorithm, key), this.random); + + iv = c.getIV(); + + return c.doFinal(keyData, keyOff, keyLen); + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key: " + e.getMessage(), e); + } + } + + public byte[] encryptKeyData(byte[] key, byte[] iv, byte[] keyData, int keyOff, int keyLen) + throws PGPException + { + try + { + c = helper.createCipher(PGPUtil.getSymmetricCipherName(this.encAlgorithm) + "/CFB/NoPadding"); + + c.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(this.encAlgorithm, key), new IvParameterSpec(iv)); + + this.iv = iv; + + return c.doFinal(keyData, keyOff, keyLen); + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key: " + e.getMessage(), e); + } + catch (InvalidAlgorithmParameterException e) + { + throw new PGPException("invalid iv: " + e.getMessage(), e); + } + } + + public byte[] getCipherIV() + { + return iv; + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java new file mode 100644 index 000000000..ba6e793ba --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java @@ -0,0 +1,146 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.IvParameterSpec; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.operator.PGPDataEncryptor; +import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +public class JcePGPDataEncryptorBuilder + implements PGPDataEncryptorBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private SecureRandom random; + private boolean withIntegrityPacket; + private int encAlgorithm; + + public JcePGPDataEncryptorBuilder(int encAlgorithm) + { + this.encAlgorithm = encAlgorithm; + + if (encAlgorithm == 0) + { + throw new IllegalArgumentException("null cipher specified"); + } + } + + /** + * Determine whether or not the resulting encrypted data will be protected using an integrity packet. + * + * @param withIntegrityPacket true if an integrity packet is to be included, false otherwise. + * @return the current builder. + */ + public JcePGPDataEncryptorBuilder setWithIntegrityPacket(boolean withIntegrityPacket) + { + this.withIntegrityPacket = withIntegrityPacket; + + return this; + } + + public JcePGPDataEncryptorBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcePGPDataEncryptorBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current builder. + */ + public JcePGPDataEncryptorBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public int getAlgorithm() + { + return encAlgorithm; + } + + public SecureRandom getSecureRandom() + { + if (random == null) + { + random = new SecureRandom(); + } + + return random; + } + + public PGPDataEncryptor build(byte[] keyBytes) + throws PGPException + { + return new MyPGPDataEncryptor(keyBytes); + } + + private class MyPGPDataEncryptor + implements PGPDataEncryptor + { + private final Cipher c; + + MyPGPDataEncryptor(byte[] keyBytes) + throws PGPException + { + c = helper.createStreamCipher(encAlgorithm, withIntegrityPacket); + + byte[] iv = new byte[c.getBlockSize()]; + + try + { + c.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(encAlgorithm, keyBytes), new IvParameterSpec(iv)); + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key: " + e.getMessage(), e); + } + catch (InvalidAlgorithmParameterException e) + { + throw new PGPException("imvalid algorithm parameter: " + e.getMessage(), e); + } + } + + public OutputStream getOutputStream(OutputStream out) + { + return new CipherOutputStream(out, c); + } + + public PGPDigestCalculator getIntegrityCalculator() + { + if (withIntegrityPacket) + { + return new SHA1PGPDigestCalculator(); + } + + return null; + } + + public int getBlockSize() + { + return c.getBlockSize(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java new file mode 100644 index 000000000..344849fef --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java @@ -0,0 +1,241 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.ECDHPublicBCPGKey; +import org.spongycastle.bcpg.ECSecretBCPGKey; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.jce.interfaces.ElGamalKey; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.RFC6637KDFCalculator; + +public class JcePublicKeyDataDecryptorFactoryBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private OperatorHelper contentHelper = new OperatorHelper(new DefaultJcaJceHelper()); + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + private JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator(); + + public JcePublicKeyDataDecryptorFactoryBuilder() + { + } + + /** + * Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param provider provider object for cryptographic primitives. + * @return the current builder. + */ + public JcePublicKeyDataDecryptorFactoryBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + keyConverter.setProvider(provider); + this.contentHelper = helper; + + return this; + } + + /** + * Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces. + * + * @param providerName the name of the provider to reference for cryptographic primitives. + * @return the current builder. + */ + public JcePublicKeyDataDecryptorFactoryBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + keyConverter.setProvider(providerName); + this.contentHelper = helper; + + return this; + } + + public JcePublicKeyDataDecryptorFactoryBuilder setContentProvider(Provider provider) + { + this.contentHelper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcePublicKeyDataDecryptorFactoryBuilder setContentProvider(String providerName) + { + this.contentHelper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PublicKeyDataDecryptorFactory build(final PrivateKey privKey) + { + return new PublicKeyDataDecryptorFactory() + { + public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) + throws PGPException + { + if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) + { + throw new PGPException("ECDH requires use of PGPPrivateKey for decryption"); + } + return decryptSessionData(keyAlgorithm, privKey, secKeyData); + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); + } + }; + } + + public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey) + { + return new PublicKeyDataDecryptorFactory() + { + public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) + throws PGPException + { + if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) + { + return decryptSessionData(privKey.getPrivateKeyDataPacket(), privKey.getPublicKeyPacket(), secKeyData); + } + + return decryptSessionData(keyAlgorithm, keyConverter.getPrivateKey(privKey), secKeyData); + } + + public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key); + } + }; + } + + private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData) + throws PGPException + { + ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey(); + X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID()); + ECDomainParameters ecParams = new ECDomainParameters(x9Params.getCurve(), x9Params.getG(), x9Params.getN()); + + byte[] enc = secKeyData[0]; + + int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8; + byte[] pEnc = new byte[pLen]; + + System.arraycopy(enc, 2, pEnc, 0, pLen); + + byte[] keyEnc = new byte[enc[pLen + 2]]; + + System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length); + + Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm()); + + ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize(); + + RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm()); + + Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap"); + + try + { + c.init(Cipher.UNWRAP_MODE, key); + + Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY); + + return PGPUtil.unpadSessionData(paddedSessionKey.getEncoded()); + } + catch (InvalidKeyException e) + { + throw new PGPException("error setting asymmetric cipher", e); + } + catch (NoSuchAlgorithmException e) + { + throw new PGPException("error setting asymmetric cipher", e); + } + } + + private byte[] decryptSessionData(int keyAlgorithm, PrivateKey privKey, byte[][] secKeyData) + throws PGPException + { + Cipher c1 = helper.createPublicKeyCipher(keyAlgorithm); + + try + { + c1.init(Cipher.DECRYPT_MODE, privKey); + } + catch (InvalidKeyException e) + { + throw new PGPException("error setting asymmetric cipher", e); + } + + if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT + || keyAlgorithm == PGPPublicKey.RSA_GENERAL) + { + byte[] bi = secKeyData[0]; // encoded MPI + + c1.update(bi, 2, bi.length - 2); + } + else + { + ElGamalKey k = (ElGamalKey)privKey; + int size = (k.getParameters().getP().bitLength() + 7) / 8; + byte[] tmp = new byte[size]; + + byte[] bi = secKeyData[0]; // encoded MPI + if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... + { + c1.update(bi, 3, bi.length - 3); + } + else + { + System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); + c1.update(tmp); + } + + bi = secKeyData[1]; // encoded MPI + for (int i = 0; i != tmp.length; i++) + { + tmp[i] = 0; + } + + if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... + { + c1.update(bi, 3, bi.length - 3); + } + else + { + System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); + c1.update(tmp); + } + } + + try + { + return c1.doFinal(); + } + catch (Exception e) + { + throw new PGPException("exception decrypting session data", e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java new file mode 100644 index 000000000..5a2708cf8 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java @@ -0,0 +1,165 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.bcpg.ECDHPublicBCPGKey; +import org.spongycastle.bcpg.MPInteger; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.crypto.EphemeralKeyPair; +import org.spongycastle.crypto.KeyEncoder; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.math.ec.ECPoint; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.RFC6637KDFCalculator; + +public class JcePublicKeyKeyEncryptionMethodGenerator + extends PublicKeyKeyEncryptionMethodGenerator +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private SecureRandom random; + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + + /** + * Create a public key encryption method generator with the method to be based on the passed in key. + * + * @param key the public key to use for encryption. + */ + public JcePublicKeyKeyEncryptionMethodGenerator(PGPPublicKey key) + { + super(key); + } + + public JcePublicKeyKeyEncryptionMethodGenerator setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + keyConverter.setProvider(provider); + + return this; + } + + public JcePublicKeyKeyEncryptionMethodGenerator setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + keyConverter.setProvider(providerName); + + return this; + } + + /** + * Provide a user defined source of randomness. + * + * @param random the secure random to be used. + * @return the current generator. + */ + public JcePublicKeyKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo) + throws PGPException + { + try + { + if (pubKey.getAlgorithm() == PublicKeyAlgorithmTags.ECDH) + { + ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKey.getPublicKeyPacket().getKey(); + X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID()); + ECDomainParameters ecParams = new ECDomainParameters(x9Params.getCurve(), x9Params.getG(), x9Params.getN()); + + // Generate the ephemeral key pair + ECKeyPairGenerator gen = new ECKeyPairGenerator(); + gen.init(new ECKeyGenerationParameters(ecParams, random)); + + EphemeralKeyPairGenerator kGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder() + { + public byte[] getEncoded(AsymmetricKeyParameter keyParameter) + { + return ((ECPublicKeyParameters)keyParameter).getQ().getEncoded(false); + } + }); + + EphemeralKeyPair ephKp = kGen.generate(); + + ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.getKeyPair().getPrivate(); + + ECPoint S = ecKey.getPoint().multiply(ephPriv.getD()).normalize(); + + RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm()); + + Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, pubKey.getFingerprint()), "AESWrap"); + + Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm()); + + c.init(Cipher.WRAP_MODE, key, random); + + byte[] paddedSessionData = PGPUtil.padSessionData(sessionInfo); + + byte[] C = c.wrap(new SecretKeySpec(paddedSessionData, PGPUtil.getSymmetricCipherName(sessionInfo[0]))); + byte[] VB = new MPInteger(new BigInteger(1, ephKp.getEncodedPublicKey())).getEncoded(); + + byte[] rv = new byte[VB.length + 1 + C.length]; + + System.arraycopy(VB, 0, rv, 0, VB.length); + rv[VB.length] = (byte)C.length; + System.arraycopy(C, 0, rv, VB.length + 1, C.length); + + return rv; + } + else + { + Cipher c = helper.createPublicKeyCipher(pubKey.getAlgorithm()); + + Key key = keyConverter.getPublicKey(pubKey); + + c.init(Cipher.ENCRYPT_MODE, key, random); + + return c.doFinal(sessionInfo); + } + } + catch (IllegalBlockSizeException e) + { + throw new PGPException("illegal block size: " + e.getMessage(), e); + } + catch (BadPaddingException e) + { + throw new PGPException("bad padding: " + e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new PGPException("key invalid: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new PGPException("unable to encode MPI: " + e.getMessage(), e); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java new file mode 100644 index 000000000..134935d31 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java @@ -0,0 +1,196 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.Signature; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jcajce.JcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +class OperatorHelper +{ + private JcaJceHelper helper; + + OperatorHelper(JcaJceHelper helper) + { + this.helper = helper; + } + + MessageDigest createDigest(int algorithm) + throws GeneralSecurityException, PGPException + { + MessageDigest dig; + + dig = helper.createDigest(PGPUtil.getDigestName(algorithm)); + + return dig; + } + + KeyFactory createKeyFactory(String algorithm) + throws GeneralSecurityException, PGPException + { + return helper.createKeyFactory(algorithm); + } + + PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + try + { + SecretKey secretKey = new SecretKeySpec(key, PGPUtil.getSymmetricCipherName(encAlgorithm)); + + final Cipher c = createStreamCipher(encAlgorithm, withIntegrityPacket); + + byte[] iv = new byte[c.getBlockSize()]; + + c.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); + + return new PGPDataDecryptor() + { + public InputStream getInputStream(InputStream in) + { + return new CipherInputStream(in, c); + } + + public int getBlockSize() + { + return c.getBlockSize(); + } + + public PGPDigestCalculator getIntegrityCalculator() + { + return new SHA1PGPDigestCalculator(); + } + }; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } + + Cipher createStreamCipher(int encAlgorithm, boolean withIntegrityPacket) + throws PGPException + { + String mode = (withIntegrityPacket) + ? "CFB" + : "OpenPGPCFB"; + + String cName = PGPUtil.getSymmetricCipherName(encAlgorithm) + + "/" + mode + "/NoPadding"; + + return createCipher(cName); + } + + Cipher createCipher(String cipherName) + throws PGPException + { + try + { + return helper.createCipher(cipherName); + } + catch (GeneralSecurityException e) + { + throw new PGPException("cannot create cipher: " + e.getMessage(), e); + } + } + + Cipher createPublicKeyCipher(int encAlgorithm) + throws PGPException + { + switch (encAlgorithm) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + return createCipher("RSA/ECB/PKCS1Padding"); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + return createCipher("ElGamal/ECB/PKCS1Padding"); + case PGPPublicKey.DSA: + throw new PGPException("Can't use DSA for encryption."); + case PGPPublicKey.ECDSA: + throw new PGPException("Can't use ECDSA for encryption."); + default: + throw new PGPException("unknown asymmetric algorithm: " + encAlgorithm); + } + } + + Cipher createKeyWrapper(int encAlgorithm) + throws PGPException + { + try + { + switch (encAlgorithm) + { + case SymmetricKeyAlgorithmTags.AES_128: + case SymmetricKeyAlgorithmTags.AES_192: + case SymmetricKeyAlgorithmTags.AES_256: + return helper.createCipher("AESWrap"); + default: + throw new PGPException("unknown wrap algorithm: " + encAlgorithm); + } + } + catch (GeneralSecurityException e) + { + throw new PGPException("cannot create cipher: " + e.getMessage(), e); + } + } + + private Signature createSignature(String cipherName) + throws PGPException + { + try + { + return helper.createSignature(cipherName); + } + catch (GeneralSecurityException e) + { + throw new PGPException("cannot create signature: " + e.getMessage(), e); + } + } + + public Signature createSignature(int keyAlgorithm, int hashAlgorithm) + throws PGPException + { + String encAlg; + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTags.DSA: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases. + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + encAlg = "ElGamal"; + break; + case PublicKeyAlgorithmTags.ECDSA: + encAlg = "ECDSA"; + break; + default: + throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return createSignature(PGPUtil.getDigestName(hashAlgorithm) + "with" + encAlg); + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java new file mode 100644 index 000000000..49b119dda --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java @@ -0,0 +1,185 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.openpgp.PGPException; + +/** + * Basic utility class + */ +class PGPUtil +{ + static String getDigestName( + int hashAlgorithm) + throws PGPException + { + switch (hashAlgorithm) + { + case HashAlgorithmTags.SHA1: + return "SHA1"; + case HashAlgorithmTags.MD2: + return "MD2"; + case HashAlgorithmTags.MD5: + return "MD5"; + case HashAlgorithmTags.RIPEMD160: + return "RIPEMD160"; + case HashAlgorithmTags.SHA256: + return "SHA256"; + case HashAlgorithmTags.SHA384: + return "SHA384"; + case HashAlgorithmTags.SHA512: + return "SHA512"; + case HashAlgorithmTags.SHA224: + return "SHA224"; + case HashAlgorithmTags.TIGER_192: + return "TIGER"; + default: + throw new PGPException("unknown hash algorithm tag in getDigestName: " + hashAlgorithm); + } + } + + static String getSignatureName( + int keyAlgorithm, + int hashAlgorithm) + throws PGPException + { + String encAlg; + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTags.DSA: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases. + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + encAlg = "ElGamal"; + break; + default: + throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return getDigestName(hashAlgorithm) + "with" + encAlg; + } + + static String getSymmetricCipherName( + int algorithm) + { + switch (algorithm) + { + case SymmetricKeyAlgorithmTags.NULL: + return null; + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + return "DESEDE"; + case SymmetricKeyAlgorithmTags.IDEA: + return "IDEA"; + case SymmetricKeyAlgorithmTags.CAST5: + return "CAST5"; + case SymmetricKeyAlgorithmTags.BLOWFISH: + return "Blowfish"; + case SymmetricKeyAlgorithmTags.SAFER: + return "SAFER"; + case SymmetricKeyAlgorithmTags.DES: + return "DES"; + case SymmetricKeyAlgorithmTags.AES_128: + return "AES"; + case SymmetricKeyAlgorithmTags.AES_192: + return "AES"; + case SymmetricKeyAlgorithmTags.AES_256: + return "AES"; + case SymmetricKeyAlgorithmTags.TWOFISH: + return "Twofish"; + default: + throw new IllegalArgumentException("unknown symmetric algorithm: " + algorithm); + } + } + + public static SecretKey makeSymmetricKey( + int algorithm, + byte[] keyBytes) + throws PGPException + { + String algName; + + switch (algorithm) + { + case SymmetricKeyAlgorithmTags.TRIPLE_DES: + algName = "DES_EDE"; + break; + case SymmetricKeyAlgorithmTags.IDEA: + algName = "IDEA"; + break; + case SymmetricKeyAlgorithmTags.CAST5: + algName = "CAST5"; + break; + case SymmetricKeyAlgorithmTags.BLOWFISH: + algName = "Blowfish"; + break; + case SymmetricKeyAlgorithmTags.SAFER: + algName = "SAFER"; + break; + case SymmetricKeyAlgorithmTags.DES: + algName = "DES"; + break; + case SymmetricKeyAlgorithmTags.AES_128: + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.AES_192: + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.AES_256: + algName = "AES"; + break; + case SymmetricKeyAlgorithmTags.TWOFISH: + algName = "Twofish"; + break; + default: + throw new PGPException("unknown symmetric algorithm: " + algorithm); + } + + return new SecretKeySpec(keyBytes, algName); + } + + public static byte[] padSessionData(byte[] sessionInfo) + { + byte[] result = new byte[40]; + + System.arraycopy(sessionInfo, 0, result, 0, sessionInfo.length); + + byte padValue = (byte)(result.length -sessionInfo.length); + + for (int i = sessionInfo.length; i != result.length; i++) + { + result[i] = padValue; + } + + return result; + } + + public static byte[] unpadSessionData(byte[] encoded) + throws PGPException + { + byte padValue = encoded[encoded.length - 1]; + + for (int i = encoded.length - padValue; i != encoded.length; i++) + { + if (encoded[i] != padValue) + { + throw new PGPException("bad padding found in session data"); + } + } + + byte[] taggedKey = new byte[encoded.length - padValue]; + + System.arraycopy(encoded, 0, taggedKey, 0, taggedKey.length); + + return taggedKey; + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java new file mode 100644 index 000000000..424770e6b --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java @@ -0,0 +1,81 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +class SHA1PGPDigestCalculator + implements PGPDigestCalculator +{ + private MessageDigest digest; + + SHA1PGPDigestCalculator() + { + try + { + digest = MessageDigest.getInstance("SHA1"); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalStateException("cannot find SHA-1: " + e.getMessage()); + } + } + + public int getAlgorithm() + { + return HashAlgorithmTags.SHA1; + } + + public OutputStream getOutputStream() + { + return new DigestOutputStream(digest); + } + + public byte[] getDigest() + { + return digest.digest(); + } + + public void reset() + { + digest.reset(); + } + + private class DigestOutputStream + extends OutputStream + { + private MessageDigest dig; + + DigestOutputStream(MessageDigest dig) + { + this.dig = dig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + dig.update(bytes, off, len); + } + + public void write(byte[] bytes) + throws IOException + { + dig.update(bytes); + } + + public void write(int b) + throws IOException + { + dig.update((byte)b); + } + + byte[] getDigest() + { + return dig.digest(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java new file mode 100644 index 000000000..808de3000 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java @@ -0,0 +1,56 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.Signature; +import java.security.SignatureException; + +class SignatureOutputStream + extends OutputStream +{ + private Signature sig; + + SignatureOutputStream(Signature sig) + { + this.sig = sig; + } + + public void write(byte[] bytes, int off, int len) + throws IOException + { + try + { + sig.update(bytes, off, len); + } + catch (SignatureException e) + { + throw new IOException("signature update caused exception: " + e.getMessage()); + } + } + + public void write(byte[] bytes) + throws IOException + { + try + { + sig.update(bytes); + } + catch (SignatureException e) + { + throw new IOException("signature update caused exception: " + e.getMessage()); + } + } + + public void write(int b) + throws IOException + { + try + { + sig.update((byte)b); + } + catch (SignatureException e) + { + throw new IOException("signature update caused exception: " + e.getMessage()); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/attr/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/attr/package.html new file mode 100644 index 000000000..251824f6c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/attr/package.html @@ -0,0 +1,5 @@ +<html> +<body bgcolor="#ffffff"> +Low level classes for dealing with OpenPGP user attributes. +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/package.html new file mode 100644 index 000000000..8bf922ebf --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/package.html @@ -0,0 +1,9 @@ +<html> +<body bgcolor="#ffffff"> +Low level classes for dealing with OpenPGP objects. +<p> +These classes deal with things at a raw OpenPGP packet level. For the most part +you are probably better off looking at the org.spongycastle.openpgp package +for what you want. +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/sig/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/sig/package.html new file mode 100644 index 000000000..474a1a8ef --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/bcpg/sig/package.html @@ -0,0 +1,5 @@ +<html> +<body bgcolor="#ffffff"> +Low level classes for dealing with OpenPGP signature attributes. +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/examples/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/examples/package.html new file mode 100644 index 000000000..47b2cf245 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/examples/package.html @@ -0,0 +1,5 @@ +<html> +<body bgcolor="#ffffff"> +Examples of use of the org.spongycastle.openpgp package. +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/bc/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/bc/package.html new file mode 100644 index 000000000..d1642127e --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/bc/package.html @@ -0,0 +1,8 @@ +<html> +<body bgcolor="#ffffff"> +BC lightweight operators for dealing with OpenPGP objects. +<p> +These provide the actual support for encryption and decryption required for the high level OpenPGP classes. +</p> +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/jcajce/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/jcajce/package.html new file mode 100644 index 000000000..928425dc8 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/jcajce/package.html @@ -0,0 +1,8 @@ +<html> +<body bgcolor="#ffffff"> +JCA/JCE based operators for dealing with OpenPGP objects. +<p> +These provide the actual support for encryption and decryption required for the high level OpenPGP classes. +</p> +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/package.html new file mode 100644 index 000000000..16e61e10a --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/operator/package.html @@ -0,0 +1,8 @@ +<html> +<body bgcolor="#ffffff"> +Interfaces and abstract classes to provide the framework to support operations on the OpenPGP high level classes. +<p> +For examples of actual implementations see the org.spongycastle.openpgp.operator.bc and org.spongycastle.openpgp.operator.jcajce packages. +</p> +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/package.html b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/package.html new file mode 100644 index 000000000..fd09835c0 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/javadoc/org/spongycastle/openpgp/package.html @@ -0,0 +1,16 @@ +<html> +<body bgcolor="#ffffff"> +High level classes for dealing with OpenPGP objects. +<p> +<b>Note</b>: These are based on the org.spongycastle.bcpg classes and use a streaming +model, so for some objects which have an input stream associated it is necessary +to read to the end of the input stream on the object before trying to read +another object from the orginal input stream. +<p> +A word on key ring files. For the purpose of this package a PGP key ring is a master key and +a collection of sub-keys associated with it. These public and secret key rings are handled by +the PGPPublicKey ring class and the PGPSecretKeyRing class respectively. In the case where +you are trying to read an key file which has multiple key rings in it, use PGPSecretKeyRingCollection +for the secret key file and PGPPublicKeyRingCollection for the public key file. +</body> +</html> diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java new file mode 100644 index 000000000..eca9f3522 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/examples/DetachedSignatureProcessor.java @@ -0,0 +1,199 @@ +package org.spongycastle.openpgp.examples; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.SignatureException; +import java.security.Security; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +/** + * A simple utility class that creates seperate signatures for files and verifies them. + * <p> + * To sign a file: DetachedSignatureProcessor -s [-a] fileName secretKey passPhrase.<br> + * If -a is specified the output file will be "ascii-armored". + * <p> + * To decrypt: DetachedSignatureProcessor -v fileName signatureFile publicKeyFile. + * <p> + * Note: this example will silently overwrite files. + * It also expects that a single pass phrase + * will have been used. + */ +public class DetachedSignatureProcessor +{ + private static void verifySignature( + String fileName, + String inputFileName, + String keyFileName) + throws GeneralSecurityException, IOException, PGPException, SignatureException + { + InputStream in = new BufferedInputStream(new FileInputStream(inputFileName)); + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + + verifySignature(fileName, in, keyIn); + + keyIn.close(); + in.close(); + } + + /* + * verify the signature in in against the file fileName. + */ + private static void verifySignature( + String fileName, + InputStream in, + InputStream keyIn) + throws GeneralSecurityException, IOException, PGPException, SignatureException + { + in = PGPUtil.getDecoderStream(in); + + PGPObjectFactory pgpFact = new PGPObjectFactory(in); + PGPSignatureList p3; + + Object o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) + { + PGPCompressedData c1 = (PGPCompressedData)o; + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p3 = (PGPSignatureList)pgpFact.nextObject(); + } + else + { + p3 = (PGPSignatureList)o; + } + + PGPPublicKeyRingCollection pgpPubRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn)); + + + InputStream dIn = new BufferedInputStream(new FileInputStream(fileName)); + + PGPSignature sig = p3.get(0); + PGPPublicKey key = pgpPubRingCollection.getPublicKey(sig.getKeyID()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), key); + + int ch; + while ((ch = dIn.read()) >= 0) + { + sig.update((byte)ch); + } + + dIn.close(); + + if (sig.verify()) + { + System.out.println("signature verified."); + } + else + { + System.out.println("signature verification failed."); + } + } + + private static void createSignature( + String inputFileName, + String keyFileName, + String outputFileName, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException, SignatureException + { + InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName)); + OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName)); + + createSignature(inputFileName, keyIn, out, pass, armor); + + out.close(); + keyIn.close(); + } + + private static void createSignature( + String fileName, + InputStream keyIn, + OutputStream out, + char[] pass, + boolean armor) + throws GeneralSecurityException, IOException, PGPException, SignatureException + { + if (armor) + { + out = new ArmoredOutputStream(out); + } + + PGPSecretKey pgpSec = PGPExampleUtil.readSecretKey(keyIn); + PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(pass)); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + BCPGOutputStream bOut = new BCPGOutputStream(out); + + InputStream fIn = new BufferedInputStream(new FileInputStream(fileName)); + + int ch; + while ((ch = fIn.read()) >= 0) + { + sGen.update((byte)ch); + } + + fIn.close(); + + sGen.generate().encode(bOut); + + if (armor) + { + out.close(); + } + } + + public static void main( + String[] args) + throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + if (args[0].equals("-s")) + { + if (args[1].equals("-a")) + { + createSignature(args[2], args[3], args[2] + ".asc", args[4].toCharArray(), true); + } + else + { + createSignature(args[1], args[2], args[1] + ".bpg", args[3].toCharArray(), false); + } + } + else if (args[0].equals("-v")) + { + verifySignature(args[1], args[2], args[3]); + } + else + { + System.err.println("usage: DetachedSignatureProcessor [-s [-a] file keyfile passPhrase]|[-v file sigFile keyFile]"); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java new file mode 100644 index 000000000..355f7ceaa --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java @@ -0,0 +1,142 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; + +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.operator.PGPContentSigner; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.util.io.TeeOutputStream; + +public class JcaPGPContentSignerBuilder + implements PGPContentSignerBuilder +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); + private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter(); + private int hashAlgorithm; + private SecureRandom random; + private int keyAlgorithm; + + public JcaPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + } + + public JcaPGPContentSignerBuilder setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public JcaPGPContentSignerBuilder setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + keyConverter.setProvider(provider); + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public JcaPGPContentSignerBuilder setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + keyConverter.setProvider(providerName); + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public JcaPGPContentSignerBuilder setDigestProvider(Provider provider) + { + digestCalculatorProviderBuilder.setProvider(provider); + + return this; + } + + public JcaPGPContentSignerBuilder setDigestProvider(String providerName) + { + digestCalculatorProviderBuilder.setProvider(providerName); + + return this; + } + + public PGPContentSigner build(final int signatureType, final PGPPrivateKey privateKey) + throws PGPException + { + final PGPDigestCalculator digestCalculator = digestCalculatorProviderBuilder.build().get(hashAlgorithm); + final Signature signature = helper.createSignature(keyAlgorithm, hashAlgorithm); + + try + { + if (random != null) + { + signature.initSign(keyConverter.getPrivateKey(privateKey)); + } + else + { + signature.initSign(keyConverter.getPrivateKey(privateKey)); + } + } + catch (InvalidKeyException e) + { + throw new PGPException("invalid key.", e); + } + + return new PGPContentSigner() + { + public int getType() + { + return signatureType; + } + + public int getHashAlgorithm() + { + return hashAlgorithm; + } + + public int getKeyAlgorithm() + { + return keyAlgorithm; + } + + public long getKeyID() + { + return privateKey.getKeyID(); + } + + public OutputStream getOutputStream() + { + return new TeeOutputStream(new SignatureOutputStream(signature), digestCalculator.getOutputStream()); + } + + public byte[] getSignature() + { + try + { + return signature.sign(); + } + catch (SignatureException e) + { // TODO: need a specific runtime exception for PGP operators. + throw new IllegalStateException("unable to create signature"); + } + } + + public byte[] getDigest() + { + return digestCalculator.getDigest(); + } + }; + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java new file mode 100644 index 000000000..7070d2d87 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java @@ -0,0 +1,220 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.Signature; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jcajce.JcaJceHelper; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; + +class OperatorHelper +{ + private JcaJceHelper helper; + + OperatorHelper(JcaJceHelper helper) + { + this.helper = helper; + } + + MessageDigest createDigest(int algorithm) + throws GeneralSecurityException, PGPException + { + MessageDigest dig; + + try + { + dig = helper.createDigest(PGPUtil.getDigestName(algorithm)); + } + catch (NoSuchAlgorithmException e) + { + throw new PGPException("cannot find provider: " + e.getMessage(), e); + } + catch (NoSuchProviderException e) + { + throw new PGPException("cannot find provider: " + e.getMessage(), e); + } + + return dig; + } + + KeyFactory createKeyFactory(String algorithm) + throws GeneralSecurityException, PGPException + { + try + { + return helper.createKeyFactory(algorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new PGPException("cannot find provider: " + e.getMessage(), e); + } + catch (NoSuchProviderException e) + { + throw new PGPException("cannot find provider: " + e.getMessage(), e); + } + } + + PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key) + throws PGPException + { + try + { + SecretKey secretKey = new SecretKeySpec(key, PGPUtil.getSymmetricCipherName(encAlgorithm)); + + final Cipher c = createStreamCipher(encAlgorithm, withIntegrityPacket); + + byte[] iv = new byte[c.getBlockSize()]; + + c.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); + + return new PGPDataDecryptor() + { + public InputStream getInputStream(InputStream in) + { + return new CipherInputStream(in, c); + } + + public int getBlockSize() + { + return c.getBlockSize(); + } + + public PGPDigestCalculator getIntegrityCalculator() + { + return new SHA1PGPDigestCalculator(); + } + }; + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception creating cipher", e); + } + } + + Cipher createStreamCipher(int encAlgorithm, boolean withIntegrityPacket) + throws PGPException + { + String mode = (withIntegrityPacket) + ? "CFB" + : "OpenPGPCFB"; + + String cName = PGPUtil.getSymmetricCipherName(encAlgorithm) + + "/" + mode + "/NoPadding"; + + return createCipher(cName); + } + + Cipher createCipher(String cipherName) + throws PGPException + { + try + { + return helper.createCipher(cipherName); + } + catch (Exception e) + { + throw new PGPException("cannot create cipher: " + e.getMessage(), e); + } + } + + Cipher createPublicKeyCipher(int encAlgorithm) + throws PGPException + { + switch (encAlgorithm) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + return createCipher("RSA/ECB/PKCS1Padding"); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + return createCipher("ElGamal/ECB/PKCS1Padding"); + case PGPPublicKey.DSA: + throw new PGPException("Can't use DSA for encryption."); + case PGPPublicKey.ECDSA: + throw new PGPException("Can't use ECDSA for encryption."); + default: + throw new PGPException("unknown asymmetric algorithm: " + encAlgorithm); + } + } + + Cipher createKeyWrapper(int encAlgorithm) + throws PGPException + { + try + { + switch (encAlgorithm) + { + case SymmetricKeyAlgorithmTags.AES_128: + case SymmetricKeyAlgorithmTags.AES_192: + case SymmetricKeyAlgorithmTags.AES_256: + return helper.createCipher("AESWrap"); + default: + throw new PGPException("unknown wrap algorithm: " + encAlgorithm); + } + } + catch (Exception e) + { + throw new PGPException("cannot create cipher: " + e.getMessage(), e); + } + } + + private Signature createSignature(String cipherName) + throws PGPException + { + try + { + return helper.createSignature(cipherName); + } + catch (Exception e) + { + throw new PGPException("cannot create signature: " + e.getMessage(), e); + } + } + + public Signature createSignature(int keyAlgorithm, int hashAlgorithm) + throws PGPException + { + String encAlg; + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTags.DSA: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases. + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + encAlg = "ElGamal"; + break; + case PublicKeyAlgorithmTags.ECDSA: + encAlg = "ECDSA"; + break; + default: + throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return createSignature(PGPUtil.getDigestName(hashAlgorithm) + "with" + encAlg); + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java new file mode 100644 index 000000000..bd98aecdb --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java @@ -0,0 +1,564 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSAElGamalTest + extends SimpleTest +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + public void performTest() + throws Exception + { + try + { + PGPPublicKey pubKey; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + if (pubKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key suitable for encryption + // + long pgpKeyID = 0; + AsymmetricKeyParameter pKey = null; + BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = keyConverter.getPublicKey(pgpKey); + pgpKeyID = pgpKey.getKeyID(); + if (pgpKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // verify the key + // + + } + } + + AsymmetricBlockCipher c = new PKCS1Encoding(new ElGamalEngine()); + + c.init(true, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.processBlock(in, 0, in.length); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + c.init(false, keyConverter.getPrivateKey(pgpPrivKey)); + + out = c.processBlock(out, 0, out.length); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // use of PGPKeyPair + // + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ElGamal", "SC"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + kpg.initialize(512); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.ELGAMAL_GENERAL , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + + + // Test bug with ElGamal P size != 0 mod 8 (don't use these sizes at home!) + SecureRandom random = new SecureRandom(); + for (int pSize = 257; pSize < 264; ++pSize) + { + // Generate some parameters of the given size + AlgorithmParameterGenerator a = AlgorithmParameterGenerator.getInstance("ElGamal", "SC"); + a.init(pSize, new SecureRandom()); + AlgorithmParameters params = a.generateParameters(); + + DHParameterSpec elP = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal", "SC"); + + keyGen.initialize(512); + + + // Run a short encrypt/decrypt test with random key for the given parameters + kp = keyGen.generateKeyPair(); + + PGPKeyPair elGamalKeyPair = new PGPKeyPair( + PublicKeyAlgorithmTags.ELGAMAL_GENERAL, kp, new Date()); + + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(random)); + + puK = elGamalKeyPair.getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + cbOut = new ByteArrayOutputStream(); + + cOut = cPk.open(cbOut, text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = elGamalKeyPair.getPrivateKey(); + + // Note: This is where an exception would be expected if the P size causes problems + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + ByteArrayOutputStream dec = new ByteArrayOutputStream(); + + int b; + while ((b = clear.read()) >= 0) + { + dec.write(b); + } + + byte[] decText = dec.toByteArray(); + + if (!areEqual(text, decText)) + { + fail("decrypted message incorrect"); + } + } + + // check sub key encoding + + it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (!pgpKey.isMasterKey()) + { + byte[] kEnc = pgpKey.getEncoded(); + + PGPObjectFactory objF = new PGPObjectFactory(kEnc); + + PGPPublicKey k = (PGPPublicKey)objF.nextObject(); + + pKey = keyConverter.getPublicKey(k); + pgpKeyID = k.getKeyID(); + if (k.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + if (objF.nextObject() != null) + { + fail("failed - stream not fully parsed."); + } + } + } + + } + catch (PGPException e) + { + fail("exception: " + e.getMessage(), e.getUnderlyingException()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPDSAElGamalTest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java new file mode 100644 index 000000000..ce4e5ac25 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java @@ -0,0 +1,2362 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; + +public class BcPGPKeyRingTest + extends SimpleTest +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + char[] sec5pass1 = "12345678".toCharArray(); + + // + // Werner Koch "odd keys" + // + byte[] pub6 = Base64.decode( + "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" + + "L4Kb7TWkd/OHQScVBO8sTUz0+2g="); + + byte[] pub6check = Base64.decode("62O9"); + + // + // revoked sub key + // + byte[] pub7 = Base64.decode( + "mQGiBEFOsIwRBADcjRx7nAs4RaWsQU6p8/ECLZD9sSeYc6CN6UDI96RKj0/hCzMs" + + "qlA0+9fzGZ7ZEJ34nuvDKlhKGC7co5eOiE0a9EijxgcrZU/LClZWa4YfyNg/ri6I" + + "yTyfOfrPQ33GNQt2iImDf3FKp7XKuY9nIxicGQEaW0kkuAmbV3oh0+9q8QCg/+fS" + + "epDEqEE/+nKONULGizKUjMED/RtL6RThRftZ9DOSdBytGYd48z35pca/qZ6HA36K" + + "PVQwi7V77VKQyKFLTOXPLnVyO85hyYB/Nv4DFHN+vcC7/49lfoyYMZlN+LarckHi" + + "NL154wmmzygB/KKysvWBLgkErEBCD0xBDd89iTQNlDtVQAWGORVffl6WWjOAkliG" + + "3dL6A/9A288HfFRnywqi3xddriV6wCPmStC3dkCS4vHk2ofS8uw4ZNoRlp1iEPna" + + "ai2Xa9DX1tkhaGk2k96MqqbBdGpbW8sMA9otJ9xdMjWEm/CgJUFUFQf3zaVy3mkM" + + "S2Lvb6P4Wc2l/diEEIyK8+PqJItSh0OVU3K9oM7ngHwVcalKILQVUkV2b2tlZCA8" + + "UmV2b2tlZEB0ZWQ+iQBOBBARAgAOBQJBTrCMBAsDAgECGQEACgkQvglkcFA/c63+" + + "QgCguh8rsJbPTtbhZcrqBi5Mo1bntLEAoPZQ0Kjmu2knRUpHBeUemHDB6zQeuQIN" + + "BEFOsIwQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz" + + "0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRP" + + "xfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvN" + + "ILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dD" + + "ox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMI" + + "PWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/93zriSvSHqsi1FeEmUBo431Jkh" + + "VerIzb6Plb1j6FIq+s3vyvx9K+dMvjotZqylWZj4GXpH+2xLJTjWkrGSfUZVI2Nk" + + "nyOFxUCKLLqaqVBFAQIjULfvQfGEWiGQKk9aRLkdG+D+8Y2N9zYoBXoQ9arvvS/t" + + "4mlOsiuaTe+BZ4x+BXTpF4b9sKZl7V8QP/TkoJWUdydkvxciHdWp7ssqyiKOFRhG" + + "818knDfFQ3cn2w/RnOb+7AF9wDncXDPYLfpPv9b2qZoLrXcyvlLffGDUdWs553ut" + + "1F5AprMURs8BGmY9BnjggfVubHdhTUoA4gVvrdaf+D9NwZAl0xK/5Y/oPuMZiQBG" + + "BBgRAgAGBQJBTrCMAAoJEL4JZHBQP3Ot09gAoMmLKloVDP+WhDXnsM5VikxysZ4+" + + "AKCrJAUO+lYAyPYwEwgK+bKmUGeKrIkARgQoEQIABgUCQU6wpQAKCRC+CWRwUD9z" + + "rQK4AJ98kKFxGU6yhHPr6jYBJPWemTNOXgCfeGB3ox4PXeS4DJDuLy9yllytOjo="); + + byte[] pub7check = Base64.decode("f/YQ"); + + byte[] pub8 = Base64.decode( + "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" + + "9Uqz3fUvGoewAWA="); + + byte[] sec8 = Base64.decode( + "lQHpBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ/4JAwLXyWhb4pf4" + + "nmCmD0lDwoYvatLiR7UQVM2MamxClIiT0lCPN9C2AYIFgRWAJNS215Tjx7P/dh7e" + + "8sYfh5XEHErT3dMbsAGHtCFKaWEgWWl5dSA8eXlqaWFAbm93bWVkaWF0ZWNoLmNv" + + "bT6wAwP//4kAXQQQEQIAHQUCQRytpgcLCQgHAwIKAhkBBRsDAAAABR4BAAAAAAoJ" + + "EPT+Vvgr/2IUmWEAoJEyJ9B7CJfmjwVTT30UbdCctVRmAJ9akw6we6UYd82vF0FK" + + "QTlSuqHXKbABZ50CawRBHK2mEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlL" + + "OCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N" + + "286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/" + + "RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2O" + + "u1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqV" + + "DNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/crbApFLO" + + "QikN9lHbn62dDxuu/dNigNmjf/A3cX+vo8aPHTXnRDyDwQALRuqbitdbM6p1LFjG" + + "8g84Aabk/jL7zzjJLqPe8pxeK1Pu+P44NzalXAYMsOOsxGuixIrz3Jw8IrpH/mkP" + + "tG/iX0NaLZ+Au9eokdLb+6NR/a51r2qL3E0ycFciJ61HzZKKHi6d0VQ7CHozCz+j" + + "d5530Mh1qq/ZjYDDjfP+snpyNIZXcLtGR/3HzkBqNgDcvClLx3322AWKDa7pX8DX" + + "qiLr8znWhPRpH9kCTnSpRzhYGT25FRcvdj4/q/oUIET/Tp+BWW3BxAc7WgpgqEfn" + + "fa0Mv72Zbn91gf4JAwITijME9IlFBGAwH6YmBtWIlnDiRbsq/Pxozuhbnes831il" + + "KmdpUKXkiIfHY0MqrEWl3Dfn6PMJGTnhgqXMrDxx3uHrq0Jl2swRnAWIIO8gID7j" + + "uPetUqEviPiwAYeJAEwEGBECAAwFAkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg" + + "+JW8m5nF3R/oZGuG87bXQBszkjMAoLhGPncuGKowJXMRVc70/8qwXQJLsAFn"); + + char[] sec8pass = "qwertyui".toCharArray(); + + byte[] sec9 = Base64.decode( + "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" + + "6neEEX/i1Q=="); + + public char[] sec9pass = "foo".toCharArray(); + + // version 4 keys with expiry dates + byte[] pub10 = Base64.decode( + "mQGiBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHLQgdGVzdCBrZXkg" + + "KHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUCQqqJrQIbAwUJACTqAAYL" + + "CQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzLAJ42AeCRIBBjv8r8qw9y" + + "laNj2GZ1sACgiWYHVXMA6B1H9I1kS3YsCd3Oq7qwAgAAuM0EQqqJrhADAKWkix8l" + + "pJN7MMTXob4xFF1TvGll0UD1bDGOMMbes6aeXSbT9QXee/fH3GnijLY7wB+qTPv9" + + "ohubrSpnv3yen3CEBW6Q2YK+NlCskma42Py8YMV2idmYjtJi1ckvHFWt5wADBQL/" + + "fkB5Q5xSGgspMaTZmtmX3zG7ZDeZ0avP8e8mRL8UszCTpqs6vMZrXwyQLZPbtMYv" + + "PQpuRGEeKj0ysimwYRA5rrLQjnRER3nyuuEUUgc4j+aeRxPf9WVsJ/a1FCHtaAP1" + + "iE8EGBECAA8FAkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCfd66H7DL7kFGd" + + "IoS+NIp8JO+noxAAn25si4QAF7og8+4T5YQUuhIhx/NesAIAAA=="); + + byte[] sec10 = Base64.decode( + "lQHhBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHP4DAwLssmOjVC+d" + + "mWB783Lpzjb9evKzsxisTdx8/jHpUSS+r//6/Guyx3aA/zUw5bbftItW57mhuNNb" + + "JTu7WrQgdGVzdCBrZXkgKHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUC" + + "QqqJrQIbAwUJACTqAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzL" + + "AJ0cYPwKeoSReY14LqJtAjnkX7URHACgsRZWfpbalrSyDnq3TtZeGPUqGX+wAgAA" + + "nQEUBEKqia4QAwClpIsfJaSTezDE16G+MRRdU7xpZdFA9WwxjjDG3rOmnl0m0/UF" + + "3nv3x9xp4oy2O8Afqkz7/aIbm60qZ798np9whAVukNmCvjZQrJJmuNj8vGDFdonZ" + + "mI7SYtXJLxxVrecAAwUC/35AeUOcUhoLKTGk2ZrZl98xu2Q3mdGrz/HvJkS/FLMw" + + "k6arOrzGa18MkC2T27TGLz0KbkRhHio9MrIpsGEQOa6y0I50REd58rrhFFIHOI/m" + + "nkcT3/VlbCf2tRQh7WgD9f4DAwLssmOjVC+dmWDXVLRopzxbBGOvodp/LZoSDb56" + + "gNJjDMJ1aXqWW9qTAg1CFjBq73J3oFpVzInXZ8+Q8inxv7bnWiHbiE8EGBECAA8F" + + "AkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCgl2jw5hfk/JsyjulQqe1Nps1q" + + "Lx0AoMdnFMZmTMLHn8scUW2j9XO312tmsAIAAA=="); + + public char[] sec10pass = "test".toCharArray(); + + public byte[] subKeyBindingKey = Base64.decode( + "mQGiBDWagYwRBAD7UcH4TAIp7tmUoHBNxVxCVz2ZrNo79M6fV63riOiH2uDxfIpr" + + "IrL0cM4ehEKoqlhngjDhX60eJrOw1nC5BpYZRnDnyDYT4wTWRguxObzGq9pqA1dM" + + "oPTJhkFZVIBgFY99/ULRqaUYIhFGgBtnwS70J8/L/PGVc3DmWRLMkTDjSQCg/5Nh" + + "MCjMK++MdYMcMl/ziaKRT6EEAOtw6PnU9afdohbpx9CK4UvCCEagfbnUtkSCQKSk" + + "6cUp6VsqyzY0pai/BwJ3h4apFMMMpVrtBAtchVgqo4xTr0Sve2j0k+ase6FSImiB" + + "g+AR7hvTUTcBjwtIExBc8TuCTqmn4GG8F7UMdl5Z0AZYj/FfAQYaRVZYP/pRVFNx" + + "Lw65BAC/Fi3qgiGCJFvXnHIckTfcAmZnKSEXWY9NJ4YQb4+/nH7Vsw0wR/ZObUHR" + + "bWgTc9Vw1uZIMe0XVj6Yk1dhGRehUnrm3mE7UJxu7pgkBCbFECFSlSSqP4MEJwZV" + + "09YP/msu50kjoxyoTpt+16uX/8B4at24GF1aTHBxwDLd8X0QWrQsTWVycmlsbCBM" + + "eW5jaCBDTEVBUiBzeXN0ZW0gREggPGNsZWFyQG1sLmNvbT6JAEsEEBECAAsFAjWa" + + "gYwECwMBAgAKCRDyAGjiP47/XanfAKCs6BPURWVQlGh635VgL+pdkUVNUwCdFcNa" + + "1isw+eAcopXPMj6ACOapepu5Ag0ENZqBlBAIAPZCV7cIfwgXcqK61qlC8wXo+VMR" + + "OU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf" + + "3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2g" + + "pXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPA" + + "Q/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQD" + + "GcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH" + + "/RYtVo+HROZ6jrNjrATEwQm1fUQrk6n5+2dniN881lF0CNkB4NkHw1Xxz4Ejnu/0" + + "iLg8fkOAsmanOsKpOkRtqUnVpsVL5mLJpFEyCY5jbcfj+KY9/25bs0ga7kLHNZia" + + "zbCxJdF+W179z3nudQxRaXG/0XISIH7ziZbSVni69sKc1osk1+OoOMbSuZ86z535" + + "Pln4fXclkFE927HxfbWoO+60hkOLKh7x+8fC82b3x9vCETujEaxrscO2xS7/MYXP" + + "8t1ffriTDmhuIuQS2q4fLgeWdqrODrMhrD8Dq7e558gzp30ZCqpiS7EmKGczL7B8" + + "gXxbBCVSTxYMJheXt2xMXsuJAD8DBRg1moGU8gBo4j+O/10RAgWdAKCPhaFIXuC8" + + "/cdiNMxTDw9ug3De5QCfYXmDzRSFUu/nrCi8yz/l09wsnxo="); + + public byte[] subKeyBindingCheckSum = Base64.decode("3HU+"); + + // + // PGP8 with SHA1 checksum. + // + public byte[] rewrapKey = Base64.decode( + "lQOWBEUPOQgBCADdjPTtl8oOwqJFA5WU8p7oDK5KRWfmXeXUZr+ZJipemY5RSvAM" + + "rxqsM47LKYbmXOJznXCQ8+PPa+VxXAsI1CXFHIFqrXSwvB/DUmb4Ec9EuvNd18Zl" + + "hJAybzmV2KMkaUp9oG/DUvxZJqkpUddNfwqZu0KKKZWF5gwW5Oy05VCpaJxQVXFS" + + "whdbRfwEENJiNx4RB3OlWhIjY2p+TgZfgQjiGB9i15R+37sV7TqzBUZF4WWcnIRQ" + + "DnpUfxHgxQ0wO/h/aooyRHSpIx5i4oNpMYq9FNIyakEx/Bomdbs5hW9dFxhrE8Es" + + "UViAYITgTsyROxmgGatGG09dcmVDJVYF4i7JAAYpAAf/VnVyUDs8HrxYTOIt4rYY" + + "jIHToBsV0IiLpA8fEA7k078L1MwSwERVVe6oHVTjeR4A9OxE52Vroh2eOLnF3ftf" + + "6QThVVZr+gr5qeG3yvQ36N7PXNEVOlkyBzGmFQNe4oCA+NR2iqnAIspnekVmwJV6" + + "xVvPCjWw/A7ZArDARpfthspwNcJAp4SWfoa2eKzvUTznTyqFu2PSS5fwQZUgOB0P" + + "Y2FNaKeqV8vEZu4SUWwLOqXBQIZXiaLvdKNgwFvUe3kSHdCNsrVzW7SYxFwaEog2" + + "o6YLKPVPqjlGX1cMOponGp+7n9nDYkQjtEsGSSMQkQRDAcBdSVJmLO07kFOQSOhL" + + "WQQA49BcgTZyhyH6TnDBMBHsGCYj43FnBigypGT9FrQHoWybfX47yZaZFROAaaMa" + + "U6man50YcYZPwzDzXHrK2MoGALY+DzB3mGeXVB45D/KYtlMHPLgntV9T5b14Scbc" + + "w1ES2OUtsSIUs0zelkoXqjLuKnSIYK3mMb67Au7AEp6LXM8EAPj2NypvC86VEnn+" + + "FH0QHvUwBpmDw0EZe25xQs0brvAG00uIbiZnTH66qsIfRhXV/gbKK9J5DTGIqQ15" + + "DuPpz7lcxg/n2+SmjQLNfXCnG8hmtBjhTe+udXAUrmIcfafXyu68SAtebgm1ga56" + + "zUfqsgN3FFuMUffLl3myjyGsg5DnA/oCFWL4WCNClOgL6A5VkNIUait8QtSdCACT" + + "Y7jdSOguSNXfln0QT5lTv+q1AjU7zjRl/LsFNmIJ5g2qdDyK937FOXM44FEEjZty" + + "/4P2dzYpThUI4QUohIj8Qi9f2pZQueC5ztH6rpqANv9geZKcciAeAbZ8Md0K2TEU" + + "RD3Lh+RSBzILtBtUZXN0IEtleSA8dGVzdEBleGFtcGxlLmNvbT6JATYEEwECACAF" + + "AkUPOQgCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDYpknHeQaskD9NB/9W" + + "EbFuLaqZAl3yjLU5+vb75BdvcfL1lUs44LZVwobNp3/0XbZdY76xVPNZURtU4u3L" + + "sJfGlaF+EqZDE0Mqc+vs5SIb0OnCzNJ00KaUFraUtkByRV32T5ECHK0gMBjCs5RT" + + "I0vVv+Qmzl4+X1Y2bJ2mlpBejHIrOzrBD5NTJimTAzyfnNfipmbqL8p/cxXKKzS+" + + "OM++ZFNACj6lRM1W9GioXnivBRC88gFSQ4/GXc8yjcrMlKA27JxV+SZ9kRWwKH2f" + + "6o6mojUQxnHr+ZFKUpo6ocvTgBDlC57d8IpwJeZ2TvqD6EdA8rZ0YriVjxGMDrX1" + + "8esfw+iLchfEwXtBIRwS"); + + char[] rewrapPass = "voltage123".toCharArray(); + + byte[] pubWithX509 = Base64.decode( + "mQENBERabjABCACtmfyo6Nph9MQjv4nmCWjZrRYnhXbivomAdIwYkLZUj1bjqE+j"+ + "uaLzjZV8xSI59odZvrmOiqlzOc4txitQ1OX7nRgbOJ7qku0dvwjtIn46+HQ+cAFn"+ + "2mTi81RyXEpO2uiZXfsNTxUtMi+ZuFLufiMc2kdk27GZYWEuasdAPOaPJnA+wW6i"+ + "ZHlt0NfXIGNz864gRwhD07fmBIr1dMFfATWxCbgMd/rH7Z/j4rvceHD2n9yrhPze"+ + "YN7W4Nuhsr2w/Ft5Cm9xO7vXT/cpto45uxn8f7jERep6bnUwNOhH8G+6xLQgTLD0"+ + "qFBGVSIneK3lobs6+xn6VaGN8W0tH3UOaxA1ABEBAAG0D0NOPXFhLWRlZXBzaWdo"+ + "dIkFDgQQZAIFAQUCRFpuMAUDCWdU0gMF/3gCGwPELGQBAQQwggTkMIIDzKADAgEC"+ + "AhBVUMV/M6rIiE+IzmnPheQWMA0GCSqGSIb3DQEBBQUAMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDAeFw0wNjA1MDQyMTEyMTZaFw0xMTA1MDQyMTIwMDJaMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2Z/Kjo2mH0xCO/ieYJ"+ + "aNmtFieFduK+iYB0jBiQtlSPVuOoT6O5ovONlXzFIjn2h1m+uY6KqXM5zi3GK1DU"+ + "5fudGBs4nuqS7R2/CO0ifjr4dD5wAWfaZOLzVHJcSk7a6Jld+w1PFS0yL5m4Uu5+"+ + "IxzaR2TbsZlhYS5qx0A85o8mcD7BbqJkeW3Q19cgY3PzriBHCEPTt+YEivV0wV8B"+ + "NbEJuAx3+sftn+Piu9x4cPaf3KuE/N5g3tbg26GyvbD8W3kKb3E7u9dP9ym2jjm7"+ + "Gfx/uMRF6npudTA06Efwb7rEtCBMsPSoUEZVIid4reWhuzr7GfpVoY3xbS0fdQ5r"+ + "EDUCAwEAAaOCAXwwggF4MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G"+ + "A1UdDgQWBBSmFTRv5y65DHtTYae48zl0ExNWZzCCASUGA1UdHwSCARwwggEYMIIB"+ + "FKCCARCgggEMhoHFbGRhcDovLy9DTj1xYS1kZWVwc2lnaHQsQ049cWEtd3VtYW4x"+ + "LWRjLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNl"+ + "cyxDTj1Db25maWd1cmF0aW9uLERDPVdlYmZlLERDPXRtczAxLERDPXFhLERDPWNv"+ + "bT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM"+ + "RGlzdHJpYnV0aW9uUG9pbnSGQmh0dHA6Ly9xYS13dW1hbjEtZGMud2ViZmUudG1z"+ + "MDEucWEuY29tL0NlcnRFbnJvbGwvcWEtZGVlcHNpZ2h0LmNybDAQBgkrBgEEAYI3"+ + "FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAfuZCW3XlB7Eok35zQbvYt9rhAndT"+ + "DNw3wPNI4ZzD1nXoYWnwhNNvWRpsOt4ExOSNdaHErfgDXAMyyg66Sro0TkAx8eAj"+ + "fPQsyRAh0nm0glzFmJN6TdOZbj7hqGZjc4opQ6nZo8h/ULnaEwMIUW4gcSkZt0ww"+ + "CuErl5NUrN3DpkREeCG/fVvQZ8ays3ibQ5ZCZnYBkLYq/i0r3NLW34WfYhjDY48J"+ + "oQWtvFSAxvRfz2NGmqnrCHPQZxqlfdta97kDa4VQ0zSeBaC70gZkLmD1GJMxWoXW"+ + "6tmEcgPY5SghInUf+L2u52V55MjyAFzVp7kTK2KY+p7qw35vzckrWkwu8AAAAAAA"+ + "AQE="); + + private static byte[] secWithPersonalCertificate = Base64.decode( + "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" + + "cGdwbWltZQULBwgJAgIZAQUbAQAAAAUeAQAAAAIVAgAKCRDlTa3BE84gWVKW" + + "BACcoCFKvph9r9QiHT1Z3N4wZH36Uxqu/059EFALnBkEdVudX/p6S9mynGRk" + + "EfhmWFC1O6dMpnt+ZBEed/4XyFWVSLPwirML+6dxfXogdUsdFF1NCRHc3QGc" + + "txnNUT/zcZ9IRIQjUhp6RkIvJPHcyfTXKSbLviI+PxzHU2Padq8pV7ABZ7kA" + + "jQRIfg8tAQQAutJR/aRnfZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr" + + "5dg50wq3I4HOamRxUwHpdPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO" + + "8LUJ2VTbfPxoLFp539SQ0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0Ft" + + "JycAEQEAAbABj4kEzQQYAQIENwUCSMYtnAUJAeEzgMLFFAAAAAAAFwNleDUw" + + "OWNlcnRpZmljYXRlQHBncC5jb20wggNhMIICyqADAgECAgkA1AoCoRKJCgsw" + + "DQYJKoZIhvcNAQEFBQAwgakxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj" + + "aCBSZXB1YmxpYzESMBAGA1UEChQJQSYmTCBzb2Z0MSAwHgYDVQQLExdJbnRl" + + "cm5hbCBEZXZlbG9wbWVudCBDQTEqMCgGA1UEAxQhQSYmTCBzb2Z0IEludGVy" + + "bmFsIERldmVsb3BtZW50IENBMR8wHQYJKoZIhvcNAQkBFhBrYWRsZWNAYWxz" + + "b2Z0LmN6MB4XDTA4MDcxNjE1MDkzM1oXDTA5MDcxNjE1MDkzM1owaTELMAkG" + + "A1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMRIwEAYDVQQKFAlB" + + "JiZMIHNvZnQxFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5IeW5l" + + "ay1JbnRyYW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAutJR/aRn" + + "fZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr5dg50wq3I4HOamRxUwHp" + + "dPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO8LUJ2VTbfPxoLFp539SQ" + + "0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0FtJycCAwEAAaOBzzCBzDAJ" + + "BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD" + + "ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNaw7A6r10PtYZzAvr9CrSKeRYJgwHwYD" + + "VR0jBBgwFoAUmqSRM8rN3+T1+tkGiqef8S5suYgwGgYDVR0RBBMwEYEPaHlu" + + "ZWtAYWxzb2Z0LmN6MCgGA1UdHwQhMB8wHaAboBmGF2h0dHA6Ly9wZXRyazIv" + + "Y2EvY2EuY3JsMAsGA1UdDwQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQCUdOWd" + + "7mBLWj1/GSiYgfwgdTrgk/VZOJvMKBiiFyy1iFEzldz6Xx+mAexnFJKfZXZb" + + "EMEGWHfWPmgJzAtuTT0Jz6tUwDmeLH3MP4m8uOZtmyUJ2aq41kciV3rGxF0G" + + "BVlZ/bWTaOzHdm6cjylt6xxLt6MJzpPBA/9ZfybSBh1DaAUbDgAAAJ0gBBkB" + + "AgAGBQJIxi2bAAoJEAdYkEWLb2R2fJED/RK+JErZ98uGo3Z81cHkdP3rk8is" + + "DUL/PR3odBPFH2SIA5wrzklteLK/ZXmBUzcvxqHEgI1F7goXbsBgeTuGgZdx" + + "pINErxkNpcMl9FTldWKGiapKrhkZ+G8knDizF/Y7Lg6uGd2nKVxzutLXdHJZ" + + "pU89Q5nzq6aJFAZo5TBIcchQAAoJEOVNrcETziBZXvQD/1mvFqBfWqwXxoj3" + + "8fHUuFrE2pcp32y3ciO2i+uNVEkNDoaVVNw5eHQaXXWpllI/Pe6LnBl4vkyc" + + "n3pjONa4PKrePkEsCUhRbIySqXIHuNwZumDOlKzZHDpCUw72LaC6S6zwuoEf" + + "ucOcxTeGIUViANWXyTIKkHfo7HfigixJIL8nsAFn"); + + private static final byte[] umlautKeySig = Base64.decode( + "mI0ETdvOgQEEALoI2a39TRk1HReEB6DP9Bu3ShZUce+/Oeg9RIL9aUFuCsNdhu02" + + "REEHjO29Jz8daPgrnJDfFepNLD6iKKru2m9P30qnhsHMIAshO2Ozfh6wKwuHRqR3" + + "L4gBDu7cCB6SLwPoD8AYG0yQSM+Do10Td87RlStxCgxpMK6R3TsRkxcFABEBAAG0" + + "OlVNTEFVVFNUQVJUOsOEw6TDlsO2w5zDvMOfOlVNTEFURU5ERSA8YXNkbGFrc2Rs" + + "QGFrc2RqLmNvbT6IuAQTAQIAIgUCTdvOgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC" + + "HgECF4AACgkQP8kDwm8AOFiArAP/ZXrlZJB1jFEjyBb04ckpE6F/aJuSYIXf0Yx5" + + "T2eS+lA69vYuqKRC1qNROBrAn/WGNOQBFNEgGoy3F3gV5NgpIphnyIEZdZWGY2rv" + + "yjunKWlioZjWc/xbSbvpvJ3Q8RyfDXBOkDEB6uF1ksimw2eJSOUTkF9AQfS5f4rT" + + "5gs013G4jQRN286BAQQApVbjd8UhsQLB4TpeKn9+dDXAfikGgxDOb19XisjRiWxA" + + "+bKFxu5tRt6fxXl6BGSGT7DhoVbNkcJGVQFYcbR31UGKCVYcWSL3yfz+PiVuf1UB" + + "Rp44cXxxqxrLqKp1rk3dGvV4Ayy8lkk3ncDGPez6lIKvj3832yVtAzUOX1QOg9EA" + + "EQEAAYifBBgBAgAJBQJN286BAhsMAAoJED/JA8JvADhYQ80D/R3TX0FBMHs/xqEh" + + "tiS86XP/8pW6eMm2eaAYINxoDY3jmDMv2HFQ+YgrYXgqGr6eVGqDMNPj4W8VBoOt" + + "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + public void test1() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + Iterator sIt = pubKey.getSignatures(); + while (sIt.hasNext()) + { + ((PGPSignature)sIt.next()).getSignatureType(); + } + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + // + // exact match + // + rIt = pubRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = pubRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on partial match 1"); + } + + // + // partial match 0 expected + // + rIt = pubRings.getKeyRings("XXX", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of public keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = pubRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on case-insensitive partial match"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + pk.getSignatures(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + + // + // exact match + // + rIt = secretRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = secretRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on partial match 1"); + } + + // + // exact match 0 expected + // + rIt = secretRings.getKeyRings("test", false); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of secret keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = secretRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on case-insensitive partial match"); + } + } + + public void test2() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + + keyCount++; + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + if (pk.getKeyID() == -1413891222336124627L) + { + int sCount = 0; + Iterator sIt = pk.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + while (sIt.hasNext()) + { + int type = ((PGPSignature)sIt.next()).getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING) + { + fail("failed to return correct signature type"); + } + sCount++; + } + + if (sCount != 1) + { + fail("failed to find binding signature"); + } + } + + pk.getSignatures(); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass1)); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass2)); + } + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 2) + { + fail("wrong number of secret keyrings"); + } + } + + public void test3() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubK = (PGPPublicKey)it.next(); + + pubK.getSignatures(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test4() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test5() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + if (noIDEA()) + { + return; + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec5pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + private boolean noIDEA() + { + return true; + } + + public void test6() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub6); + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.getKeyID() == 0x5ce086b5b5a18ff4L) + { + int count = 0; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test6."); + } + } + } + } + + byte[] encRing = pubRings.getEncoded(); + } + + public void test7() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(pub7, new BcKeyFingerprintCalculator()); + Iterator it = pgpPub.getPublicKeys(); + PGPPublicKey masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + + int count = 0; + PGPSignature sig = null; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + + while (sIt.hasNext()) + { + sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test7."); + } + + sig.init(new BcPGPContentVerifierBuilderProvider(), masterKey); + + if (!sig.verifyCertification(k)) + { + fail("failed to verify revocation certification"); + } + } + } + + public void test8() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub8); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec8); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec8pass)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test9() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec9); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + PGPPrivateKey pKey = k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec9pass)); + if (keyCount == 1 && pKey != null) + { + fail("primary secret key found, null expected"); + } + } + + if (keyCount != 3) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test10() + throws Exception + { + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new BcKeyFingerprintCalculator()); + Iterator secretKeys = secretRing.getSecretKeys(); + + while (secretKeys.hasNext()) + { + PGPPublicKey pubKey = ((PGPSecretKey)secretKeys.next()).getPublicKey(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on secret key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on secret key ring"); + } + } + + PGPPublicKeyRing publicRing = new PGPPublicKeyRing(pub10, new BcKeyFingerprintCalculator()); + Iterator publicKeys = publicRing.getPublicKeys(); + + while (publicKeys.hasNext()) + { + PGPPublicKey pubKey = (PGPPublicKey)publicKeys.next(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on public key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on public key ring"); + } + } + } + + public void generateTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void insertMasterTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + + rsaKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing1 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair2, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing2 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing2 = keyRingGen.generatePublicKeyRing(); + + try + { + PGPPublicKeyRing.insertPublicKey(pubRing1, pubRing2.getPublicKey()); + fail("adding second master key (public) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in public test"); + } + } + + try + { + PGPSecretKeyRing.insertSecretKey(secRing1, secRing2.getSecretKey()); + fail("adding second master key (secret) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in secret test"); + } + } + } + + public void generateSha1Test() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, true, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void test11() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(subKeyBindingKey, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + while (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + if (key.getValidSeconds() != 0) + { + fail("expiration time non-zero"); + } + } + } + + private void rewrapTest() + throws Exception + { + SecureRandom rand = new SecureRandom(); + + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + } + } + } + + private void testPublicKeyRingWithX509() + throws Exception + { + checkPublicKeyRingWithX509(pubWithX509); + + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(pubWithX509, new BcKeyFingerprintCalculator()); + + checkPublicKeyRingWithX509(pubRing.getEncoded()); + } + + private void testSecretKeyRingWithPersonalCertificate() + throws Exception + { + checkSecretKeyRingWithPersonalCertificate(secWithPersonalCertificate); + PGPSecretKeyRingCollection secRing = new PGPSecretKeyRingCollection(secWithPersonalCertificate); + checkSecretKeyRingWithPersonalCertificate(secRing.getEncoded()); + } + + private void testUmlaut() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(umlautKeySig, new BcKeyFingerprintCalculator()); + + PGPPublicKey pub = pubRing.getPublicKey(); + String userID = (String)pub.getUserIDs().next(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID test"); + } + } + } + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + char[] passPhrase = "passwd".toCharArray(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + userID, PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + + pub = pubRing1.getPublicKey(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID creation test"); + } + } + } + } + + private void checkSecretKeyRingWithPersonalCertificate(byte[] keyRing) + throws Exception + { + PGPSecretKeyRingCollection secCol = new PGPSecretKeyRingCollection(keyRing); + + + int count = 0; + + for (Iterator rIt = secCol.getKeyRings(); rIt.hasNext();) + { + PGPSecretKeyRing ring = (PGPSecretKeyRing)rIt.next(); + + for (Iterator it = ring.getExtraPublicKeys(); it.hasNext();) + { + it.next(); + count++; + } + } + + if (count != 1) + { + fail("personal certificate data subkey not found - count = " + count); + } + } + + private void checkPublicKeyRingWithX509(byte[] keyRing) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(keyRing, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + if (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + Iterator sIt = key.getSignatures(); + + if (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + if (sig.getKeyAlgorithm() != 100) + { + fail("experimental signature not found"); + } + if (!areEqual(sig.getSignature(), Hex.decode("000101"))) + { + fail("experimental encoding check failed"); + } + } + else + { + fail("no signature found"); + } + } + else + { + fail("no key found"); + } + } + + public void performTest() + throws Exception + { + try + { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + // test7(); + test8(); + test9(); + test10(); + test11(); + generateTest(); + generateSha1Test(); + rewrapTest(); + testPublicKeyRingWithX509(); + testSecretKeyRingWithPersonalCertificate(); + insertMasterTest(); + testUmlaut(); + } + catch (PGPException e) + { + if (e.getUnderlyingException() != null) + { + Exception ex = e.getUnderlyingException(); + fail("exception: " + ex, ex); + } + else + { + fail("exception: " + e, e); + } + } + } + + public String getName() + { + return "BcPGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPKeyRingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java new file mode 100644 index 000000000..b6952107c --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java @@ -0,0 +1,451 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; + +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTestResult; +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class PGPDSAElGamalTest implements Test +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + private boolean notEqual( + byte[] b1, + byte[] b2) + { + if (b1.length != b2.length) + { + return true; + } + + for (int i = 0; i != b2.length; i++) + { + if (b1[i] != b2[i]) + { + return true; + } + } + + return false; + } + + public TestResult perform() + { + try + { + String file = null; + KeyFactory fact = KeyFactory.getInstance("DSA", "SC"); + PGPPublicKey pubKey = null; + PrivateKey privKey = null; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PGPPublicKey.DSA, PGPUtil.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream(cGen.open(bOut)); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bcOut, PGPLiteralData.BINARY, "_CONSOLE", data.getBytes().length, new Date()); + int ch; + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + sGen.generate().encode(bcOut); + + lGen.close(); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(pubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + return new SimpleTestResult(false, getName() + ": Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key sutiable for encryption + // + long pgpKeyID = 0; + PublicKey pKey = null; + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = pgpKey.getKey("SC"); + pgpKeyID = pgpKey.getKeyID(); + + // + // verify the key + // + + } + } + + Cipher c = Cipher.getInstance("ElGamal/None/PKCS1Padding", "SC"); + + c.init(Cipher.ENCRYPT_MODE, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.doFinal(in); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + c.init(Cipher.DECRYPT_MODE, pgpPrivKey.getKey()); + + out = c.doFinal(out); + + if (notEqual(in, out)) + { + return new SimpleTestResult(false, getName() + ": decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (notEqual(bOut.toByteArray(), text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.initVerify(pgpPub.getPublicKey(), "SC"); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + return new SimpleTestResult(false, getName() + ": Failed signature check"); + } + + if (notEqual(bOut.toByteArray(), text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.TRIPLE_DES, new SecureRandom(), "SC"); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(puK); + + OutputStream cOut = cPk.open(cbOut, bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (notEqual(out, text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in generated packet"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + e.printStackTrace(); + if (e instanceof PGPException) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + Test test = new PGPDSAElGamalTest(); + TestResult result = test.perform(); + + System.out.println(result.toString()); + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java new file mode 100644 index 000000000..4510f3864 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java @@ -0,0 +1,968 @@ +package org.spongycastle.openpgp.test; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTestResult; +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class PGPKeyRingTest + implements Test +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + + + char[] sec5pass1 = "12345678".toCharArray(); + + private boolean notEqual( + byte[] b1, + byte[] b2) + { + if (b1.length != b2.length) + { + return true; + } + + for (int i = 0; i != b2.length; i++) + { + if (b1[i] != b2[i]) + { + return true; + } + } + + return false; + } + + public TestResult test1() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test2() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + + keyCount++; + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(sec2pass1, "SC"); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(sec2pass2, "SC"); + } + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - "+ e); + } + } + + public TestResult test3() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test4() + { + try + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test5() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec5pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult perform() + { + TestResult res = test1(); + if (!res.isSuccessful()) + { + return res; + } + + res = test2(); + if (!res.isSuccessful()) + { + return res; + } + + res = test3(); + if (!res.isSuccessful()) + { + return res; + } + + res = test4(); + if (!res.isSuccessful()) + { + return res; + } + + res = test5(); + if (!res.isSuccessful()) + { + return res; + } + + return res; + } + + public String getName() + { + return "PGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Test test = new PGPKeyRingTest(); + TestResult result = test.perform(); + + System.out.println(result.toString()); + } +} diff --git a/libraries/spongycastle/pg/src/main/jdk1.4/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/libraries/spongycastle/pg/src/main/jdk1.4/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java new file mode 100644 index 000000000..52639b242 --- /dev/null +++ b/libraries/spongycastle/pg/src/main/jdk1.4/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java @@ -0,0 +1,373 @@ +package org.spongycastle.openpgp.operator.jcajce; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import org.spongycastle.jce.interfaces.ECPrivateKey; +import org.spongycastle.jce.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Date; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.DEROctetString; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9ECPoint; +import org.spongycastle.bcpg.BCPGKey; +import org.spongycastle.bcpg.DSAPublicBCPGKey; +import org.spongycastle.bcpg.DSASecretBCPGKey; +import org.spongycastle.bcpg.ECDHPublicBCPGKey; +import org.spongycastle.bcpg.ECDSAPublicBCPGKey; +import org.spongycastle.bcpg.ECSecretBCPGKey; +import org.spongycastle.bcpg.ElGamalPublicBCPGKey; +import org.spongycastle.bcpg.ElGamalSecretBCPGKey; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyPacket; +import org.spongycastle.bcpg.RSAPublicBCPGKey; +import org.spongycastle.bcpg.RSASecretBCPGKey; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jcajce.DefaultJcaJceHelper; +import org.spongycastle.jcajce.NamedJcaJceHelper; +import org.spongycastle.jcajce.ProviderJcaJceHelper; +import org.spongycastle.jce.interfaces.ElGamalPrivateKey; +import org.spongycastle.jce.interfaces.ElGamalPublicKey; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.jce.spec.ElGamalPrivateKeySpec; +import org.spongycastle.jce.spec.ElGamalPublicKeySpec; +import org.spongycastle.openpgp.PGPAlgorithmParameters; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKdfParameters; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; + +public class JcaPGPKeyConverter +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private KeyFingerPrintCalculator fingerPrintCalculator = new JcaKeyFingerprintCalculator(); + + public JcaPGPKeyConverter setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JcaPGPKeyConverter setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public PublicKey getPublicKey(PGPPublicKey publicKey) + throws PGPException + { + KeyFactory fact; + + PublicKeyPacket publicPk = publicKey.getPublicKeyPacket(); + + try + { + switch (publicPk.getAlgorithm()) + { + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_SIGN: + RSAPublicBCPGKey rsaK = (RSAPublicBCPGKey)publicPk.getKey(); + RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsaK.getModulus(), rsaK.getPublicExponent()); + + fact = helper.createKeyFactory("RSA"); + + return fact.generatePublic(rsaSpec); + case PublicKeyAlgorithmTags.DSA: + DSAPublicBCPGKey dsaK = (DSAPublicBCPGKey)publicPk.getKey(); + DSAPublicKeySpec dsaSpec = new DSAPublicKeySpec(dsaK.getY(), dsaK.getP(), dsaK.getQ(), dsaK.getG()); + + fact = helper.createKeyFactory("DSA"); + + return fact.generatePublic(dsaSpec); + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey)publicPk.getKey(); + ElGamalPublicKeySpec elSpec = new ElGamalPublicKeySpec(elK.getY(), new ElGamalParameterSpec(elK.getP(), elK.getG())); + + fact = helper.createKeyFactory("ElGamal"); + + return fact.generatePublic(elSpec); + case PublicKeyAlgorithmTags.EC: + ECDHPublicBCPGKey ecdhK = (ECDHPublicBCPGKey)publicPk.getKey(); + ECPublicKeySpec ecDhSpec = new ECPublicKeySpec( + ecdhK.getPoint(), + convertX9Parameters(ecdhK.getCurveOID(), NISTNamedCurves.getByOID(ecdhK.getCurveOID()))); + fact = helper.createKeyFactory("ECDH"); + + return fact.generatePublic(ecDhSpec); + case PublicKeyAlgorithmTags.ECDSA: + ECDSAPublicBCPGKey ecdsaK = (ECDSAPublicBCPGKey)publicPk.getKey(); + ECPublicKeySpec ecDsaSpec = new ECPublicKeySpec( + ecdsaK.getPoint(), + convertX9Parameters(ecdsaK.getCurveOID(), NISTNamedCurves.getByOID(ecdsaK.getCurveOID()))); + fact = helper.createKeyFactory("ECDSA"); + + return fact.generatePublic(ecDsaSpec); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("exception constructing public key", e); + } + } + + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p/> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param algorithmParameters additional parameters to be stored against the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @throws PGPException on key creation problem. + */ + public PGPPublicKey getPGPPublicKey(int algorithm, PGPAlgorithmParameters algorithmParameters, PublicKey pubKey, Date time) + throws PGPException + { + BCPGKey bcpgKey; + + if (pubKey instanceof RSAPublicKey) + { + RSAPublicKey rK = (RSAPublicKey)pubKey; + + bcpgKey = new RSAPublicBCPGKey(rK.getModulus(), rK.getPublicExponent()); + } + else if (pubKey instanceof DSAPublicKey) + { + DSAPublicKey dK = (DSAPublicKey)pubKey; + DSAParams dP = dK.getParams(); + + bcpgKey = new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY()); + } + else if (pubKey instanceof ElGamalPublicKey) + { + ElGamalPublicKey eK = (ElGamalPublicKey)pubKey; + ElGamalParameterSpec eS = eK.getParameters(); + + bcpgKey = new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY()); + } + else if (pubKey instanceof ECPublicKey) + { + SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()); + + // TODO: should probably match curve by comparison as well + ASN1ObjectIdentifier curveOid = ASN1ObjectIdentifier.getInstance(keyInfo.getAlgorithm().getParameters()); + + X9ECParameters params = NISTNamedCurves.getByOID(curveOid); + + ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes()); + X9ECPoint derQ = new X9ECPoint(params.getCurve(), key); + + if (algorithm == PGPPublicKey.EC) + { + PGPKdfParameters kdfParams = (PGPKdfParameters)algorithmParameters; + if (kdfParams == null) + { + // We default to these as they are specified as mandatory in RFC 6631. + kdfParams = new PGPKdfParameters(HashAlgorithmTags.SHA256, SymmetricKeyAlgorithmTags.AES_128); + } + bcpgKey = new ECDHPublicBCPGKey(curveOid, derQ.getPoint(), kdfParams.getHashAlgorithm(), kdfParams.getSymmetricWrapAlgorithm()); + } + else + { + bcpgKey = new ECDSAPublicBCPGKey(curveOid, derQ.getPoint()); + } + } + else + { + throw new PGPException("unknown key class"); + } + + return new PGPPublicKey(new PublicKeyPacket(algorithm, time, bcpgKey), fingerPrintCalculator); + } + + /** + * Create a PGPPublicKey from the passed in JCA one. + * <p/> + * Note: the time passed in affects the value of the key's keyID, so you probably only want + * to do this once for a JCA key, or make sure you keep track of the time you used. + * + * @param algorithm asymmetric algorithm type representing the public key. + * @param pubKey actual public key to associate. + * @param time date of creation. + * @throws PGPException on key creation problem. + */ + public PGPPublicKey getPGPPublicKey(int algorithm, PublicKey pubKey, Date time) + throws PGPException + { + return getPGPPublicKey(algorithm, null, pubKey, time); + } + + public PrivateKey getPrivateKey(PGPPrivateKey privKey) + throws PGPException + { + if (privKey instanceof JcaPGPPrivateKey) + { + return ((JcaPGPPrivateKey)privKey).getPrivateKey(); + } + + PublicKeyPacket pubPk = privKey.getPublicKeyPacket(); + BCPGKey privPk = privKey.getPrivateKeyDataPacket(); + + try + { + KeyFactory fact; + + switch (pubPk.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_GENERAL: + case PGPPublicKey.RSA_SIGN: + RSAPublicBCPGKey rsaPub = (RSAPublicBCPGKey)pubPk.getKey(); + RSASecretBCPGKey rsaPriv = (RSASecretBCPGKey)privPk; + RSAPrivateCrtKeySpec rsaPrivSpec = new RSAPrivateCrtKeySpec( + rsaPriv.getModulus(), + rsaPub.getPublicExponent(), + rsaPriv.getPrivateExponent(), + rsaPriv.getPrimeP(), + rsaPriv.getPrimeQ(), + rsaPriv.getPrimeExponentP(), + rsaPriv.getPrimeExponentQ(), + rsaPriv.getCrtCoefficient()); + + fact = helper.createKeyFactory("RSA"); + + return fact.generatePrivate(rsaPrivSpec); + case PGPPublicKey.DSA: + DSAPublicBCPGKey dsaPub = (DSAPublicBCPGKey)pubPk.getKey(); + DSASecretBCPGKey dsaPriv = (DSASecretBCPGKey)privPk; + DSAPrivateKeySpec dsaPrivSpec = + new DSAPrivateKeySpec(dsaPriv.getX(), dsaPub.getP(), dsaPub.getQ(), dsaPub.getG()); + + fact = helper.createKeyFactory("DSA"); + + return fact.generatePrivate(dsaPrivSpec); + case PublicKeyAlgorithmTags.ECDH: + ECDHPublicBCPGKey ecdhPub = (ECDHPublicBCPGKey)pubPk.getKey(); + ECSecretBCPGKey ecdhK = (ECSecretBCPGKey)privPk; + ECPrivateKeySpec ecDhSpec = new ECPrivateKeySpec( + ecdhK.getX(), + convertX9Parameters(ecdhPub.getCurveOID(), NISTNamedCurves.getByOID(ecdhPub.getCurveOID()))); + fact = helper.createKeyFactory("ECDH"); + + return fact.generatePrivate(ecDhSpec); + case PublicKeyAlgorithmTags.ECDSA: + ECDSAPublicBCPGKey ecdsaPub = (ECDSAPublicBCPGKey)pubPk.getKey(); + ECSecretBCPGKey ecdsaK = (ECSecretBCPGKey)privPk; + ECPrivateKeySpec ecDsaSpec = new ECPrivateKeySpec( + ecdsaK.getX(), + convertX9Parameters(ecdsaPub.getCurveOID(), NISTNamedCurves.getByOID(ecdsaPub.getCurveOID()))); + fact = helper.createKeyFactory("ECDSA"); + + return fact.generatePrivate(ecDsaSpec); + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPublicBCPGKey elPub = (ElGamalPublicBCPGKey)pubPk.getKey(); + ElGamalSecretBCPGKey elPriv = (ElGamalSecretBCPGKey)privPk; + ElGamalPrivateKeySpec elSpec = new ElGamalPrivateKeySpec(elPriv.getX(), new ElGamalParameterSpec(elPub.getP(), elPub.getG())); + + fact = helper.createKeyFactory("ElGamal"); + + return fact.generatePrivate(elSpec); + default: + throw new PGPException("unknown public key algorithm encountered"); + } + } + catch (PGPException e) + { + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception constructing key", e); + } + } + + /** + * Convert a PrivateKey into a PGPPrivateKey. + * + * @param pub the corresponding PGPPublicKey to privKey. + * @param privKey the private key for the key in pub. + * @return a PGPPrivateKey + * @throws PGPException + */ + public PGPPrivateKey getPGPPrivateKey(PGPPublicKey pub, PrivateKey privKey) + throws PGPException + { + BCPGKey privPk; + + switch (pub.getAlgorithm()) + { + case PGPPublicKey.RSA_ENCRYPT: + case PGPPublicKey.RSA_SIGN: + case PGPPublicKey.RSA_GENERAL: + RSAPrivateCrtKey rsK = (RSAPrivateCrtKey)privKey; + + privPk = new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ()); + break; + case PGPPublicKey.DSA: + DSAPrivateKey dsK = (DSAPrivateKey)privKey; + + privPk = new DSASecretBCPGKey(dsK.getX()); + break; + case PGPPublicKey.ELGAMAL_ENCRYPT: + case PGPPublicKey.ELGAMAL_GENERAL: + ElGamalPrivateKey esK = (ElGamalPrivateKey)privKey; + + privPk = new ElGamalSecretBCPGKey(esK.getX()); + break; + case PGPPublicKey.EC: + case PGPPublicKey.ECDSA: + ECPrivateKey ecK = (ECPrivateKey)privKey; + + privPk = new ECSecretBCPGKey(ecK.getD()); + break; + default: + throw new PGPException("unknown key class"); + } + + return new PGPPrivateKey(pub.getKeyID(), pub.getPublicKeyPacket(), privPk); + } + + private ECParameterSpec convertX9Parameters(ASN1ObjectIdentifier curveOid, X9ECParameters curveParameters) + { + return new ECNamedCurveParameterSpec(curveOid.getId(), + curveParameters.getCurve(), + curveParameters.getG(), + curveParameters.getN(), + curveParameters.getH(), + curveParameters.getSeed()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/examples/test/AllTests.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/examples/test/AllTests.java new file mode 100644 index 000000000..f015de6f0 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/examples/test/AllTests.java @@ -0,0 +1,415 @@ +package org.spongycastle.openpgp.examples.test; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.spongycastle.openpgp.examples.ClearSignedFileProcessor; +import org.spongycastle.openpgp.examples.DSAElGamalKeyRingGenerator; +import org.spongycastle.openpgp.examples.KeyBasedFileProcessor; +import org.spongycastle.openpgp.examples.KeyBasedLargeFileProcessor; +import org.spongycastle.openpgp.examples.PBEFileProcessor; +import org.spongycastle.openpgp.examples.RSAKeyPairGenerator; +import org.spongycastle.openpgp.examples.SignedFileProcessor; +import org.spongycastle.util.encoders.Base64; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; + +public class AllTests + extends TestCase +{ + byte[] clearSignedPublicKey = Base64.decode( + "mQELBEQh2+wBCAD26kte0hO6flr7Y2aetpPYutHY4qsmDPy+GwmmqVeCDkX+" + + "r1g7DuFbMhVeu0NkKDnVl7GsJ9VarYsFYyqu0NzLa9XS2qlTIkmJV+2/xKa1" + + "tzjn18fT/cnAWL88ZLCOWUr241aPVhLuIc6vpHnySpEMkCh4rvMaimnTrKwO" + + "42kgeDGd5cXfs4J4ovRcTbc4hmU2BRVsRjiYMZWWx0kkyL2zDVyaJSs4yVX7" + + "Jm4/LSR1uC/wDT0IJJuZT/gQPCMJNMEsVCziRgYkAxQK3OWojPSuv4rXpyd4" + + "Gvo6IbvyTgIskfpSkCnQtORNLIudQSuK7pW+LkL62N+ohuKdMvdxauOnAAYp" + + "tBNnZ2dnZ2dnZyA8Z2dnQGdnZ2c+iQE2BBMBAgAgBQJEIdvsAhsDBgsJCAcD" + + "AgQVAggDBBYCAwECHgECF4AACgkQ4M/Ier3f9xagdAf/fbKWBjLQM8xR7JkR" + + "P4ri8YKOQPhK+VrddGUD59/wzVnvaGyl9MZE7TXFUeniQq5iXKnm22EQbYch" + + "v2Jcxyt2H9yptpzyh4tP6tEHl1C887p2J4qe7F2ATua9CzVGwXQSUbKtj2fg" + + "UZP5SsNp25guhPiZdtkf2sHMeiotmykFErzqGMrvOAUThrO63GiYsRk4hF6r" + + "cQ01d+EUVpY/sBcCxgNyOiB7a84sDtrxnX5BTEZDTEj8LvuEyEV3TMUuAjx1" + + "7Eyd+9JtKzwV4v3hlTaWOvGro9nPS7YaPuG+RtufzXCUJPbPfTjTvtGOqvEz" + + "oztls8tuWA0OGHba9XfX9rfgorACAAM="); + + String crOnlyMessage = + "\r" + + " hello world!\r" + + "\r" + + "- dash\r"; + + String nlOnlyMessage = + "\n" + + " hello world!\n" + + "\n" + + "- dash\n"; + + String crNlMessage = + "\r\n" + + " hello world!\r\n" + + "\r\n" + + "- dash\r\n"; + + String crNlMessageTrailingWhiteSpace = + "\r\n" + + " hello world! \t\r\n" + + "\r\n" + + "\r\n"; + + String crOnlySignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\r" + + "Hash: SHA256\r" + + "\r" + + "\r" + + " hello world!\r" + + "\r" + + "- - dash\r" + + "-----BEGIN PGP SIGNATURE-----\r" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r" + + "\r" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r"; + + + String nlOnlySignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\n" + + "Hash: SHA256\n" + + "\n" + + "\n" + + " hello world!\n" + + "\n" + + "- - dash\n" + + "-----BEGIN PGP SIGNATURE-----\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\n" + + "\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\n" + + "=84Nd\n" + + "-----END PGP SIGNATURE-----\n"; + + String crNlSignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\r\n" + + "Hash: SHA256\r\n" + + "\r\n" + + "\r\n" + + " hello world!\r\n" + + "\r\n" + + "- - dash\r\n" + + "-----BEGIN PGP SIGNATURE-----\r\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n" + + "\r\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r\n"; + + String crNlSignedMessageTrailingWhiteSpace = + "-----BEGIN PGP SIGNED MESSAGE-----\r\n" + + "Hash: SHA256\r\n" + + "\r\n" + + "\r\n" + + " hello world! \t\r\n" + + "\r\n" + + "- - dash\r\n" + + "-----BEGIN PGP SIGNATURE-----\r\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n" + + "\r\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r\n"; + + private PrintStream _oldOut; + private PrintStream _oldErr; + + private ByteArrayOutputStream _currentOut; + private ByteArrayOutputStream _currentErr; + + public void setUp() + throws Exception + { + _oldOut = System.out; + _oldErr = System.err; + _currentOut = new ByteArrayOutputStream(); + _currentErr = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(_currentOut)); + System.setErr(new PrintStream(_currentErr)); + } + + public void tearDown() + { + System.setOut(_oldOut); + System.setErr(_oldErr); + } + + public void testRSAKeyGeneration() + throws Exception + { + RSAKeyPairGenerator.main(new String[] { "test", "password" }); + + createSmallTestInput(); + createLargeTestInput(); + + checkSigning("bpg"); + checkKeyBasedEncryption("bpg"); + checkLargeKeyBasedEncryption("bpg"); + + RSAKeyPairGenerator.main(new String[] { "-a", "test", "password" }); + + checkSigning("asc"); + checkKeyBasedEncryption("asc"); + checkLargeKeyBasedEncryption("asc"); + } + + public void testDSAElGamaleKeyGeneration() + throws Exception + { + DSAElGamalKeyRingGenerator.main(new String[] { "test", "password" }); + + createSmallTestInput(); + createLargeTestInput(); + + checkSigning("bpg"); + checkKeyBasedEncryption("bpg"); + checkLargeKeyBasedEncryption("bpg"); + + DSAElGamalKeyRingGenerator.main(new String[] { "-a", "test", "password" }); + + checkSigning("asc"); + checkKeyBasedEncryption("asc"); + checkLargeKeyBasedEncryption("asc"); + } + + public void testPBEEncryption() + throws Exception + { + _currentErr.reset(); + + PBEFileProcessor.main(new String[] { "-e", "test.txt", "password" }); + + PBEFileProcessor.main(new String[] { "-d", "test.txt.bpg", "password" }); + + assertEquals("no message integrity check", getLine(_currentErr)); + + PBEFileProcessor.main(new String[] { "-e", "-i", "test.txt", "password" }); + + PBEFileProcessor.main(new String[] { "-d", "test.txt.bpg", "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + + PBEFileProcessor.main(new String[] { "-e", "-ai", "test.txt", "password" }); + + PBEFileProcessor.main(new String[] { "-d", "test.txt.asc", "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + } + + public void testClearSigned() + throws Exception + { + createTestFile(clearSignedPublicKey, "pub.bpg"); + + checkClearSignedVerify(nlOnlySignedMessage); + checkClearSignedVerify(crOnlySignedMessage); + checkClearSignedVerify(crNlSignedMessage); + checkClearSignedVerify(crNlSignedMessageTrailingWhiteSpace); + + ClearSignedFileProcessor.main(new String[] { "-v", "test.txt.asc", "pub.bpg" }); + + RSAKeyPairGenerator.main(new String[] { "test", "password" }); + + checkClearSigned(crOnlyMessage); + checkClearSigned(nlOnlyMessage); + checkClearSigned(crNlMessage); + checkClearSigned(crNlMessageTrailingWhiteSpace); + } + + public void testClearSignedBogusInput() + throws Exception + { + createTestFile(clearSignedPublicKey, "test.txt"); + + ClearSignedFileProcessor.main(new String[] { "-s", "test.txt", "secret.bpg", "password" }); + } + + private void checkClearSignedVerify(String message) + throws Exception + { + createTestData(message, "test.txt.asc"); + + ClearSignedFileProcessor.main(new String[] { "-v", "test.txt.asc", "pub.bpg" }); + } + + private void checkClearSigned(String message) + throws Exception + { + createTestData(message, "test.txt"); + + ClearSignedFileProcessor.main(new String[] { "-s", "test.txt", "secret.bpg", "password" }); + ClearSignedFileProcessor.main(new String[] { "-v", "test.txt.asc", "pub.bpg" }); + } + + private void checkSigning(String type) + throws Exception + { + _currentOut.reset(); + + SignedFileProcessor.main(new String[] { "-s", "test.txt", "secret." + type, "password" }); + + SignedFileProcessor.main(new String[] { "-v", "test.txt.bpg", "pub." + type }); + + assertEquals("signature verified.", getLine(_currentOut)); + + SignedFileProcessor.main(new String[] { "-s", "-a", "test.txt", "secret." + type, "password" }); + + SignedFileProcessor.main(new String[] { "-v", "test.txt.asc", "pub." + type }); + + assertEquals("signature verified.", getLine(_currentOut)); + } + + private void checkKeyBasedEncryption(String type) + throws Exception + { + _currentErr.reset(); + + KeyBasedFileProcessor.main(new String[] { "-e", "test.txt", "pub." + type }); + + KeyBasedFileProcessor.main(new String[] { "-d", "test.txt.bpg", "secret." + type, "password" }); + + assertEquals("no message integrity check", getLine(_currentErr)); + + KeyBasedFileProcessor.main(new String[] { "-e", "-i", "test.txt", "pub." + type }); + + KeyBasedFileProcessor.main(new String[] { "-d", "test.txt.bpg", "secret." + type, "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + + KeyBasedFileProcessor.main(new String[] { "-e", "-ai", "test.txt", "pub." + type }); + + KeyBasedFileProcessor.main(new String[] { "-d", "test.txt.asc", "secret." + type, "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + } + + private void checkLargeKeyBasedEncryption(String type) + throws Exception + { + _currentErr.reset(); + + KeyBasedLargeFileProcessor.main(new String[] { "-e", "large.txt", "pub." + type }); + + KeyBasedLargeFileProcessor.main(new String[] { "-d", "large.txt.bpg", "secret." + type, "password" }); + + assertEquals("no message integrity check", getLine(_currentErr)); + + KeyBasedLargeFileProcessor.main(new String[] { "-e", "-i", "large.txt", "pub." + type }); + + KeyBasedLargeFileProcessor.main(new String[] { "-d", "large.txt.bpg", "secret." + type, "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + + KeyBasedLargeFileProcessor.main(new String[] { "-e", "-ai", "large.txt", "pub." + type }); + + KeyBasedLargeFileProcessor.main(new String[] { "-d", "large.txt.asc", "secret." + type, "password" }); + + assertEquals("message integrity check passed", getLine(_currentErr)); + } + + private void createSmallTestInput() + throws IOException + { + BufferedWriter bfOut = new BufferedWriter(new FileWriter("test.txt")); + + bfOut.write("hello world!"); + bfOut.newLine(); + + bfOut.close(); + } + + private void createLargeTestInput() + throws IOException + { + BufferedWriter bfOut = new BufferedWriter(new FileWriter("large.txt")); + + for (int i = 0; i != 2000; i++) + { + bfOut.write("hello world!"); + bfOut.newLine(); + } + + bfOut.close(); + } + + private void createTestData(String testData, String name) + throws IOException + { + BufferedWriter bfOut = new BufferedWriter(new FileWriter(name)); + + bfOut.write(testData); + + bfOut.close(); + } + + private void createTestFile(byte[] keyData, String name) + throws IOException + { + FileOutputStream fOut = new FileOutputStream(name); + + fOut.write(keyData); + + fOut.close(); + } + + private String getLine( + ByteArrayOutputStream out) + throws IOException + { + BufferedReader bRd = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(out.toByteArray()))); + + out.reset(); + + return bRd.readLine(); + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("OpenPGP Example Tests"); + + suite.addTestSuite(AllTests.class); + + return suite; + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/AllTests.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/AllTests.java new file mode 100644 index 000000000..81bfa3015 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/AllTests.java @@ -0,0 +1,46 @@ +package org.spongycastle.openpgp.test; + +import java.security.Security; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testPGP() + { + Security.addProvider(new BouncyCastleProvider()); + + org.spongycastle.util.test.Test[] tests = RegressionTest.tests; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("OpenPGP Tests"); + + suite.addTestSuite(AllTests.class); + suite.addTestSuite(DSA2Test.class); + suite.addTestSuite(PGPUnicodeTest.class); + + return suite; + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java new file mode 100644 index 000000000..2e909e6f8 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java @@ -0,0 +1,564 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSAElGamalTest + extends SimpleTest +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + public void performTest() + throws Exception + { + try + { + PGPPublicKey pubKey; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + if (pubKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key suitable for encryption + // + long pgpKeyID = 0; + AsymmetricKeyParameter pKey = null; + BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = keyConverter.getPublicKey(pgpKey); + pgpKeyID = pgpKey.getKeyID(); + if (pgpKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // verify the key + // + + } + } + + AsymmetricBlockCipher c = new PKCS1Encoding(new ElGamalEngine()); + + c.init(true, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.processBlock(in, 0, in.length); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + c.init(false, keyConverter.getPrivateKey(pgpPrivKey)); + + out = c.processBlock(out, 0, out.length); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // use of PGPKeyPair + // + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ElGamal", "SC"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + kpg.initialize(elParams); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.ELGAMAL_GENERAL , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + + + // Test bug with ElGamal P size != 0 mod 8 (don't use these sizes at home!) + SecureRandom random = new SecureRandom(); + for (int pSize = 257; pSize < 264; ++pSize) + { + // Generate some parameters of the given size + AlgorithmParameterGenerator a = AlgorithmParameterGenerator.getInstance("ElGamal", "SC"); + a.init(pSize, new SecureRandom()); + AlgorithmParameters params = a.generateParameters(); + + DHParameterSpec elP = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal", "SC"); + + keyGen.initialize(elP); + + + // Run a short encrypt/decrypt test with random key for the given parameters + kp = keyGen.generateKeyPair(); + + PGPKeyPair elGamalKeyPair = new PGPKeyPair( + PublicKeyAlgorithmTags.ELGAMAL_GENERAL, kp, new Date()); + + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(random)); + + puK = elGamalKeyPair.getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + cbOut = new ByteArrayOutputStream(); + + cOut = cPk.open(cbOut, text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = elGamalKeyPair.getPrivateKey(); + + // Note: This is where an exception would be expected if the P size causes problems + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + ByteArrayOutputStream dec = new ByteArrayOutputStream(); + + int b; + while ((b = clear.read()) >= 0) + { + dec.write(b); + } + + byte[] decText = dec.toByteArray(); + + if (!areEqual(text, decText)) + { + fail("decrypted message incorrect"); + } + } + + // check sub key encoding + + it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (!pgpKey.isMasterKey()) + { + byte[] kEnc = pgpKey.getEncoded(); + + PGPObjectFactory objF = new PGPObjectFactory(kEnc); + + PGPPublicKey k = (PGPPublicKey)objF.nextObject(); + + pKey = keyConverter.getPublicKey(k); + pgpKeyID = k.getKeyID(); + if (k.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + if (objF.nextObject() != null) + { + fail("failed - stream not fully parsed."); + } + } + } + + } + catch (PGPException e) + { + fail("exception: " + e.getMessage(), e.getUnderlyingException()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPDSAElGamalTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSATest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSATest.java new file mode 100644 index 000000000..5d87730bd --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPDSATest.java @@ -0,0 +1,633 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mQGiBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbLQzRXJpYyBFY2hp" + + "ZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExEC" + + "ABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j9enEyjRDAlwAn2rrom0s" + + "MhufWK5vIRwg7gj5qsLEAJ4vnT5dPBVblofsG+pDoCVeJXGGng=="); + + byte[] testPrivKey = + Base64.decode( + "lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC" + + "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu" + + "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh" + + "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j" + + "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD" + + "4nXkHg=="); + + byte[] testPrivKey2 = + Base64.decode( + "lQHhBEAnoewRBADRvKgDhbV6pMzqYfUgBsLxSHzmycpuxGbjMrpyKHDOEemj" + + "iQb6TyyBKUoR28/pfshFP9R5urtKIT7wjVrDuOkxYkgRhNm+xmPXW2Lw3D++" + + "MQrC5VWe8ywBltz6T9msmChsaKo2hDhIiRI/mg9Q6rH9pJKtVGi4R7CgGxM2" + + "STQ5fwCgub38qGS1W2O4hUsa+3gva5gaNZUEAItegda4/H4t88XdWxW3D8pv" + + "RnFz26/ADdImVaQlBoumD15VmcgYoT1Djizey7X8vfV+pntudESzLbn3GHlI" + + "6C09seH4e8eYP63t7KU/qbUCDomlSswd1OgQ/RxfN86q765K2t3K1i3wDSxe" + + "EgSRyGKee0VNvOBFOFhuWt+patXaBADE1riNkUxg2P4lBNWwu8tEZRmsl/Ys" + + "DBIzXBshoMzZCvS5PnNXMW4G3SAaC9OC9jvKSx9IEWhKjfjs3QcWzXR28mcm" + + "5na0bTxeOMlaPPhBdkTCmFl0IITWlH/pFlR2ah9WYoWYhZEL2tqB82wByzxH" + + "SkSeD9V5oeSCdCcqiqkEmv4DAwLeNsQ2XGJVRmA4lld+CR5vRxpT/+/2xklp" + + "lxVf/nx0+thrHDpro3u/nINIIObk0gh59+zaEEe3APlHqbQVYWFhIGJiYiA8" + + "Y2NjQGRkZC5lZWU+iFoEExECABoFAkAnoewFCwcDAgEDFQIDAxYCAQIeAQIX" + + "gAAKCRA5nBpCS63az85BAKCbPfU8ATrFvkXhzGNGlc1BJo6DWQCgnK125xVK" + + "lWLpt6ZJJ7TXcx3nkm6wAgAAnQFXBEAnoe0QBACsQxPvaeBcv2TkbgU/5Wc/" + + "tO222dPE1mxFbXjGTKfb+6ge96iyD8kTRLrKCkEEeVBa8AZqMSoXUVN6tV8j" + + "/zD8Bc76o5iJ6wgpg3Mmy2GxInVfsfZN6/G3Y2ukmouz+CDNvQdUw8cTguIb" + + "QoV3XhQ03MLbfVmNcHsku9F4CuKNWwADBQP0DSSe8v5PXF9CSCXOIxBDcQ5x" + + "RKjyYOveqoH/4lbOV0YNUbIDZq4RaUdotpADuPREFmWf0zTB6KV/WIiag8XU" + + "WU9zdDvLKR483Bo6Do5pDBcN+NqfQ+ntGY9WJ7BSFnhQ3+07i1K+NsfFTRfv" + + "hf9X3MP75rCf7MxAIWHTabEmUf4DAwLeNsQ2XGJVRmA8DssBUCghogG9n8T3" + + "qfBeKsplGyCcF+JjPeQXkKQaoYGJ0aJz36qFP9d8DuWtT9soQcqIxVf6mTa8" + + "kN1594hGBBgRAgAGBQJAJ6HtAAoJEDmcGkJLrdrPpMkAnRyjQSKugz0YJqOB" + + "yGasMLQLxd2OAKCEIlhtCarlufVQNGZsuWxHVbU8crACAAA="); + + byte[] sig1 = + Base64.decode( + "owGbwMvMwCR4VvnryyOnTJwZ10gncZSkFpfolVSU2Ltz78hIzcnJVyjPL8pJUeTq" + + "sGdmZQCJwpQLMq3ayTA/0Fj3xf4jbwPfK/H3zj55Z9L1n2k/GOapKJrvMZ4tLiCW" + + "GtP/XeDqX4fORDUA"); + + byte[] sig1crc = Base64.decode("OZa/"); + + byte[] testPubWithUserAttr = + Base64.decode( + "mQGiBD2Rqv0RBADqKCkhVEtB/lEEr/9CubuHEy2oN/yU5j+2GXSdcNdVnRI/rwFy" + + "fHEQIk3uU7zHSUKFrC59yDm0sODYyjEdE3BVb0xvEJ5LE/OdndcIMXT1DungZ1vB" + + "zIK/3lr33W/PHixYxv9jduH3WrTehBpiKkgMZp8XloSFj2Cnw9LDyfqB7QCg/8K1" + + "o2k75NkOd9ZjnA9ye7Ri3bEEAKyr61Mo7viPWBK1joWAEsxG0OBWM+iSlG7kwh31" + + "8efgC/7Os6x4Y0jzs8mpcbBjeZtZjS9lRbfp7RinhF269xL0TZ3JxIdtaAV/6yDQ" + + "9NXfZY9dskN++HIR/5GCEEgq/qTJZt6ti5k7aV19ZFfO6wiK3NUy08wOrVsdOkVE" + + "w9IcBADaplhpcel3201uU3OCboogJtw81R5MJMZ4Y9cKL/ca2jGISn0nA7KrAw9v" + + "ShheSixGO4BV9JECkLEbtg7i+W/j/De6S+x2GLNcphuTP3UmgtKbhs0ItRqzW561" + + "s6gLkqi6aWmgaFLd8E1pMJcd9DSY95P13EYB9VJIUxFNUopzo7QcUmFsZiBIYXVz" + + "ZXIgPGhhdXNlckBhY20ub3JnPokAWAQQEQIAGAUCPZGq/QgLAwkIBwIBCgIZAQUb" + + "AwAAAAAKCRAqIBiOh4JvOKg4AJ9j14yygOqqzqiLKeaasIzqT8LCIgCggx14WuLO" + + "wOUTUswTaVKMFnU7tseJAJwEEAECAAYFAj2Rqx8ACgkQ9aWTKMpUDFV+9QP/RiWT" + + "5FAF5Rgb7beaApsgXsME+Pw7HEYFtqGa6VcXEpbcUXO6rjaXsgMgY90klWlWCF1T" + + "HOyKITvj2FdhE+0j8NQn4vaGpiTwORW/zMf/BZ0abdSWQybp10Yjs8gXw30UheO+" + + "F1E524MC+s2AeUi2hwHMiS+AVYd4WhxWHmWuBpTRypP/AAALTgEQAAEBAAAAAQAA" + + "AAABAAAA/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQ" + + "Dg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/" + + "2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7" + + "Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozv/wAARCABqAF0DASIAAhEBAxEB/8QAHwAAAQUB" + + "AQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQID" + + "AAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0" + + "NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKT" + + "lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl" + + "5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL" + + "/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB" + + "CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpj" + + "ZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3" + + "uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIR" + + "AxEAPwD2aiiq9xcxWsRllcKqjOT06E/0oAsVm6jrmm6VGXvLuOPGflz8x+grzXxV" + + "8U51u5LXRgBGowZHXknnkc9OQcV51caneXdw9xPOXlckl2AJHY4J6cD1oA9J1z4p" + + "TRkrYQhRyQ0hIY5/2QRx7k9ulczN8SvEEshdZkX0UorDrznI759a5Mksckkknqec" + + "mkoA7WD4oavEoEttbTepYEZ+mCMVv6H8SLTULhbe/gFozAYkD5Unp3Ax/kV5XRQB" + + "9EAhgCDkHkEcgilryTwd4zn0m4WzvpTJZSMBuY5MfbueletKyugZWDKwyCOc/j3o" + + "AduyWLDeWB5Ynj8jSUUUAdFXn/xU15dO0RbGGYC5uWwUB6L1Jx+n413F1cJa2stz" + + "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" + + "7DGrzw=="); + + byte[] aesSecretKey = Base64.decode( + "lQHpBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyv4JAwKyWlhdblPudWBFXNkW5ydKn0AV2f51wEtj" + + "Zy0aLIeutVMSJf1ytLqjFqrnFe6pdJrHO3G00TE8OuFhftWosLGLbEGytDtF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gQUVTMjU2KSA8ZXJpY0Bib3Vu" + + "Y3ljYXN0bGUub3JnPohZBBMRAgAZBQJAUnSGBAsHAwIDFQIDAxYCAQIeAQIX" + + "gAAKCRBYt1NnUiCgeFKaAKCiqtOO+NQES1gJW6XuOGmSkXt8bQCfcuW7SXZH" + + "zxK1FfdcG2HEDs3YEVawAgAA"); + + byte[] aesPublicKey = Base64.decode( + "mQGiBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyrQ7RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IEFFUzI1NikgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ0" + + "hgQLBwMCAxUCAwMWAgECHgECF4AACgkQWLdTZ1IgoHhSmgCfU83BLBF2nCua" + + "zk2dXB9zO1l6XS8AnA07U4cq5W0GrKM6/kP9HWtPhgOFsAIAAA=="); + + byte[] twofishSecretKey = Base64.decode( + "lQHpBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV/4KAwLcGrxT3X0qR2CZyZYSVBdjXeNYKXuGBtOf" + + "ood26WOtwLw4+l9sHVoiXNv0LomkO58ndJRPGCeZWZEDMVrfkS7rcOlktDxF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gdHdvZmlzaCkgPGVyaWNAYm91" + + "bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ20gQLBwMCAxUCAwMWAgECHgEC" + + "F4AACgkQaCCMaHh9zR2+RQCghcQwlt4B4YmNxp2b3v6rP3E8M0kAn2Gspi4u" + + "A/ynoqnC1O8HNlbjPdlVsAIAAA=="); + + byte[] twofishPublicKey = Base64.decode( + "mQGiBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV7Q8RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IHR3b2Zpc2gpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkFAkBS" + + "dtIECwcDAgMVAgMDFgIBAh4BAheAAAoJEGggjGh4fc0dvkUAn2QGdNk8Wrrd" + + "+DvKECrO5+yoPRx3AJ91DhCMme6uMrQorKSDYxHlgc7iT7ACAAA="); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Generated signature test + * + * @param sKey + * @param pgpPrivKey + */ + public void generateTest( + PGPSecretKeyRing sKey, + PGPPublicKey pgpPubKey, + PGPPrivateKey pgpPrivKey) + throws Exception + { + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + Iterator it = sKey.getSecretKey().getPublicKey().getUserIDs(); + String primaryUserID = (String)it.next(); + + spGen.setSignerUserID(true, primaryUserID); + + sGen.setHashedSubpackets(spGen.generate()); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + PGPObjectFactory pgpFact = new PGPObjectFactory(bOut.toByteArray()); + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + } + + public void performTest() + throws Exception + { + String file = null; + KeyFactory fact = KeyFactory.getInstance("DSA", "SC"); + PGPPublicKey pubKey = null; + PrivateKey privKey = null; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // test signature message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + int ch; + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + // + // signature generation + // + generateTest(sKey, pubKey, pgpPrivKey); + + // + // signature generation - canonical text + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.TEXT, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature - canconical text + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // Read the public key with user attributes + // + pgpPub = new PGPPublicKeyRing(testPubWithUserAttr, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + sigs.next(); + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("Failed user attributes check"); + } + + byte[] pgpPubBytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(pgpPubBytes, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + it = pubKey.getUserAttributes(); + count = 0; + while (it.hasNext()) + { + it.next(); + count++; + } + + if (count != 1) + { + fail("Failed user attributes reread"); + } + + // + // reading test extra data - key with edge condition for DSA key password. + // + char [] passPhrase = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + + sKey = new PGPSecretKeyRing(testPrivKey2, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(passPhrase, "SC"); + + byte[] bytes = pgpPrivKey.getKey().getEncoded(); + + // + // reading test - aes256 encrypted passphrase. + // + sKey = new PGPSecretKeyRing(aesSecretKey, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + bytes = pgpPrivKey.getKey().getEncoded(); + + // + // reading test - twofish encrypted passphrase. + // + sKey = new PGPSecretKeyRing(twofishSecretKey, new BcKeyFingerprintCalculator()); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + bytes = pgpPrivKey.getKey().getEncoded(); + + // + // use of PGPKeyPair + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", "SC"); + + kpg.initialize(512); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.DSA , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + } + + public String getName() + { + return "BcPGPDSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java new file mode 100644 index 000000000..92d89a227 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java @@ -0,0 +1,2348 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; + +public class BcPGPKeyRingTest + extends SimpleTest +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + char[] sec5pass1 = "12345678".toCharArray(); + + // + // Werner Koch "odd keys" + // + byte[] pub6 = Base64.decode( + "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" + + "L4Kb7TWkd/OHQScVBO8sTUz0+2g="); + + byte[] pub6check = Base64.decode("62O9"); + + // + // revoked sub key + // + byte[] pub7 = Base64.decode( + "mQGiBFKQDEMRBACtcEzu15gGDrZKLuO2zgDJ9qFkweOxKyeO45LKIfUGBful" + + "lheoFHbsJIeNGjWbSOfWWtphTaSu9//BJt4xxg2pqVLYqzR+hEPpDy9kXxnZ" + + "LwwxjAP2TcOvuZKWe+JzoYQxDunOH4Zu9CPJhZhF3RNPw+tbv0jHfTV/chtb" + + "23Dj5wCg7eoM8bL9NYXacsAfkS//m+AB1MkD/jEZJqJSQHW8WVP7wKRrAZse" + + "N4l9b8+yY4RwLIodhD8wGsMYjkCF4yb/SQ5QlmLlvrHDLBofRzG+8oxldX4o" + + "GLZWvqPmW+BlS4QNSr+ZBu+OwnpClXG2pR+ExumXNoeArREyylrmOgD+0cUa" + + "8K2UbOxbJ8EioyOKxa7wjUVxmHmhBACAGQGLT/lpHA5zcU0g8AlSk8fsd+bB" + + "nwa/+9xdLqVsCTZdOWULtPOw9hbAdjjAy0L4M/MDAJYYtCEl9rB7aOc9PVdT" + + "h7CT9Ma6ltiSMKDlqWbDmogNEGx9Gz3GjiSGxAy/SN6JR1On4c60TAiTv6eE" + + "uEHszE6CH4qceK5W8HLLB7QncmV2b2tlIChSZXZva2UgVGVzdCkgPHJldm9r" + + "ZUB0ZXN0LnRlc3Q+iGIEExECACIFAlKQDEMCGwMGCwkIBwMCBhUIAgkKCwQW" + + "AgMBAh4BAheAAAoJEBCIvJhXZzdIrDQAn2S5/G+eitU6/pr5Yz4j9s0/6aMt" + + "AKC08q7BPJ5lTaRJ5zV8llSywMvWEbACAAO5AQ0EUpAMQxAEAKu4nnga6FRp" + + "eCobO78ewKuAZACfzo9lbWo8JfbwT2xrISZU6DNIMD85PlzTk/Q9UuEw0SC5" + + "KdQYLbj0yll88r/0tUoxcBNkvMQHqUVfVgl1+utv0qtDmR0OE5wVebUYgYHA" + + "vONSZdhFU8f5OxPhAW8Ol8gA1Bl8orhRXkEnMlXnAAMFA/97Dvl3LXHnwpak" + + "+p94fU5WWf9SLp4QPLIhKJzXjv4Uh9UO4u1ajEwUTRk+Djv6sRCuFYL3qLNp" + + "Io9b3vLluRbPk8YIwKGctyD7cz3XH9AIbM2HNUyJWljlWEEMU/7uKI5ophGI" + + "3/Huhqx/bjzY3LzWiLKQ5lSbwUJRCdGYnMiVuIhJBBgRAgAJBQJSkAxDAhsM" + + "AAoJEBCIvJhXZzdIvTgAn1Vx4PUO1wQNpY8PMU+Cl7dl+JeJAJ97lrNiXbom" + + "kdIm80plEuLQjweyELACAAM="); + + byte[] pub7revoke = Base64.decode("iEkEIBECAAkFAlKQDQ4CHQIACgkQEIi8mFdnN0hfzACfSpQ/+OoC48Rf2DZcKvmM" + + "3dEq8qMAoOnHg0/s/X/Is3bJwUiDEpnWmUoI"); + + byte[] pub8 = Base64.decode( + "mQGiBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ7ABh7QhSmlhIFlp" + + "eXUgPHl5amlhQG5vd21lZGlhdGVjaC5jb20+sAMD//+JAF0EEBECAB0FAkEcraYH" + + "CwkIBwMCCgIZAQUbAwAAAAUeAQAAAAAKCRD0/lb4K/9iFJlhAKCRMifQewiX5o8F" + + "U099FG3QnLVUZgCfWpMOsHulGHfNrxdBSkE5Urqh1ymwAWe5Ag0EQRytphAIAPZC" + + "V7cIfwgXcqK61qlC8wXo+VMROU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdM" + + "ZIZJ+AyDvWXpF9Sh01D49Vlf3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHO" + + "fMlm/xX5u/2RXscBqtNbno2gpXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNs" + + "OA1FHQ98iLMcfFstjvbzySPAQ/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq" + + "/zzhsSlAGBGNfISnCnLWhsQDGcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2J" + + "SyIZJrqrol7DVekyCzsAAgIH/3K2wKRSzkIpDfZR25+tnQ8brv3TYoDZo3/wN3F/" + + "r6PGjx0150Q8g8EAC0bqm4rXWzOqdSxYxvIPOAGm5P4y+884yS6j3vKcXitT7vj+" + + "ODc2pVwGDLDjrMRrosSK89ycPCK6R/5pD7Rv4l9DWi2fgLvXqJHS2/ujUf2uda9q" + + "i9xNMnBXIietR82Sih4undFUOwh6Mws/o3eed9DIdaqv2Y2Aw43z/rJ6cjSGV3C7" + + "Rkf9x85AajYA3LwpS8d99tgFig2u6V/A16oi6/M51oT0aR/ZAk50qUc4WBk9uRUX" + + "L3Y+P6v6FCBE/06fgVltwcQHO1oKYKhH532tDL+9mW5/dYGwAYeJAEwEGBECAAwF" + + "AkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg+JW8m5nF3R/oZGuG87bXQBszkjMA" + + "oLhGPncuGKowJXMRVc70/8qwXQJLsAFnmQGiBD2K5rYRBADD6kznWZA9nH/pMlk0" + + "bsG4nI3ELgyI7KpgRSS+Dr17+CCNExxCetT+fRFpiEvUcSxeW4pOe55h0bQWSqLo" + + "MNErXVJEXrm1VPkC08W8D/gZuPIsdtKJu4nowvdoA+WrI473pbeONGjaEDbuIJak" + + "yeKM1VMSGhsImdKtxqhndq2/6QCg/xARUIzPRvKr2TJ52K393895X1kEAMCdjSs+" + + "vABnhaeNNR5+NNkkIOCCjCS8qZRZ4ZnIayvn9ueG3KrhZeBIHoajUHrlTXBVj7XO" + + "wXVfGpW17jCDiqhU8Pu6VwEwX1iFbuUwqBffiRLXKg0zfcN+MyFKToi+VsJi4jiZ" + + "zcwUFMb8jE8tvR/muXti7zKPRPCbNBExoCt4A/0TgkzAosG/W4dUkkbc6XoHrjob" + + "iYuy6Xbs/JYlV0vf2CyuKCZC6UoznO5x2GkvOyVtAgyG4HSh1WybdrutZ8k0ysks" + + "mOthE7n7iczdj9Uwg2h+TfgDUnxcCAwxnOsX5UaBqGdkX1PjCWs+O3ZhUDg6UsZc" + + "7O5a3kstf16lHpf4q7ABAIkAYQQfEQIAIQUCPYrmtgIHABcMgBHRi/xlIgI+Q6LT" + + "kNJ7zKvTd87NHAAKCRDJM3gHb/sRj7bxAJ9f6mdlXQH7gMaYiY5tBe/FRtPr1gCf" + + "UhDJQG0ARvORFWHjwhhBMLxW7j2wAWC0KkRlc21vbmQgS2VlIDxkZXNtb25kLmtl" + + "ZUBub3dtZWRpYXRlY2guY29tPrADAQD9iQBYBBARAgAYBQI9iua2CAsDCQgHAgEK" + + "AhkBBRsDAAAAAAoJEMkzeAdv+xGP7v4An19iqadBCCgDIe2DTpspOMidwQYPAJ4/" + + "5QXbcn4ClhOKTO3ZEZefQvvL27ABYLkCDQQ9iua2EAgA9kJXtwh/CBdyorrWqULz" + + "Bej5UxE5T7bxbrlLOCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHT" + + "UPj1WV/cdlJPPT2N286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq" + + "01uejaClcjrUGvC/RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O" + + "9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcK" + + "ctaGxAMZyAcpesqVDNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TIL" + + "OwACAgf/SO+bbg+owbFKVN5HgOjOElQZVnCsegwCLqTeQzPPzsWmkGX2qZJPDIRN" + + "RZfJzti6+oLJwaRA/3krjviUty4VKhZ3lKg8fd9U0jEdnw+ePA7yJ6gZmBHL15U5" + + "OKH4Zo+OVgDhO0c+oetFpend+eKcvtoUcRoQoi8VqzYUNG0b/nmZGDlxQe1/ZNbP" + + "HpNf1BAtJXivCEKMD6PVzsLPg2L4tFIvD9faeeuKYQ4jcWtTkBLuIaZba3i3a4wG" + + "xTN20j9HpISVuLW/EfZAK1ef4DNjLmHEU9dMzDqfi+hPmMbGlFqcKr+VjcYIDuje" + + "o+92xm/EWAmlti88r2hZ3MySamHDrLABAIkATAQYEQIADAUCPYrmtgUbDAAAAAAK" + + "CRDJM3gHb/sRjzVTAKDVS+OJLMeS9VLAmT8atVCB42MwIQCgoh1j3ccWnhc/h6B7" + + "9Uqz3fUvGoewAWA="); + + byte[] sec8 = Base64.decode( + "lQHpBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ/4JAwLXyWhb4pf4" + + "nmCmD0lDwoYvatLiR7UQVM2MamxClIiT0lCPN9C2AYIFgRWAJNS215Tjx7P/dh7e" + + "8sYfh5XEHErT3dMbsAGHtCFKaWEgWWl5dSA8eXlqaWFAbm93bWVkaWF0ZWNoLmNv" + + "bT6wAwP//4kAXQQQEQIAHQUCQRytpgcLCQgHAwIKAhkBBRsDAAAABR4BAAAAAAoJ" + + "EPT+Vvgr/2IUmWEAoJEyJ9B7CJfmjwVTT30UbdCctVRmAJ9akw6we6UYd82vF0FK" + + "QTlSuqHXKbABZ50CawRBHK2mEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlL" + + "OCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N" + + "286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/" + + "RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2O" + + "u1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqV" + + "DNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/crbApFLO" + + "QikN9lHbn62dDxuu/dNigNmjf/A3cX+vo8aPHTXnRDyDwQALRuqbitdbM6p1LFjG" + + "8g84Aabk/jL7zzjJLqPe8pxeK1Pu+P44NzalXAYMsOOsxGuixIrz3Jw8IrpH/mkP" + + "tG/iX0NaLZ+Au9eokdLb+6NR/a51r2qL3E0ycFciJ61HzZKKHi6d0VQ7CHozCz+j" + + "d5530Mh1qq/ZjYDDjfP+snpyNIZXcLtGR/3HzkBqNgDcvClLx3322AWKDa7pX8DX" + + "qiLr8znWhPRpH9kCTnSpRzhYGT25FRcvdj4/q/oUIET/Tp+BWW3BxAc7WgpgqEfn" + + "fa0Mv72Zbn91gf4JAwITijME9IlFBGAwH6YmBtWIlnDiRbsq/Pxozuhbnes831il" + + "KmdpUKXkiIfHY0MqrEWl3Dfn6PMJGTnhgqXMrDxx3uHrq0Jl2swRnAWIIO8gID7j" + + "uPetUqEviPiwAYeJAEwEGBECAAwFAkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg" + + "+JW8m5nF3R/oZGuG87bXQBszkjMAoLhGPncuGKowJXMRVc70/8qwXQJLsAFn"); + + char[] sec8pass = "qwertyui".toCharArray(); + + byte[] sec9 = Base64.decode( + "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" + + "6neEEX/i1Q=="); + + public char[] sec9pass = "foo".toCharArray(); + + // version 4 keys with expiry dates + byte[] pub10 = Base64.decode( + "mQGiBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHLQgdGVzdCBrZXkg" + + "KHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUCQqqJrQIbAwUJACTqAAYL" + + "CQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzLAJ42AeCRIBBjv8r8qw9y" + + "laNj2GZ1sACgiWYHVXMA6B1H9I1kS3YsCd3Oq7qwAgAAuM0EQqqJrhADAKWkix8l" + + "pJN7MMTXob4xFF1TvGll0UD1bDGOMMbes6aeXSbT9QXee/fH3GnijLY7wB+qTPv9" + + "ohubrSpnv3yen3CEBW6Q2YK+NlCskma42Py8YMV2idmYjtJi1ckvHFWt5wADBQL/" + + "fkB5Q5xSGgspMaTZmtmX3zG7ZDeZ0avP8e8mRL8UszCTpqs6vMZrXwyQLZPbtMYv" + + "PQpuRGEeKj0ysimwYRA5rrLQjnRER3nyuuEUUgc4j+aeRxPf9WVsJ/a1FCHtaAP1" + + "iE8EGBECAA8FAkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCfd66H7DL7kFGd" + + "IoS+NIp8JO+noxAAn25si4QAF7og8+4T5YQUuhIhx/NesAIAAA=="); + + byte[] sec10 = Base64.decode( + "lQHhBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHP4DAwLssmOjVC+d" + + "mWB783Lpzjb9evKzsxisTdx8/jHpUSS+r//6/Guyx3aA/zUw5bbftItW57mhuNNb" + + "JTu7WrQgdGVzdCBrZXkgKHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUC" + + "QqqJrQIbAwUJACTqAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzL" + + "AJ0cYPwKeoSReY14LqJtAjnkX7URHACgsRZWfpbalrSyDnq3TtZeGPUqGX+wAgAA" + + "nQEUBEKqia4QAwClpIsfJaSTezDE16G+MRRdU7xpZdFA9WwxjjDG3rOmnl0m0/UF" + + "3nv3x9xp4oy2O8Afqkz7/aIbm60qZ798np9whAVukNmCvjZQrJJmuNj8vGDFdonZ" + + "mI7SYtXJLxxVrecAAwUC/35AeUOcUhoLKTGk2ZrZl98xu2Q3mdGrz/HvJkS/FLMw" + + "k6arOrzGa18MkC2T27TGLz0KbkRhHio9MrIpsGEQOa6y0I50REd58rrhFFIHOI/m" + + "nkcT3/VlbCf2tRQh7WgD9f4DAwLssmOjVC+dmWDXVLRopzxbBGOvodp/LZoSDb56" + + "gNJjDMJ1aXqWW9qTAg1CFjBq73J3oFpVzInXZ8+Q8inxv7bnWiHbiE8EGBECAA8F" + + "AkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCgl2jw5hfk/JsyjulQqe1Nps1q" + + "Lx0AoMdnFMZmTMLHn8scUW2j9XO312tmsAIAAA=="); + + public char[] sec10pass = "test".toCharArray(); + + public byte[] subKeyBindingKey = Base64.decode( + "mQGiBDWagYwRBAD7UcH4TAIp7tmUoHBNxVxCVz2ZrNo79M6fV63riOiH2uDxfIpr" + + "IrL0cM4ehEKoqlhngjDhX60eJrOw1nC5BpYZRnDnyDYT4wTWRguxObzGq9pqA1dM" + + "oPTJhkFZVIBgFY99/ULRqaUYIhFGgBtnwS70J8/L/PGVc3DmWRLMkTDjSQCg/5Nh" + + "MCjMK++MdYMcMl/ziaKRT6EEAOtw6PnU9afdohbpx9CK4UvCCEagfbnUtkSCQKSk" + + "6cUp6VsqyzY0pai/BwJ3h4apFMMMpVrtBAtchVgqo4xTr0Sve2j0k+ase6FSImiB" + + "g+AR7hvTUTcBjwtIExBc8TuCTqmn4GG8F7UMdl5Z0AZYj/FfAQYaRVZYP/pRVFNx" + + "Lw65BAC/Fi3qgiGCJFvXnHIckTfcAmZnKSEXWY9NJ4YQb4+/nH7Vsw0wR/ZObUHR" + + "bWgTc9Vw1uZIMe0XVj6Yk1dhGRehUnrm3mE7UJxu7pgkBCbFECFSlSSqP4MEJwZV" + + "09YP/msu50kjoxyoTpt+16uX/8B4at24GF1aTHBxwDLd8X0QWrQsTWVycmlsbCBM" + + "eW5jaCBDTEVBUiBzeXN0ZW0gREggPGNsZWFyQG1sLmNvbT6JAEsEEBECAAsFAjWa" + + "gYwECwMBAgAKCRDyAGjiP47/XanfAKCs6BPURWVQlGh635VgL+pdkUVNUwCdFcNa" + + "1isw+eAcopXPMj6ACOapepu5Ag0ENZqBlBAIAPZCV7cIfwgXcqK61qlC8wXo+VMR" + + "OU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf" + + "3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2g" + + "pXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPA" + + "Q/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQD" + + "GcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH" + + "/RYtVo+HROZ6jrNjrATEwQm1fUQrk6n5+2dniN881lF0CNkB4NkHw1Xxz4Ejnu/0" + + "iLg8fkOAsmanOsKpOkRtqUnVpsVL5mLJpFEyCY5jbcfj+KY9/25bs0ga7kLHNZia" + + "zbCxJdF+W179z3nudQxRaXG/0XISIH7ziZbSVni69sKc1osk1+OoOMbSuZ86z535" + + "Pln4fXclkFE927HxfbWoO+60hkOLKh7x+8fC82b3x9vCETujEaxrscO2xS7/MYXP" + + "8t1ffriTDmhuIuQS2q4fLgeWdqrODrMhrD8Dq7e558gzp30ZCqpiS7EmKGczL7B8" + + "gXxbBCVSTxYMJheXt2xMXsuJAD8DBRg1moGU8gBo4j+O/10RAgWdAKCPhaFIXuC8" + + "/cdiNMxTDw9ug3De5QCfYXmDzRSFUu/nrCi8yz/l09wsnxo="); + + public byte[] subKeyBindingCheckSum = Base64.decode("3HU+"); + + // + // PGP8 with SHA1 checksum. + // + public byte[] rewrapKey = Base64.decode( + "lQOWBEUPOQgBCADdjPTtl8oOwqJFA5WU8p7oDK5KRWfmXeXUZr+ZJipemY5RSvAM" + + "rxqsM47LKYbmXOJznXCQ8+PPa+VxXAsI1CXFHIFqrXSwvB/DUmb4Ec9EuvNd18Zl" + + "hJAybzmV2KMkaUp9oG/DUvxZJqkpUddNfwqZu0KKKZWF5gwW5Oy05VCpaJxQVXFS" + + "whdbRfwEENJiNx4RB3OlWhIjY2p+TgZfgQjiGB9i15R+37sV7TqzBUZF4WWcnIRQ" + + "DnpUfxHgxQ0wO/h/aooyRHSpIx5i4oNpMYq9FNIyakEx/Bomdbs5hW9dFxhrE8Es" + + "UViAYITgTsyROxmgGatGG09dcmVDJVYF4i7JAAYpAAf/VnVyUDs8HrxYTOIt4rYY" + + "jIHToBsV0IiLpA8fEA7k078L1MwSwERVVe6oHVTjeR4A9OxE52Vroh2eOLnF3ftf" + + "6QThVVZr+gr5qeG3yvQ36N7PXNEVOlkyBzGmFQNe4oCA+NR2iqnAIspnekVmwJV6" + + "xVvPCjWw/A7ZArDARpfthspwNcJAp4SWfoa2eKzvUTznTyqFu2PSS5fwQZUgOB0P" + + "Y2FNaKeqV8vEZu4SUWwLOqXBQIZXiaLvdKNgwFvUe3kSHdCNsrVzW7SYxFwaEog2" + + "o6YLKPVPqjlGX1cMOponGp+7n9nDYkQjtEsGSSMQkQRDAcBdSVJmLO07kFOQSOhL" + + "WQQA49BcgTZyhyH6TnDBMBHsGCYj43FnBigypGT9FrQHoWybfX47yZaZFROAaaMa" + + "U6man50YcYZPwzDzXHrK2MoGALY+DzB3mGeXVB45D/KYtlMHPLgntV9T5b14Scbc" + + "w1ES2OUtsSIUs0zelkoXqjLuKnSIYK3mMb67Au7AEp6LXM8EAPj2NypvC86VEnn+" + + "FH0QHvUwBpmDw0EZe25xQs0brvAG00uIbiZnTH66qsIfRhXV/gbKK9J5DTGIqQ15" + + "DuPpz7lcxg/n2+SmjQLNfXCnG8hmtBjhTe+udXAUrmIcfafXyu68SAtebgm1ga56" + + "zUfqsgN3FFuMUffLl3myjyGsg5DnA/oCFWL4WCNClOgL6A5VkNIUait8QtSdCACT" + + "Y7jdSOguSNXfln0QT5lTv+q1AjU7zjRl/LsFNmIJ5g2qdDyK937FOXM44FEEjZty" + + "/4P2dzYpThUI4QUohIj8Qi9f2pZQueC5ztH6rpqANv9geZKcciAeAbZ8Md0K2TEU" + + "RD3Lh+RSBzILtBtUZXN0IEtleSA8dGVzdEBleGFtcGxlLmNvbT6JATYEEwECACAF" + + "AkUPOQgCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDYpknHeQaskD9NB/9W" + + "EbFuLaqZAl3yjLU5+vb75BdvcfL1lUs44LZVwobNp3/0XbZdY76xVPNZURtU4u3L" + + "sJfGlaF+EqZDE0Mqc+vs5SIb0OnCzNJ00KaUFraUtkByRV32T5ECHK0gMBjCs5RT" + + "I0vVv+Qmzl4+X1Y2bJ2mlpBejHIrOzrBD5NTJimTAzyfnNfipmbqL8p/cxXKKzS+" + + "OM++ZFNACj6lRM1W9GioXnivBRC88gFSQ4/GXc8yjcrMlKA27JxV+SZ9kRWwKH2f" + + "6o6mojUQxnHr+ZFKUpo6ocvTgBDlC57d8IpwJeZ2TvqD6EdA8rZ0YriVjxGMDrX1" + + "8esfw+iLchfEwXtBIRwS"); + + char[] rewrapPass = "voltage123".toCharArray(); + + byte[] pubWithX509 = Base64.decode( + "mQENBERabjABCACtmfyo6Nph9MQjv4nmCWjZrRYnhXbivomAdIwYkLZUj1bjqE+j"+ + "uaLzjZV8xSI59odZvrmOiqlzOc4txitQ1OX7nRgbOJ7qku0dvwjtIn46+HQ+cAFn"+ + "2mTi81RyXEpO2uiZXfsNTxUtMi+ZuFLufiMc2kdk27GZYWEuasdAPOaPJnA+wW6i"+ + "ZHlt0NfXIGNz864gRwhD07fmBIr1dMFfATWxCbgMd/rH7Z/j4rvceHD2n9yrhPze"+ + "YN7W4Nuhsr2w/Ft5Cm9xO7vXT/cpto45uxn8f7jERep6bnUwNOhH8G+6xLQgTLD0"+ + "qFBGVSIneK3lobs6+xn6VaGN8W0tH3UOaxA1ABEBAAG0D0NOPXFhLWRlZXBzaWdo"+ + "dIkFDgQQZAIFAQUCRFpuMAUDCWdU0gMF/3gCGwPELGQBAQQwggTkMIIDzKADAgEC"+ + "AhBVUMV/M6rIiE+IzmnPheQWMA0GCSqGSIb3DQEBBQUAMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDAeFw0wNjA1MDQyMTEyMTZaFw0xMTA1MDQyMTIwMDJaMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2Z/Kjo2mH0xCO/ieYJ"+ + "aNmtFieFduK+iYB0jBiQtlSPVuOoT6O5ovONlXzFIjn2h1m+uY6KqXM5zi3GK1DU"+ + "5fudGBs4nuqS7R2/CO0ifjr4dD5wAWfaZOLzVHJcSk7a6Jld+w1PFS0yL5m4Uu5+"+ + "IxzaR2TbsZlhYS5qx0A85o8mcD7BbqJkeW3Q19cgY3PzriBHCEPTt+YEivV0wV8B"+ + "NbEJuAx3+sftn+Piu9x4cPaf3KuE/N5g3tbg26GyvbD8W3kKb3E7u9dP9ym2jjm7"+ + "Gfx/uMRF6npudTA06Efwb7rEtCBMsPSoUEZVIid4reWhuzr7GfpVoY3xbS0fdQ5r"+ + "EDUCAwEAAaOCAXwwggF4MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G"+ + "A1UdDgQWBBSmFTRv5y65DHtTYae48zl0ExNWZzCCASUGA1UdHwSCARwwggEYMIIB"+ + "FKCCARCgggEMhoHFbGRhcDovLy9DTj1xYS1kZWVwc2lnaHQsQ049cWEtd3VtYW4x"+ + "LWRjLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNl"+ + "cyxDTj1Db25maWd1cmF0aW9uLERDPVdlYmZlLERDPXRtczAxLERDPXFhLERDPWNv"+ + "bT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM"+ + "RGlzdHJpYnV0aW9uUG9pbnSGQmh0dHA6Ly9xYS13dW1hbjEtZGMud2ViZmUudG1z"+ + "MDEucWEuY29tL0NlcnRFbnJvbGwvcWEtZGVlcHNpZ2h0LmNybDAQBgkrBgEEAYI3"+ + "FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAfuZCW3XlB7Eok35zQbvYt9rhAndT"+ + "DNw3wPNI4ZzD1nXoYWnwhNNvWRpsOt4ExOSNdaHErfgDXAMyyg66Sro0TkAx8eAj"+ + "fPQsyRAh0nm0glzFmJN6TdOZbj7hqGZjc4opQ6nZo8h/ULnaEwMIUW4gcSkZt0ww"+ + "CuErl5NUrN3DpkREeCG/fVvQZ8ays3ibQ5ZCZnYBkLYq/i0r3NLW34WfYhjDY48J"+ + "oQWtvFSAxvRfz2NGmqnrCHPQZxqlfdta97kDa4VQ0zSeBaC70gZkLmD1GJMxWoXW"+ + "6tmEcgPY5SghInUf+L2u52V55MjyAFzVp7kTK2KY+p7qw35vzckrWkwu8AAAAAAA"+ + "AQE="); + + private static byte[] secWithPersonalCertificate = Base64.decode( + "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" + + "cGdwbWltZQULBwgJAgIZAQUbAQAAAAUeAQAAAAIVAgAKCRDlTa3BE84gWVKW" + + "BACcoCFKvph9r9QiHT1Z3N4wZH36Uxqu/059EFALnBkEdVudX/p6S9mynGRk" + + "EfhmWFC1O6dMpnt+ZBEed/4XyFWVSLPwirML+6dxfXogdUsdFF1NCRHc3QGc" + + "txnNUT/zcZ9IRIQjUhp6RkIvJPHcyfTXKSbLviI+PxzHU2Padq8pV7ABZ7kA" + + "jQRIfg8tAQQAutJR/aRnfZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr" + + "5dg50wq3I4HOamRxUwHpdPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO" + + "8LUJ2VTbfPxoLFp539SQ0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0Ft" + + "JycAEQEAAbABj4kEzQQYAQIENwUCSMYtnAUJAeEzgMLFFAAAAAAAFwNleDUw" + + "OWNlcnRpZmljYXRlQHBncC5jb20wggNhMIICyqADAgECAgkA1AoCoRKJCgsw" + + "DQYJKoZIhvcNAQEFBQAwgakxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj" + + "aCBSZXB1YmxpYzESMBAGA1UEChQJQSYmTCBzb2Z0MSAwHgYDVQQLExdJbnRl" + + "cm5hbCBEZXZlbG9wbWVudCBDQTEqMCgGA1UEAxQhQSYmTCBzb2Z0IEludGVy" + + "bmFsIERldmVsb3BtZW50IENBMR8wHQYJKoZIhvcNAQkBFhBrYWRsZWNAYWxz" + + "b2Z0LmN6MB4XDTA4MDcxNjE1MDkzM1oXDTA5MDcxNjE1MDkzM1owaTELMAkG" + + "A1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMRIwEAYDVQQKFAlB" + + "JiZMIHNvZnQxFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5IeW5l" + + "ay1JbnRyYW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAutJR/aRn" + + "fZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr5dg50wq3I4HOamRxUwHp" + + "dPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO8LUJ2VTbfPxoLFp539SQ" + + "0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0FtJycCAwEAAaOBzzCBzDAJ" + + "BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD" + + "ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNaw7A6r10PtYZzAvr9CrSKeRYJgwHwYD" + + "VR0jBBgwFoAUmqSRM8rN3+T1+tkGiqef8S5suYgwGgYDVR0RBBMwEYEPaHlu" + + "ZWtAYWxzb2Z0LmN6MCgGA1UdHwQhMB8wHaAboBmGF2h0dHA6Ly9wZXRyazIv" + + "Y2EvY2EuY3JsMAsGA1UdDwQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQCUdOWd" + + "7mBLWj1/GSiYgfwgdTrgk/VZOJvMKBiiFyy1iFEzldz6Xx+mAexnFJKfZXZb" + + "EMEGWHfWPmgJzAtuTT0Jz6tUwDmeLH3MP4m8uOZtmyUJ2aq41kciV3rGxF0G" + + "BVlZ/bWTaOzHdm6cjylt6xxLt6MJzpPBA/9ZfybSBh1DaAUbDgAAAJ0gBBkB" + + "AgAGBQJIxi2bAAoJEAdYkEWLb2R2fJED/RK+JErZ98uGo3Z81cHkdP3rk8is" + + "DUL/PR3odBPFH2SIA5wrzklteLK/ZXmBUzcvxqHEgI1F7goXbsBgeTuGgZdx" + + "pINErxkNpcMl9FTldWKGiapKrhkZ+G8knDizF/Y7Lg6uGd2nKVxzutLXdHJZ" + + "pU89Q5nzq6aJFAZo5TBIcchQAAoJEOVNrcETziBZXvQD/1mvFqBfWqwXxoj3" + + "8fHUuFrE2pcp32y3ciO2i+uNVEkNDoaVVNw5eHQaXXWpllI/Pe6LnBl4vkyc" + + "n3pjONa4PKrePkEsCUhRbIySqXIHuNwZumDOlKzZHDpCUw72LaC6S6zwuoEf" + + "ucOcxTeGIUViANWXyTIKkHfo7HfigixJIL8nsAFn"); + + private static final byte[] umlautKeySig = Base64.decode( + "mI0ETdvOgQEEALoI2a39TRk1HReEB6DP9Bu3ShZUce+/Oeg9RIL9aUFuCsNdhu02" + + "REEHjO29Jz8daPgrnJDfFepNLD6iKKru2m9P30qnhsHMIAshO2Ozfh6wKwuHRqR3" + + "L4gBDu7cCB6SLwPoD8AYG0yQSM+Do10Td87RlStxCgxpMK6R3TsRkxcFABEBAAG0" + + "OlVNTEFVVFNUQVJUOsOEw6TDlsO2w5zDvMOfOlVNTEFURU5ERSA8YXNkbGFrc2Rs" + + "QGFrc2RqLmNvbT6IuAQTAQIAIgUCTdvOgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC" + + "HgECF4AACgkQP8kDwm8AOFiArAP/ZXrlZJB1jFEjyBb04ckpE6F/aJuSYIXf0Yx5" + + "T2eS+lA69vYuqKRC1qNROBrAn/WGNOQBFNEgGoy3F3gV5NgpIphnyIEZdZWGY2rv" + + "yjunKWlioZjWc/xbSbvpvJ3Q8RyfDXBOkDEB6uF1ksimw2eJSOUTkF9AQfS5f4rT" + + "5gs013G4jQRN286BAQQApVbjd8UhsQLB4TpeKn9+dDXAfikGgxDOb19XisjRiWxA" + + "+bKFxu5tRt6fxXl6BGSGT7DhoVbNkcJGVQFYcbR31UGKCVYcWSL3yfz+PiVuf1UB" + + "Rp44cXxxqxrLqKp1rk3dGvV4Ayy8lkk3ncDGPez6lIKvj3832yVtAzUOX1QOg9EA" + + "EQEAAYifBBgBAgAJBQJN286BAhsMAAoJED/JA8JvADhYQ80D/R3TX0FBMHs/xqEh" + + "tiS86XP/8pW6eMm2eaAYINxoDY3jmDMv2HFQ+YgrYXgqGr6eVGqDMNPj4W8VBoOt" + + "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + public void test1() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + Iterator sIt = pubKey.getSignatures(); + while (sIt.hasNext()) + { + ((PGPSignature)sIt.next()).getSignatureType(); + } + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + // + // exact match + // + rIt = pubRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = pubRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on partial match 1"); + } + + // + // partial match 0 expected + // + rIt = pubRings.getKeyRings("XXX", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of public keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = pubRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on case-insensitive partial match"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + pk.getSignatures(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + + // + // exact match + // + rIt = secretRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = secretRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on partial match 1"); + } + + // + // exact match 0 expected + // + rIt = secretRings.getKeyRings("test", false); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of secret keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = secretRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on case-insensitive partial match"); + } + } + + public void test2() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + + keyCount++; + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + if (pk.getKeyID() == -1413891222336124627L) + { + int sCount = 0; + Iterator sIt = pk.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + while (sIt.hasNext()) + { + int type = ((PGPSignature)sIt.next()).getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING) + { + fail("failed to return correct signature type"); + } + sCount++; + } + + if (sCount != 1) + { + fail("failed to find binding signature"); + } + } + + pk.getSignatures(); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass1)); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass2)); + } + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 2) + { + fail("wrong number of secret keyrings"); + } + } + + public void test3() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubK = (PGPPublicKey)it.next(); + + pubK.getSignatures(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test4() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test5() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + if (noIDEA()) + { + return; + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec5pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + private boolean noIDEA() + { + return true; + } + + public void test6() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub6); + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.getKeyID() == 0x5ce086b5b5a18ff4L) + { + int count = 0; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test6."); + } + } + } + } + + byte[] encRing = pubRings.getEncoded(); + } + + public void revocationTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(pub7, new BcKeyFingerprintCalculator()); + Iterator it = pgpPub.getPublicKeys(); + PGPPublicKey masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + } + + PGPSignature sig =((PGPSignatureList)new PGPObjectFactory(pub7revoke).nextObject()).get(0); + + sig.init(new BcPGPContentVerifierBuilderProvider(), masterKey); + + if (!sig.verifyCertification(masterKey)) + { + fail("failed to verify revocation certification"); + } + } + + public void test8() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub8); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec8); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec8pass)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test9() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec9); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + PGPPrivateKey pKey = k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec9pass)); + if (keyCount == 1 && pKey != null) + { + fail("primary secret key found, null expected"); + } + } + + if (keyCount != 3) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test10() + throws Exception + { + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new BcKeyFingerprintCalculator()); + Iterator secretKeys = secretRing.getSecretKeys(); + + while (secretKeys.hasNext()) + { + PGPPublicKey pubKey = ((PGPSecretKey)secretKeys.next()).getPublicKey(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on secret key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on secret key ring"); + } + } + + PGPPublicKeyRing publicRing = new PGPPublicKeyRing(pub10, new BcKeyFingerprintCalculator()); + Iterator publicKeys = publicRing.getPublicKeys(); + + while (publicKeys.hasNext()) + { + PGPPublicKey pubKey = (PGPPublicKey)publicKeys.next(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on public key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on public key ring"); + } + } + } + + public void generateTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void insertMasterTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + + rsaKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing1 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair2, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing2 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing2 = keyRingGen.generatePublicKeyRing(); + + try + { + PGPPublicKeyRing.insertPublicKey(pubRing1, pubRing2.getPublicKey()); + fail("adding second master key (public) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in public test"); + } + } + + try + { + PGPSecretKeyRing.insertSecretKey(secRing1, secRing2.getSecretKey()); + fail("adding second master key (secret) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in secret test"); + } + } + } + + public void generateSha1Test() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, true, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void test11() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(subKeyBindingKey, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + while (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + if (key.getValidSeconds() != 0) + { + fail("expiration time non-zero"); + } + } + } + + private void rewrapTest() + throws Exception + { + SecureRandom rand = new SecureRandom(); + + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + } + } + } + + private void testPublicKeyRingWithX509() + throws Exception + { + checkPublicKeyRingWithX509(pubWithX509); + + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(pubWithX509, new BcKeyFingerprintCalculator()); + + checkPublicKeyRingWithX509(pubRing.getEncoded()); + } + + private void testSecretKeyRingWithPersonalCertificate() + throws Exception + { + checkSecretKeyRingWithPersonalCertificate(secWithPersonalCertificate); + PGPSecretKeyRingCollection secRing = new PGPSecretKeyRingCollection(secWithPersonalCertificate); + checkSecretKeyRingWithPersonalCertificate(secRing.getEncoded()); + } + + private void testUmlaut() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(umlautKeySig, new BcKeyFingerprintCalculator()); + + PGPPublicKey pub = pubRing.getPublicKey(); + String userID = (String)pub.getUserIDs().next(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID test"); + } + } + } + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + char[] passPhrase = "passwd".toCharArray(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + userID, PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + + pub = pubRing1.getPublicKey(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID creation test"); + } + } + } + } + + private void checkSecretKeyRingWithPersonalCertificate(byte[] keyRing) + throws Exception + { + PGPSecretKeyRingCollection secCol = new PGPSecretKeyRingCollection(keyRing); + + + int count = 0; + + for (Iterator rIt = secCol.getKeyRings(); rIt.hasNext();) + { + PGPSecretKeyRing ring = (PGPSecretKeyRing)rIt.next(); + + for (Iterator it = ring.getExtraPublicKeys(); it.hasNext();) + { + it.next(); + count++; + } + } + + if (count != 1) + { + fail("personal certificate data subkey not found - count = " + count); + } + } + + private void checkPublicKeyRingWithX509(byte[] keyRing) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(keyRing, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + if (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + Iterator sIt = key.getSignatures(); + + if (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + if (sig.getKeyAlgorithm() != 100) + { + fail("experimental signature not found"); + } + if (!areEqual(sig.getSignature(), Hex.decode("000101"))) + { + fail("experimental encoding check failed"); + } + } + else + { + fail("no signature found"); + } + } + else + { + fail("no key found"); + } + } + + public void performTest() + throws Exception + { + try + { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + revocationTest(); + test8(); + test9(); + test10(); + test11(); + generateTest(); + generateSha1Test(); + rewrapTest(); + testPublicKeyRingWithX509(); + testSecretKeyRingWithPersonalCertificate(); + insertMasterTest(); + testUmlaut(); + } + catch (PGPException e) + { + if (e.getUnderlyingException() != null) + { + Exception ex = e.getUnderlyingException(); + fail("exception: " + ex, ex); + } + else + { + fail("exception: " + e, e); + } + } + } + + public String getName() + { + return "BcPGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPKeyRingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPPBETest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPPBETest.java new file mode 100644 index 000000000..4955f1e1b --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPPBETest.java @@ -0,0 +1,400 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPPBETest + extends SimpleTest +{ + private static final Date TEST_DATE = new Date(1062200111000L); + + byte[] enc1 = Base64.decode( + "jA0EAwMC5M5wWBP2HBZgySvUwWFAmMRLn7dWiZN6AkQMvpE3b6qwN3SSun7zInw2" + + "hxxdgFzVGfbjuB8w"); + + byte[] enc1crc = Base64.decode("H66L"); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Message with both PBE and symmetric + */ + byte[] testPBEAsym = Base64.decode( + "hQIOA/ZlQEFWB5vuEAf/covEUaBve7NlWWdiO5NZubdtTHGElEXzG9hyBycp9At8" + + "nZGi27xOZtEGFQo7pfz4JySRc3O0s6w7PpjJSonFJyNSxuze2LuqRwFWBYYcbS8/" + + "7YcjB6PqutrT939OWsozfNqivI9/QyZCjBvFU89pp7dtUngiZ6MVv81ds2I+vcvk" + + "GlIFcxcE1XoCIB3EvbqWNaoOotgEPT60unnB2BeDV1KD3lDRouMIYHfZ3SzBwOOI" + + "6aK39sWnY5sAK7JjFvnDAMBdueOiI0Fy+gxbFD/zFDt4cWAVSAGTC4w371iqppmT" + + "25TM7zAtCgpiq5IsELPlUZZnXKmnYQ7OCeysF0eeVwf+OFB9fyvCEv/zVQocJCg8" + + "fWxfCBlIVFNeNQpeGygn/ZmRaILvB7IXDWP0oOw7/F2Ym66IdYYIp2HeEZv+jFwa" + + "l41w5W4BH/gtbwGjFQ6CvF/m+lfUv6ZZdzsMIeEOwhP5g7rXBxrbcnGBaU+PXbho" + + "gjDqaYzAWGlrmAd6aPSj51AGeYXkb2T1T/yoJ++M3GvhH4C4hvitamDkksh/qRnM" + + "M/s8Nku6z1+RXO3M6p5QC1nlAVqieU8esT43945eSoC77K8WyujDNbysDyUCUTzt" + + "p/aoQwe/HgkeOTJNelKR9y2W3xinZLFzep0SqpNI/e468yB/2/LGsykIyQa7JX6r" + + "BYwuBAIDAkOKfv5rK8v0YDfnN+eFqwhTcrfBj5rDH7hER6nW3lNWcMataUiHEaMg" + + "o6Q0OO1vptIGxW8jClTD4N1sCNwNu9vKny8dKYDDHbCjE06DNTv7XYVW3+JqTL5E" + + "BnidvGgOmA=="); + + /** + * decrypt the passed in message stream + */ + private byte[] decryptMessage( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(pass, new BcPGPDigestCalculatorProvider())); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + int ch; + + while ((ch = unc.read()) >= 0) + { + bOut.write(ch); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + private byte[] decryptMessageBuffered( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(pass, new BcPGPDigestCalculatorProvider())); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + byte[] buf = new byte[1024]; + int len; + + while ((len = unc.read(buf)) >= 0) + { + bOut.write(buf, 0, len); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + public void performTest() + throws Exception + { + byte[] out = decryptMessage(enc1, TEST_DATE); + + if (out[0] != 'h' || out[1] != 'e' || out[2] != 'l') + { + fail("wrong plain text in packet"); + } + + // + // create a PBE encrypted message and read it back. + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // encryption step - convert to literal data, compress, encode. + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + Date cDate = new Date((System.currentTimeMillis() / 1000) * 1000); + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + OutputStream comOut = comData.open(new UncloseableOutputStream(bOut)); + OutputStream ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + text.length, + cDate); + + ldOut.write(text); + + ldOut.close(); + + comOut.close(); + + // + // encrypt - with stream close + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(new SecureRandom())); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - with generator close + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(new SecureRandom())); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cPk.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - partial packet style. + // + SecureRandom rand = new SecureRandom(); + byte[] test = new byte[1233]; + + rand.nextBytes(test); + + bOut = new ByteArrayOutputStream(); + + comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + comOut = comData.open(bOut); + lData = new PGPLiteralDataGenerator(); + + ldOut = lData.open(new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, TEST_DATE, + new byte[16]); + + + ldOut.write(test); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // with integrity packet + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(true).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in buffer generated packet"); + } + + // + // sample message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPBEAsym); + + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpFact.nextObject(); + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(1); + + InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory("password".toCharArray(), new BcPGPDigestCalculatorProvider())); + + pgpFact = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + InputStream unc = ld.getInputStream(); + int ch; + + while ((ch = unc.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), Hex.decode("5361742031302e30322e30370d0a"))) + { + fail("data mismatch on combined PBE"); + } + + // + // with integrity packet - one byte message + // + byte[] msg = new byte[1]; + bOut = new ByteArrayOutputStream(); + + comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + lData = new PGPLiteralDataGenerator(); + comOut = comData.open(new UncloseableOutputStream(bOut)); + ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + msg.length, + cDate); + + ldOut.write(msg); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(true).setSecureRandom(rand)); + + cPk.addMethod(new BcPBEKeyEncryptionMethodGenerator(pass)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in buffer generated packet"); + } + } + + public String getName() + { + return "BcPGPPBETest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPPBETest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPRSATest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPRSATest.java new file mode 100644 index 000000000..03f180038 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/BcPGPRSATest.java @@ -0,0 +1,1376 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.attr.ImageAttribute; +import org.spongycastle.bcpg.sig.Features; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.generators.RSAKeyPairGenerator; +import org.spongycastle.crypto.params.RSAKeyGenerationParameters; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.PGPV3SignatureGenerator; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPRSATest + extends SimpleTest +{ + byte[] testPubKey = Base64.decode( + "mIsEPz2nJAEEAOTVqWMvqYE693qTgzKv/TJpIj3hI8LlYPC6m1dk0z3bDLwVVk9F" + + "FAB+CWS8RdFOWt/FG3tEv2nzcoNdRvjv9WALyIGNawtae4Ml6oAT06/511yUzXHO" + + "k+9xK3wkXN5jdzUhf4cA2oGpLSV/pZlocsIDL+jCUQtumUPwFodmSHhzAAYptC9F" + + "cmljIEVjaGlkbmEgKHRlc3Qga2V5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPoi4" + + "BBMBAgAiBQI/PackAhsDBQkAg9YABAsHAwIDFQIDAxYCAQIeAQIXgAAKCRA1WGFG" + + "/fPzc8WMA/9BbjuB8E48QAlxoiVf9U8SfNelrz/ONJA/bMvWr/JnOGA9PPmFD5Uc" + + "+kV/q+i94dEMjsC5CQ1moUHWSP2xlQhbOzBP2+oPXw3z2fBs9XJgnTH6QWMAAvLs" + + "3ug9po0loNHLobT/D/XdXvcrb3wvwvPT2FptZqrtonH/OdzT9JdfrA=="); + + byte[] testPrivKey = Base64.decode( + "lQH8BD89pyQBBADk1aljL6mBOvd6k4Myr/0yaSI94SPC5WDwuptXZNM92wy8FVZP" + + "RRQAfglkvEXRTlrfxRt7RL9p83KDXUb47/VgC8iBjWsLWnuDJeqAE9Ov+ddclM1x" + + "zpPvcSt8JFzeY3c1IX+HANqBqS0lf6WZaHLCAy/owlELbplD8BaHZkh4cwAGKf4D" + + "AwKbLeIOVYTEdWD5v/YgW8ERs0pDsSIfBTvsJp2qA798KeFuED6jGsHUzdi1M990" + + "6PRtplQgnoYmYQrzEc6DXAiAtBR4Kuxi4XHx0ZR2wpVlVxm2Ypgz7pbBNWcWqzvw" + + "33inl7tR4IDsRdJOY8cFlN+1tSCf16sDidtKXUVjRjZNYJytH18VfSPlGXMeYgtw" + + "3cSGNTERwKaq5E/SozT2MKTiORO0g0Mtyz+9MEB6XVXFavMun/mXURqbZN/k9BFb" + + "z+TadpkihrLD1xw3Hp+tpe4CwPQ2GdWKI9KNo5gEnbkJgLrSMGgWalPhknlNHRyY" + + "bSq6lbIMJEE3LoOwvYWwweR1+GrV9farJESdunl1mDr5/d6rKru+FFDwZM3na1IF" + + "4Ei4FpqhivZ4zG6pN5XqLy+AK85EiW4XH0yAKX1O4YlbmDU4BjxhiwTdwuVMCjLO" + + "5++jkz5BBQWdFX8CCMA4FJl36G70IbGzuFfOj07ly7QvRXJpYyBFY2hpZG5hICh0" + + "ZXN0IGtleSkgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IuAQTAQIAIgUCPz2nJAIb" + + "AwUJAIPWAAQLBwMCAxUCAwMWAgECHgECF4AACgkQNVhhRv3z83PFjAP/QW47gfBO" + + "PEAJcaIlX/VPEnzXpa8/zjSQP2zL1q/yZzhgPTz5hQ+VHPpFf6voveHRDI7AuQkN" + + "ZqFB1kj9sZUIWzswT9vqD18N89nwbPVyYJ0x+kFjAALy7N7oPaaNJaDRy6G0/w/1" + + "3V73K298L8Lz09habWaq7aJx/znc0/SXX6w="); + + byte[] testPubKeyV3 = Base64.decode( + "mQCNAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURtApGSVhDSVRZX1FBiQCVAwUQP7O+UZ6Fwdi2fBUJAQFMwwQA" + + "qRnFsdg4xQnB8Y5d4cOpXkIn9AZgYS3cxtuSJB84vG2CgC39nfv4c+nlLkWP" + + "4puG+mZuJNgVoE84cuAF4I//1anKjlU7q1M6rFQnt5S4uxPyG3dFXmgyU1b4" + + "PBOnA0tIxjPzlIhJAMsPCGGA5+5M2JP0ad6RnzqzE3EENMX+GqY="); + + byte[] testPrivKeyV3 = Base64.decode( + "lQHfAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURAXWwRBZQHNikA/f0ScLLjrXi4s0hgQecg+dkpDow94eu5+AR" + + "0DzZnfurpgfUJCNiDi5W/5c3Zj/xyrfMAgkbCgJ1m6FZqAQh7Mq73l7Kfu4/" + + "XIkyDF3tDgRuZNezB+JuElX10tV03xumHepp6M6CfhXqNJ15F33F99TA5hXY" + + "CPYD7SiSOpIhQkCOAgDAA63imxbpuKE2W7Y4I1BUHB7WQi8ZdkZd04njNTv+" + + "rFUuOPapQVfbWG0Vq8ld3YmJB4QWsa2mmqn+qToXbwufAgBpXkjvqK5yPiHF" + + "Px2QbFc1VqoCJB6PO5JRIqEiUZBFGdDlLxt3VSyqz7IZ/zEnxZq+tPCGGGSm" + + "/sAGiMvENcHVAfy0kTXU42TxEAYJyyNyqjXOobDJpEV1mKhFskRXt7tbMfOS" + + "Yf91oX8f6xw6O2Nal+hU8dS0Bmfmk5/enHmvRLHQocO0CkZJWENJVFlfUUE="); + + byte[] sig1 = Base64.decode( + "owGbwMvMwMRoGpHo9vfz52LGNTJJnBmpOTn5eiUVJfb23JvAHIXy/KKcFEWuToap" + + "zKwMIGG4Bqav0SwMy3yParsEKi2LMGI9xhh65sBxb05n5++ZLcWNJ/eLFKdWbm95" + + "tHbDV7GMwj/tUctUpFUXWPYFCLdNsDiVNuXbQvZtdXV/5xzY+9w1nCnijH9JoNiJ" + + "22n2jo0zo30/TZLo+jDl2vTzIvPeLEsPM3ZUE/1Ytqs4SG2TxIQbH7xf3uzcYXq2" + + "5Fw9AA=="); + + byte[] sig1crc = Base64.decode("+3i0"); + + byte[] subKey = Base64.decode( + "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" + + "CphiUYWnsC0mQ+J15B4="); + + byte[] enc1 = Base64.decode( + "hIwDKwfQexPJboABA/4/7prhYYMORTiQ5avQKx0XYpCLujzGefYjnyuWZnx3Iev8" + + "Pmsguumm+OLLvtXhhkXQmkJRXbIg6Otj2ubPYWflRPgpJSgOrNOreOl5jeABOrtw" + + "bV6TJb9OTtZuB7cTQSCq2gmYiSZkluIiDjNs3R3mEanILbYzOQ3zKSggKpzlv9JQ" + + "AZUqTyDyJ6/OUbJF5fI5uiv76DCsw1zyMWotUIu5/X01q+AVP5Ly3STzI7xkWg/J" + + "APz4zUHism7kSYz2viAQaJx9/bNnH3AM6qm1Fuyikl4="); + + byte[] enc1crc = Base64.decode("lv4o"); + + byte[] enc2 = Base64.decode( + "hIwDKwfQexPJboABBAC62jcJH8xKnKb1neDVmiovYON04+7VQ2v4BmeHwJrdag1g" + + "Ya++6PeBlQ2Q9lSGBwLobVuJmQ7cOnPUJP727JeSGWlMyFtMbBSHekOaTenT5lj7" + + "Zk7oRHxMp/hByzlMacIDzOn8LPSh515RHM57eDLCOwqnAxGQwk67GRl8f5dFH9JQ" + + "Aa7xx8rjCqPbiIQW6t5LqCNvPZOiSCmftll6+se1XJhFEuq8WS4nXtPfTiJ3vib4" + + "3soJdHzGB6AOs+BQ6aKmmNTVAxa5owhtSt1Z/6dfSSk="); + + byte[] subPubKey = Base64.decode( + "mIsEPz2nJAEEAOTVqWMvqYE693qTgzKv/TJpIj3hI8LlYPC6m1dk0z3bDLwVVk9F" + + "FAB+CWS8RdFOWt/FG3tEv2nzcoNdRvjv9WALyIGNawtae4Ml6oAT06/511yUzXHO" + + "k+9xK3wkXN5jdzUhf4cA2oGpLSV/pZlocsIDL+jCUQtumUPwFodmSHhzAAYptC9F" + + "cmljIEVjaGlkbmEgKHRlc3Qga2V5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPoi4" + + "BBMBAgAiBQI/PackAhsDBQkAg9YABAsHAwIDFQIDAxYCAQIeAQIXgAAKCRA1WGFG" + + "/fPzc8WMA/9BbjuB8E48QAlxoiVf9U8SfNelrz/ONJA/bMvWr/JnOGA9PPmFD5Uc" + + "+kV/q+i94dEMjsC5CQ1moUHWSP2xlQhbOzBP2+oPXw3z2fBs9XJgnTH6QWMAAvLs" + + "3ug9po0loNHLobT/D/XdXvcrb3wvwvPT2FptZqrtonH/OdzT9JdfrIhMBBARAgAM" + + "BQI/RxooBYMAemL8AAoJEM0j9enEyjRDiBgAn3RcLK+gq90PvnQFTw2DNqdq7KA0" + + "AKCS0EEIXCzbV1tfTdCUJ3hVh3btF7QkRXJpYyBFY2hpZG5hIDxlcmljQGJvdW5j" + + "eWNhc3RsZS5vcmc+iLgEEwECACIFAj9HFA0CGwMFCQCD1gAECwcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEDVYYUb98/Nzc7oD/j21cUKKI/y+Dvs5e+eZufM8EDUbqMJFk0/X" + + "Ihe6w6OkerIs3kr2HDqWr+jik2IK6KrfaiyoZFfeW/+cN24lTWSfY7D4jZVyL6iM" + + "xd0IZ0So/Bl+/juMcvBGshQnbY46haw5G2C9J5FR3gjODyOu5oUzu5+vmGuMSXVy" + + "4tbUevwuiEwEEBECAAwFAj9HGigFgwB6YvwACgkQzSP16cTKNEPwBQCdHm0Amwza" + + "NmVmDHm3rmqI7rp2oQ0An2YbiP/H/kmBNnmTeH55kd253QOhuIsEP0ccUAEEANCd" + + "gSHam/CbQxio72Ma47tLqyVVc8iNQpwowKewrS6B0CVEFOz2o51eCXddrpH3KCuz" + + "Ea2mMnUBI8YdjKj6bkICUt3YEFa8Tj2qw0GARa2TXdrWcTo2GpIRK71JkaOslhWs" + + "QocKjIJfF1roATYNQ72vv2HKBkPpcviYOEVmaB/PAAYpiJ8EGAECAAkFAj9HHFAC" + + "GwwACgkQNVhhRv3z83M5hQP8DIOR4TeTZKpo5x1BX3lzV3R85npMsN/EZ7gjCpn3" + + "ONwIg7PhbSetoKUOjxLGReKPUuX8XI1ZN1UoNNFzA7UP9AbCgXORsYSU6uxvv0qH" + + "jON91htfbqEtPuS3/99cDaE56b+p6TmmKXE0Oy/Swp88ABeB9ISTxOm4ZltgjHLB" + + "PyGZAaIEP0cHNREEALOTFEJUYmDn4t30NThemd3gsIqAyrtg5jsaLQSAr3aBtprr" + + "7NChAWrPW23dBSanPdhWSb/MsRIEG0LUAIhcaCBR0UxSvkjzv7UJvq/N58C0a3w3" + + "lVXUX8xe/WAnPgG7ofLEv+dx97vWOw2Fq/SLjICN/Z61YWPnfMrpH46ODkfrAKCk" + + "v3+1dV8Y/xKoW/Ks3/jfsyUVJQQAg3dXqP8xSWXKtmCWu2vb5FYjBGsb8AKasXqF" + + "O6A6XhxDFoNg1ye1duGVOUmz0zGNMffHHPKXKKsLKrsfMYnFnNK3oVN6HUMVjOEn" + + "tC5hHXGtJ0DpjSZS/SLEPkDic8eO6lDlAKocZ5F8UP+gfMPjOoGcmn8Encu9Drps" + + "REmYqXkD/i6PcehXGlvS+/IQ19GooyYYEhLJxjgygfm5Z9vbcV9+xP2v0ovqcniz" + + "IvNpFHSC9xtyORz1nd6kjuCveNOtvh3xY/GwNtP9bW2/1sb8QdrCiYtyKG25RTrx" + + "R6Pr/Bmi3Vcmq7adCWS7nv4z9XZfXLbpUqyKmiU9+Nri7IE47K9stDNFcmljIEVj" + + "aGlkbmEgKERTQSBUZXN0IEtleSkgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IWQQT" + + "EQIAGQUCP0cHNQQLBwMCAxUCAwMWAgECHgECF4AACgkQzSP16cTKNEMCXACfauui" + + "bSwyG59Yrm8hHCDuCPmqwsQAni+dPl08FVuWh+wb6kOgJV4lcYae"); + + byte[] subPubCrc = Base64.decode("rikt"); + + byte[] pgp8Key = Base64.decode( + "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF" + + "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd" + + "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy" + + "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y" + + "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7" + + "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO" + + "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP" + + "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY" + + "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb" + + "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4" + + "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj" + + "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I" + + "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH" + + "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt" + + "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j" + + "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw" + + "AgAA"); + + char[] pgp8Pass = "2002 Buffalo Sabres".toCharArray(); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + byte[] fingerprintKey = Base64.decode( + "mQEPA0CiJdUAAAEIAMI+znDlPd2kQoEcnxqxLcRz56Z7ttFKHpnYp0UkljZdquVc" + + "By1jMfXGVV64xN1IvMcyenLXUE0IUeUBCQs6tHunFRAPSeCxJ3FdFe1B5MpqQG8A" + + "BnEpAds/hAUfRDZD5y/lolk1hjvFMrRh6WXckaA/QQ2t00NmTrJ1pYUpkw9tnVQb" + + "LUjWJhfZDBBcN0ADtATzgkugxMtcDxR6I5x8Ndn+IilqIm23kxGIcmMd/BHOec4c" + + "jRwJXXDb7u8tl+2knAf9cwhPHp3+Zy4uGSQPdzQnXOhBlA+4WDa0RROOevWgq8uq" + + "8/9Xp/OlTVL+OoIzjsI6mJP1Joa4qmqAnaHAmXcAEQEAAbQoQk9BM1JTS1kgPEJP" + + "QSBNb25pdG9yaW5nIEAgODg4LTI2OS01MjY2PokBFQMFEECiJdWqaoCdocCZdwEB" + + "0RsH/3HPxoUZ3G3K7T3jgOnJUckTSHWU3XspHzMVgqOxjTrcexi5IsAM5M+BulfW" + + "T2aO+Kqf5w8cKTKgW02DNpHUiPjHx0nzDE+Do95zbIErGeK+Twkc4O/aVsvU9GGO" + + "81VFI6WMvDQ4CUAUnAdk03MRrzI2nAuhn4NJ5LQS+uJrnqUJ4HmFAz6CQZQKd/kS" + + "Xgq+A6i7aI1LG80YxWa9ooQgaCrb9dwY/kPQ+yC22zQ3FExtv+Fv3VtAKTilO3vn" + + "BA4Y9uTHuObHfI+1yxUS2PrlRUX0m48ZjpIX+cEN3QblGBJudI/A1QSd6P0LZeBr" + + "7F1Z1aF7ZDo0KzgiAIBvgXkeTpw="); + + byte[] fingerprintCheck = Base64.decode("CTv2"); + + byte[] expiry60and30daysSig13Key = Base64.decode( + "mQGiBENZt/URBAC5JccXiwe4g6MuviEC8NI/x0NaVkGFAOY04d5E4jeIycBP" + + "SrpOPrjETuigqhrj8oqed2+2yUqfnK4nhTsTAjyeJ3PpWC1pGAKzJgYmJk+K" + + "9aTLq0BQWiXDdv5RG6fDmeq1umvOfcXBqGFAguLPZC+U872bSLnfe3lqGNA8" + + "jvmY7wCgjhzVQVm10NN5ST8nemPEcSjnBrED/R494gHL6+r5OgUgXnNCDejA" + + "4InoDImQCF+g7epp5E1MB6CMYSg2WSY2jHFuHpwnUb7AiOO0ZZ3UBqM9rYnK" + + "kDvxkFCxba7Ms+aFj9blRNmy3vG4FewDcTdxzCtjUk6dRfu6UoARpqlTE/q7" + + "Xo6EQP1ncwJ+UTlcHkTBvg/usI/yBACGjBqX8glb5VfNaZgNHMeS/UIiUiuV" + + "SVFojiSDOHcnCe/6y4M2gVm38zz1W9qhoLfLpiAOFeL0yj6wzXvsjjXQiKQ8" + + "nBE4Mf+oeH2qiQ/LfzQrGpI5eNcMXrzK9nigmz2htYO2GjQfupEnu1RHBTH8" + + "NjofD2AShL9IO73plRuExrQgVGVzdCBLZXkgPHRlc3RAYm91bmN5Y2FzdGxl" + + "Lm9yZz6IZAQTEQIAJAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAUCQ1m4DgUJ" + + "AE8aGQAKCRD8QP1QuU7Kqw+eAJ0dZ3ZAqr73X61VmCkbyPoszLQMAQCfdFs2" + + "YMDeUvX34Q/8Ba0KgO5f3RSwAgADuM0EQ1m39hADAIHpVGcLqS9UkmQaWBvH" + + "WP6TnN7Y1Ha0TJOuxpbFjBW+CmVh/FjcsnavFXDXpo2zc742WT+vrHBSa/0D" + + "1QEBsnCaX5SRRVp7Mqs8q+aDhjcHMIP8Sdxf7GozXDORkrRaJwADBQL9HLYm" + + "7Rr5iYWDcvs+Pi6O1zUyb1tjkxEGaV/rcozl2MMmr2mzJ6x/Bz8SuhZEJS0m" + + "bB2CvAA39aQi9jHlV7q0SV73NOkd2L/Vt2UZhzlUdvrJ37PgYDv+Wd9Ufz6g" + + "MzLSiE8EGBECAA8FAkNZt/YCGwwFCQAnjQAACgkQ/ED9ULlOyqsTqQCcDnAZ" + + "7YymCfhm1yJiuFQg3qiX6Z4An19OSEgeSKugVcH49g1sxUB0zNdIsAIAAw=="); + + byte[] jpegImage = Base64.decode( + "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"); + + byte[] embeddedJPEGKey = Base64.decode( + "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" + + "Qvmtc8X7lqK/zLRVkSSBItik2IIhsAIAAw=="); + + + private void fingerPrintTest() + throws Exception + { + // + // version 3 + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(fingerprintKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("4FFB9F0884266C715D1CEAC804A3BBFA"))) + { + fail("version 3 fingerprint test failed"); + } + + // + // version 4 + // + pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("3062363c1046a01a751946bb35586146fdf3f373"))) + { + fail("version 4 fingerprint test failed"); + } + } + + private void mixedTest(PGPPrivateKey pgpPrivKey, PGPPublicKey pgpPubKey) + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // literal data + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, text.length, new Date()); + + lOut.write(text); + + lGen.close(); + + byte[] bytes = bOut.toByteArray(); + + PGPObjectFactory f = new PGPObjectFactory(bytes); + checkLiteralData((PGPLiteralData)f.nextObject(), text); + + ByteArrayOutputStream bcOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_128).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom())); + + encGen.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(pgpPubKey)); + + encGen.addMethod(new BcPBEKeyEncryptionMethodGenerator("password".toCharArray())); + + OutputStream cOut = encGen.open(bcOut, bytes.length); + + cOut.write(bytes); + + cOut.close(); + + byte[] encData = bcOut.toByteArray(); + + // + // asymmetric + // + PGPObjectFactory pgpF = new PGPObjectFactory(encData); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpFact.nextObject(), text); + + // + // PBE + // + pgpF = new PGPObjectFactory(encData); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPBEEncryptedData encPbe = (PGPPBEEncryptedData)encList.get(1); + + clear = encPbe.getDataStream(new BcPBEDataDecryptorFactory("password".toCharArray(), new BcPGPDigestCalculatorProvider())); + + pgpF = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpF.nextObject(), text); + } + + private void checkLiteralData(PGPLiteralData ld, byte[] data) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals(PGPLiteralData.CONSOLE)) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + int ch; + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), data)) + { + fail("wrong plain text in decrypted packet"); + } + } + + private void existingEmbeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(embeddedJPEGKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature sig = (PGPSignature)sigs.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + if (!sig.verifyCertification(attributes, pubKey)) + { + fail("signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find user attributes"); + } + } + + private void embeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + PGPSecretKeyRing pgpSec = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + PGPUserAttributeSubpacketVectorGenerator vGen = new PGPUserAttributeSubpacketVectorGenerator(); + + vGen.setImageAttribute(ImageAttribute.JPEG, jpegImage); + + PGPUserAttributeSubpacketVector uVec = vGen.generate(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, pgpSec.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass))); + + PGPSignature sig = sGen.generateCertification(uVec, pubKey); + + PGPPublicKey nKey = PGPPublicKey.addCertification(pubKey, uVec, sig); + + Iterator it = nKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = nKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature s = (PGPSignature)sigs.next(); + + s.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + if (!s.verifyCertification(attributes, pubKey)) + { + fail("added signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed added user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find added user attributes"); + } + + nKey = PGPPublicKey.removeCertification(nKey, uVec); + count = 0; + for (it = nKey.getUserAttributes(); it.hasNext();) + { + count++; + } + if (count != 0) + { + fail("found attributes where none expected"); + } + } + + private void sigsubpacketTest() + throws Exception + { + char[] passPhrase = "test".toCharArray(); + String identity = "TEST <test@test.org>"; + Date date = new Date(); + + RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); + kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 2048, 25)); + AsymmetricCipherKeyPair kpSgn = kpg.generateKeyPair(); + AsymmetricCipherKeyPair kpEnc = kpg.generateKeyPair(); + + PGPKeyPair sgnKeyPair = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpSgn, date); + PGPKeyPair encKeyPair = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpEnc, date); + + PGPSignatureSubpacketVector unhashedPcks = null; + PGPSignatureSubpacketGenerator svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setPrimaryUserID(true, true); + int[] encAlgs = {SymmetricKeyAlgorithmTags.AES_256, + SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.TRIPLE_DES}; + svg.setPreferredSymmetricAlgorithms(true, encAlgs); + int[] hashAlgs = {HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA512, + HashAlgorithmTags.SHA384, + HashAlgorithmTags.SHA256, + HashAlgorithmTags.RIPEMD160}; + svg.setPreferredHashAlgorithms(true, hashAlgs); + int[] comprAlgs = {CompressionAlgorithmTags.ZLIB, + CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP}; + svg.setPreferredCompressionAlgorithms(true, comprAlgs); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + svg.setKeyFlags(true, KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA); + PGPSignatureSubpacketVector hashedPcks = svg.generate(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + sgnKeyPair, identity, new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1), + hashedPcks, unhashedPcks, new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(passPhrase)); + + svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setKeyFlags(true, KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); + svg.setPrimaryUserID(true, false); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + hashedPcks = svg.generate(); + + keyRingGen.addSubKey(encKeyPair, hashedPcks, unhashedPcks); + + byte[] encodedKeyRing = keyRingGen.generatePublicKeyRing().getEncoded(); + + PGPPublicKeyRing keyRing = new PGPPublicKeyRing(encodedKeyRing, new BcKeyFingerprintCalculator()); + + for (Iterator it = keyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pKey = (PGPPublicKey)it.next(); + + if (pKey.isEncryptionKey()) + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (v.getKeyExpirationTime() != 86400L * 366 * 2) + { + fail("key expiration time wrong"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.isPrimaryUserID()) + { + fail("primary userID flag wrong"); + } + if (v.getKeyFlags() != KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE) + { + fail("keyFlags wrong"); + } + } + } + else + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (!Arrays.areEqual(v.getPreferredSymmetricAlgorithms(), encAlgs)) + { + fail("preferred encryption algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredHashAlgorithms(), hashAlgs)) + { + fail("preferred hash algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredCompressionAlgorithms(), comprAlgs)) + { + fail("preferred compression algs don't match"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.getKeyFlags() != KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA) + { + fail("keyFlags wrong"); + } + } + } + } + } + + public void performTest() + throws Exception + { + PublicKey pubKey = null; + + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new BcKeyFingerprintCalculator()); + + pubKey = pgpPub.getPublicKey().getKey("SC"); + + Iterator it = pgpPub.getPublicKey().getUserIDs(); + + String uid = (String)it.next(); + + it = pgpPub.getPublicKey().getSignaturesForID(uid); + + PGPSignature sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + if (!sig.verifyCertification(uid, pgpPub.getPublicKey())) + { + fail("failed to verify certification"); + } + + // + // write a public key + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pgpPub.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPubKey)) + { + fail("public key rewrite failed"); + } + + // + // Read the public key + // + PGPPublicKeyRing pgpPubV3 = new PGPPublicKeyRing(testPubKeyV3, new BcKeyFingerprintCalculator()); + PublicKey pubKeyV3 = pgpPub.getPublicKey().getKey("SC"); + + // + // write a V3 public key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPubV3.encode(pOut); + + // + // Read a v3 private key + // + char[] passP = "FIXCITY_QA".toCharArray(); + + if (!noIDEA()) + { + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKeyV3, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passP)); + + // + // write a v3 private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKeyV3)) + { + fail("private key V3 rewrite failed"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKey, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // write a private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKey)) + { + fail("private key rewrite failed"); + } + + + // + // test encryption + // + Cipher c = Cipher.getInstance("RSA", "SC"); + + c.init(Cipher.ENCRYPT_MODE, pubKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.doFinal(in); + + c.init(Cipher.DECRYPT_MODE, pgpPrivKey.getKey()); + + out = c.doFinal(out); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // test signature message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1, new BcKeyFingerprintCalculator()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream(), new BcKeyFingerprintCalculator()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + int ch; + + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey(ops.getKeyID())); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + // + // encrypted message - read subkey + // + pgpPriv = new PGPSecretKeyRing(subKey, new BcKeyFingerprintCalculator()); + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(enc1, new BcKeyFingerprintCalculator()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream(), new BcKeyFingerprintCalculator()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt - short message + // + byte[] shortText = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o' }; + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), shortText.length); + + cOut.write(shortText); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray(), new BcKeyFingerprintCalculator()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + PublicKeyDataDecryptorFactory dataDecryptorFactory = new BcPublicKeyDataDecryptorFactory(pgpPrivKey); + + if (encP.getSymmetricAlgorithm(dataDecryptorFactory) != SymmetricKeyAlgorithmTags.CAST5) + { + fail("symmetric algorithm mismatch"); + } + + clear = encP.getDataStream(dataDecryptorFactory); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, shortText)) + { + fail("wrong plain text in generated short text packet"); + } + + // + // encrypt + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(new SecureRandom())); + puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // read public key with sub key. + // + pgpF = new PGPObjectFactory(subPubKey, new BcKeyFingerprintCalculator()); + Object o; + + while ((o = pgpFact.nextObject()) != null) + { + // System.out.println(o); + } + + // + // key pair generation - CAST5 encryption + // + char[] passPhrase = "hello".toCharArray(); + + RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); + + kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25)); + + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, new BcPGPKeyPair(PublicKeyAlgorithmTags.RSA_GENERAL, kp, new Date()), "fred", null, null, new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).build(passPhrase)); + + PGPPublicKey key = secretKey.getPublicKey(); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + key = PGPPublicKey.removeCertification(key, uid, sig); + + if (key == null) + { + fail("failed certification removal"); + } + + byte[] keyEnc = key.getEncoded(); + + key = PGPPublicKey.addCertification(key, uid, sig); + + keyEnc = key.getEncoded(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.KEY_REVOCATION, secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase))); + + sig = sGen.generateCertification(key); + + key = PGPPublicKey.addCertification(key, sig); + + keyEnc = key.getEncoded(); + + PGPPublicKeyRing tmpRing = new PGPPublicKeyRing(keyEnc, new BcKeyFingerprintCalculator()); + + key = tmpRing.getPublicKey(); + + Iterator sgIt = key.getSignaturesOfType(PGPSignature.KEY_REVOCATION); + + sig = (PGPSignature)sgIt.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(key)) + { + fail("failed to verify revocation certification"); + } + + // + // use of PGPKeyPair + // + PGPKeyPair pgpKp = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL , kp, new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + k1.getEncoded(); + + mixedTest(k2, k1); + + // + // key pair generation - AES_256 encryption. + // + kp = kpg.generateKeyPair(); + + secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, new BcPGPKeyPair(PublicKeyAlgorithmTags.RSA_GENERAL, kp, new Date()), "fred", null, null, new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1), new BcPBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256).build(passPhrase)); + + secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + secretKey.encode(new ByteArrayOutputStream()); + + // + // secret key password changing. + // + String newPass = "newPass"; + + secretKey = PGPSecretKey.copyWithNewPassword(secretKey, new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase), new BcPBESecretKeyEncryptorBuilder(secretKey.getKeyEncryptionAlgorithm()).build(newPass.toCharArray())); + + secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(newPass.toCharArray())); + + secretKey.encode(new ByteArrayOutputStream()); + + key = secretKey.getPublicKey(); + + key.encode(new ByteArrayOutputStream()); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), key); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(newPass.toCharArray())); + + // + // signature generation + // + String data = "hello world!"; + + bOut = new ByteArrayOutputStream(); + + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + + sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bcOut); + + bcOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved: " + p2.getModificationTime() + " " + testDate); + } + + dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), secretKey.getPublicKey()); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // signature generation - version 3 + // + bOut = new ByteArrayOutputStream(); + + testIn = new ByteArrayInputStream(data.getBytes()); + PGPV3SignatureGenerator sGenV3 = new PGPV3SignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + bcOut = new BCPGOutputStream(cGen.open(bOut)); + + sGen.generateOnePassVersion(false).encode(bcOut); + + lGen = new PGPLiteralDataGenerator(); + lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bcOut); + + bcOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), secretKey.getPublicKey()); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed v3 generated signature check"); + } + + // + // extract PGP 8 private key + // + pgpPriv = new PGPSecretKeyRing(pgp8Key, new BcKeyFingerprintCalculator()); + + secretKey = pgpPriv.getSecretKey(); + + pgpPrivKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pgp8Pass)); + + // + // expiry + // + testExpiry(expiry60and30daysSig13Key, 60, 30); + + fingerPrintTest(); + existingEmbeddedJpegTest(); + embeddedJpegTest(); + sigsubpacketTest(); + } + + private void testExpiry( + byte[] encodedRing, + int masterDays, + int subKeyDays) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(encodedRing, new BcKeyFingerprintCalculator()); + PGPPublicKey k = pubRing.getPublicKey(); + + if (k.getValidDays() != masterDays) + { + fail("mismatch on master valid days."); + } + + Iterator it = pubRing.getPublicKeys(); + + it.next(); + + k = (PGPPublicKey)it.next(); + + if (k.getValidDays() != subKeyDays) + { + fail("mismatch on subkey valid days."); + } + } + + private boolean noIDEA() + { + return true; + } + + public String getName() + { + return "BcPGPRSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPRSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/DSA2Test.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/DSA2Test.java new file mode 100644 index 000000000..eb8adcd46 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/DSA2Test.java @@ -0,0 +1,290 @@ +package org.spongycastle.openpgp.test; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.util.test.UncloseableOutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Security; +import java.util.Date; + +/** + * GPG compatability test vectors + */ +public class DSA2Test + extends TestCase +{ + private static final String TEST_DATA_HOME = "bc.test.data.home"; + + public void setUp() + { + if (Security.getProvider("SC") == null) + { + Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); + } + } + + public void testK1024H160() + throws Exception + { + doSigVerifyTest("DSA-1024-160.pub", "dsa-1024-160-sign.gpg"); + } + + public void testK1024H224() + throws Exception + { + doSigVerifyTest("DSA-1024-160.pub", "dsa-1024-224-sign.gpg"); + } + + public void testK1024H256() + throws Exception + { + doSigVerifyTest("DSA-1024-160.pub", "dsa-1024-256-sign.gpg"); + } + + public void testK1024H384() + throws Exception + { + doSigVerifyTest("DSA-1024-160.pub", "dsa-1024-384-sign.gpg"); + } + + public void testK1024H512() + throws Exception + { + doSigVerifyTest("DSA-1024-160.pub", "dsa-1024-512-sign.gpg"); + } + + public void testK2048H224() + throws Exception + { + doSigVerifyTest("DSA-2048-224.pub", "dsa-2048-224-sign.gpg"); + } + + public void testK3072H256() + throws Exception + { + doSigVerifyTest("DSA-3072-256.pub", "dsa-3072-256-sign.gpg"); + } + + public void testK7680H384() + throws Exception + { + doSigVerifyTest("DSA-7680-384.pub", "dsa-7680-384-sign.gpg"); + } + + public void testK15360H512() + throws Exception + { + doSigVerifyTest("DSA-15360-512.pub", "dsa-15360-512-sign.gpg"); + } + + public void testGenerateK1024H224() + throws Exception + { + doSigGenerateTest("DSA-1024-160.sec", "DSA-1024-160.pub", PGPUtil.SHA224); + } + + public void testGenerateK1024H256() + throws Exception + { + doSigGenerateTest("DSA-1024-160.sec", "DSA-1024-160.pub", PGPUtil.SHA256); + } + + public void testGenerateK1024H384() + throws Exception + { + doSigGenerateTest("DSA-1024-160.sec", "DSA-1024-160.pub", PGPUtil.SHA384); + } + + public void testGenerateK1024H512() + throws Exception + { + doSigGenerateTest("DSA-1024-160.sec", "DSA-1024-160.pub", PGPUtil.SHA512); + } + + public void testGenerateK2048H256() + throws Exception + { + doSigGenerateTest("DSA-2048-224.sec", "DSA-2048-224.pub", PGPUtil.SHA256); + } + + public void testGenerateK2048H512() + throws Exception + { + doSigGenerateTest("DSA-2048-224.sec", "DSA-2048-224.pub", PGPUtil.SHA512); + } + + private void doSigGenerateTest(String privateKeyFile, String publicKeyFile, int digest) + throws Exception + { + PGPSecretKeyRing secRing = loadSecretKey(privateKeyFile); + PGPPublicKeyRing pubRing = loadPublicKey(publicKeyFile); + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PublicKeyAlgorithmTags.DSA, digest, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, secRing.getSecretKey().extractPrivateKey("test".toCharArray(), "SC")); + + BCPGOutputStream bcOut = new BCPGOutputStream(bOut); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + PGPObjectFactory pgpFact = new PGPObjectFactory(bOut.toByteArray()); + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + + assertEquals(digest, ops.getHashAlgorithm()); + assertEquals(PublicKeyAlgorithmTags.DSA, ops.getKeyAlgorithm()); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(pubRing.getPublicKey(), "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + + assertEquals(digest, sig.getHashAlgorithm()); + assertEquals(PublicKeyAlgorithmTags.DSA, sig.getKeyAlgorithm()); + + assertTrue(ops.verify(sig)); + } + + private void doSigVerifyTest( + String publicKeyFile, + String sigFile) + throws Exception + { + PGPPublicKeyRing publicKey = loadPublicKey(publicKeyFile); + PGPObjectFactory pgpFact = loadSig(sigFile); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(publicKey.getPublicKey(), "SC"); + + int ch; + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + assertTrue(ops.verify(p3.get(0))); + } + + private PGPObjectFactory loadSig( + String sigName) + throws Exception + { + FileInputStream fIn = new FileInputStream(getDataHome() + "/sigs/" + sigName); + + return new PGPObjectFactory(fIn); + } + + private PGPPublicKeyRing loadPublicKey( + String keyName) + throws Exception + { + FileInputStream fIn = new FileInputStream(getDataHome() + "/keys/" + keyName); + + return new PGPPublicKeyRing(fIn); + } + + private PGPSecretKeyRing loadSecretKey( + String keyName) + throws Exception + { + FileInputStream fIn = new FileInputStream(getDataHome() + "/keys/" + keyName); + + return new PGPSecretKeyRing(fIn); + } + + private String getDataHome() + { + String dataHome = System.getProperty(TEST_DATA_HOME); + + if (dataHome == null) + { + throw new IllegalStateException(TEST_DATA_HOME + " property not set"); + } + + return dataHome + "/openpgp/dsa"; + } + + public static void main (String[] args) + throws Exception + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("GPG DSA2 tests"); + + suite.addTestSuite(DSA2Test.class); + + return suite; + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPArmoredTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPArmoredTest.java new file mode 100644 index 000000000..3581f1692 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPArmoredTest.java @@ -0,0 +1,255 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.openpgp.PGPObjectFactory; + +public class PGPArmoredTest + extends SimpleTest +{ + byte[] sample = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] marker = Hex.decode("2d2d2d2d2d454e4420504750205055424c4943204b455920424c4f434b2d2d2d2d2d"); + + // Contains "Hello World!" as an armored message + // The 'blank line' after the headers contains (legal) whitespace - see RFC2440 6.2 + private static final String blankLineData = + "-----BEGIN PGP MESSAGE-----\n" + + "Version: BCPG v1.32\n" + + "Comment: A dummy message\n" + + " \t \t\n" + + "SGVsbG8gV29ybGQh\n" + + "=d9Xi\n" + + "-----END PGP MESSAGE-----\n"; + + private int markerCount( + byte[] data) + { + int ind = 0; + int matches = 0; + + while (ind < data.length) + { + if (data[ind] == 0x2d) + { + int count = 0; + while (count < marker.length) + { + if (data[ind + count] != marker[count]) + { + break; + } + count++; + } + + if (count == marker.length) + { + matches++; + } + + ind += count; + } + else + { + ind++; + } + } + + return matches; + } + + private void blankLineTest() throws Exception + { + byte[] blankLineBytes = Strings.toByteArray(blankLineData); + ByteArrayInputStream bIn = new ByteArrayInputStream(blankLineBytes); + ArmoredInputStream aIn = new ArmoredInputStream(bIn, true); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int c; + while ((c = aIn.read()) >= 0) + { + bOut.write(c); + } + + byte[] expected = Strings.toByteArray("Hello World!"); + + if (!Arrays.areEqual(expected, bOut.toByteArray())) + { + fail("Incorrect message retrieved in blank line test."); + } + } + + public void performTest() + throws Exception + { + // + // test immediate close + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream aOut = new ArmoredOutputStream(bOut); + + aOut.close(); + + byte[] data = bOut.toByteArray(); + + if (data.length != 0) + { + fail("No data should have been written"); + } + + // + // multiple close + // + bOut = new ByteArrayOutputStream(); + aOut = new ArmoredOutputStream(bOut); + + aOut.write(sample); + + aOut.close(); + + aOut.close(); + + int mc = markerCount(bOut.toByteArray()); + + if (mc < 1) + { + fail("No end marker found"); + } + + if (mc > 1) + { + fail("More than one end marker found"); + } + + // + // writing and reading single objects + // + bOut = new ByteArrayOutputStream(); + aOut = new ArmoredOutputStream(bOut); + + aOut.write(sample); + + aOut.close(); + + ArmoredInputStream aIn = new ArmoredInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + PGPObjectFactory fact = new PGPObjectFactory(aIn); + int count = 0; + + while (fact.nextObject() != null) + { + count++; + } + + if (count != 1) + { + fail("wrong number of objects found: " + count); + } + + // + // writing and reading multiple objects - in single block + // + bOut = new ByteArrayOutputStream(); + aOut = new ArmoredOutputStream(bOut); + + aOut.write(sample); + aOut.write(sample); + + aOut.close(); + + aIn = new ArmoredInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + fact = new PGPObjectFactory(aIn); + count = 0; + + while (fact.nextObject() != null) + { + count++; + } + + if (count != 2) + { + fail("wrong number of objects found: " + count); + } + + // + // writing and reading multiple objects - in single block + // + bOut = new ByteArrayOutputStream(); + aOut = new ArmoredOutputStream(bOut); + + aOut.write(sample); + + aOut.close(); // does not close underlying stream + + aOut = new ArmoredOutputStream(bOut); + + aOut.write(sample); + + aOut.close(); + + aIn = new ArmoredInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + count = 0; + boolean atLeastOne; + do + { + atLeastOne = false; + fact = new PGPObjectFactory(aIn); + + while (fact.nextObject() != null) + { + atLeastOne = true; + count++; + } + } + while (atLeastOne); + + if (count != 2) + { + fail("wrong number of objects found: " + count); + } + + blankLineTest(); + } + + public String getName() + { + return "PGPArmoredTest"; + } + + public static void main( + String[] args) + { + runTest(new PGPArmoredTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPClearSignedSignatureTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPClearSignedSignatureTest.java new file mode 100644 index 000000000..ed20127b6 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPClearSignedSignatureTest.java @@ -0,0 +1,454 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SignatureException; +import java.util.Iterator; + +public class PGPClearSignedSignatureTest + extends SimpleTest +{ + byte[] publicKey = Base64.decode( + "mQELBEQh2+wBCAD26kte0hO6flr7Y2aetpPYutHY4qsmDPy+GwmmqVeCDkX+" + + "r1g7DuFbMhVeu0NkKDnVl7GsJ9VarYsFYyqu0NzLa9XS2qlTIkmJV+2/xKa1" + + "tzjn18fT/cnAWL88ZLCOWUr241aPVhLuIc6vpHnySpEMkCh4rvMaimnTrKwO" + + "42kgeDGd5cXfs4J4ovRcTbc4hmU2BRVsRjiYMZWWx0kkyL2zDVyaJSs4yVX7" + + "Jm4/LSR1uC/wDT0IJJuZT/gQPCMJNMEsVCziRgYkAxQK3OWojPSuv4rXpyd4" + + "Gvo6IbvyTgIskfpSkCnQtORNLIudQSuK7pW+LkL62N+ohuKdMvdxauOnAAYp" + + "tBNnZ2dnZ2dnZyA8Z2dnQGdnZ2c+iQE2BBMBAgAgBQJEIdvsAhsDBgsJCAcD" + + "AgQVAggDBBYCAwECHgECF4AACgkQ4M/Ier3f9xagdAf/fbKWBjLQM8xR7JkR" + + "P4ri8YKOQPhK+VrddGUD59/wzVnvaGyl9MZE7TXFUeniQq5iXKnm22EQbYch" + + "v2Jcxyt2H9yptpzyh4tP6tEHl1C887p2J4qe7F2ATua9CzVGwXQSUbKtj2fg" + + "UZP5SsNp25guhPiZdtkf2sHMeiotmykFErzqGMrvOAUThrO63GiYsRk4hF6r" + + "cQ01d+EUVpY/sBcCxgNyOiB7a84sDtrxnX5BTEZDTEj8LvuEyEV3TMUuAjx1" + + "7Eyd+9JtKzwV4v3hlTaWOvGro9nPS7YaPuG+RtufzXCUJPbPfTjTvtGOqvEz" + + "oztls8tuWA0OGHba9XfX9rfgorACAAM="); + + byte[] secretKey = Base64.decode( + "lQOWBEQh2+wBCAD26kte0hO6flr7Y2aetpPYutHY4qsmDPy+GwmmqVeCDkX+" + + "r1g7DuFbMhVeu0NkKDnVl7GsJ9VarYsFYyqu0NzLa9XS2qlTIkmJV+2/xKa1" + + "tzjn18fT/cnAWL88ZLCOWUr241aPVhLuIc6vpHnySpEMkCh4rvMaimnTrKwO" + + "42kgeDGd5cXfs4J4ovRcTbc4hmU2BRVsRjiYMZWWx0kkyL2zDVyaJSs4yVX7" + + "Jm4/LSR1uC/wDT0IJJuZT/gQPCMJNMEsVCziRgYkAxQK3OWojPSuv4rXpyd4" + + "Gvo6IbvyTgIskfpSkCnQtORNLIudQSuK7pW+LkL62N+ohuKdMvdxauOnAAYp" + + "AAf+JCJJeAXEcrTVHotsrRR5idzmg6RK/1MSQUijwPmP7ZGy1BmpAmYUfbxn" + + "B56GvXyFV3Pbj9PgyJZGS7cY+l0BF4ZqN9USiQtC9OEpCVT5LVMCFXC/lahC" + + "/O3EkjQy0CYK+GwyIXa+Flxcr460L/Hvw2ZEXJZ6/aPdiR+DU1l5h99Zw8V1" + + "Y625MpfwN6ufJfqE0HLoqIjlqCfi1iwcKAK2oVx2SwnT1W0NwUUXjagGhD2s" + + "VzJVpLqhlwmS0A+RE9Niqrf80/zwE7QNDF2DtHxmMHJ3RY/pfu5u1rrFg9YE" + + "lmS60mzOe31CaD8Li0k5YCJBPnmvM9mN3/DWWprSZZKtmQQA96C2/VJF5EWm" + + "+/Yxi5J06dG6Bkz311Ui4p2zHm9/4GvTPCIKNpGx9Zn47YFD3tIg3fIBVPOE" + + "ktG38pEPx++dSSFF9Ep5UgmYFNOKNUVq3yGpatBtCQBXb1LQLAMBJCJ5TQmk" + + "68hMOEaqjMHSOa18cS63INgA6okb/ueAKIHxYQcEAP9DaXu5n9dZQw7pshbN" + + "Nu/T5IP0/D/wqM+W5r+j4P1N7PgiAnfKA4JjKrUgl8PGnI2qM/Qu+g3qK++c" + + "F1ESHasnJPjvNvY+cfti06xnJVtCB/EBOA2UZkAr//Tqa76xEwYAWRBnO2Y+" + + "KIVOT+nMiBFkjPTrNAD6fSr1O4aOueBhBAC6aA35IfjC2h5MYk8+Z+S4io2o" + + "mRxUZ/dUuS+kITvWph2e4DT28Xpycpl2n1Pa5dCDO1lRqe/5JnaDYDKqxfmF" + + "5tTG8GR4d4nVawwLlifXH5Ll7t5NcukGNMCsGuQAHMy0QHuAaOvMdLs5kGHn" + + "8VxfKEVKhVrXsvJSwyXXSBtMtUcRtBNnZ2dnZ2dnZyA8Z2dnQGdnZ2c+iQE2" + + "BBMBAgAgBQJEIdvsAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ4M/I" + + "er3f9xagdAf/fbKWBjLQM8xR7JkRP4ri8YKOQPhK+VrddGUD59/wzVnvaGyl" + + "9MZE7TXFUeniQq5iXKnm22EQbYchv2Jcxyt2H9yptpzyh4tP6tEHl1C887p2" + + "J4qe7F2ATua9CzVGwXQSUbKtj2fgUZP5SsNp25guhPiZdtkf2sHMeiotmykF" + + "ErzqGMrvOAUThrO63GiYsRk4hF6rcQ01d+EUVpY/sBcCxgNyOiB7a84sDtrx" + + "nX5BTEZDTEj8LvuEyEV3TMUuAjx17Eyd+9JtKzwV4v3hlTaWOvGro9nPS7Ya" + + "PuG+RtufzXCUJPbPfTjTvtGOqvEzoztls8tuWA0OGHba9XfX9rfgorACAAA="); + + String crOnlyMessage = + "\r" + + " hello world!\r" + + "\r" + + "- dash\r"; + + String nlOnlyMessage = + "\n" + + " hello world!\n" + + "\n" + + "- dash\n"; + + String crNlMessage = + "\r\n" + + " hello world!\r\n" + + "\r\n" + + "- dash\r\n"; + + String crOnlySignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\r" + + "Hash: SHA256\r" + + "\r" + + "\r" + + " hello world!\r" + + "\r" + + "- - dash\r" + + "-----BEGIN PGP SIGNATURE-----\r" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r" + + "\r" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r"; + + + String nlOnlySignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\n" + + "Hash: SHA256\n" + + "\n" + + "\n" + + " hello world!\n" + + "\n" + + "- - dash\n" + + "-----BEGIN PGP SIGNATURE-----\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\n" + + "\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\n" + + "=84Nd\n" + + "-----END PGP SIGNATURE-----\n"; + + String crNlSignedMessage = + "-----BEGIN PGP SIGNED MESSAGE-----\r\n" + + "Hash: SHA256\r\n" + + "\r\n" + + "\r\n" + + " hello world!\r\n" + + "\r\n" + + "- - dash\r\n" + + "-----BEGIN PGP SIGNATURE-----\r\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n" + + "\r\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r\n"; + + String crNlSignedMessageTrailingWhiteSpace = + "-----BEGIN PGP SIGNED MESSAGE-----\r\n" + + "Hash: SHA256\r\n" + + "\r\n" + + "\r\n" + + " hello world! \t\r\n" + + "\r\n" + + "- - dash\r\n" + + "-----BEGIN PGP SIGNATURE-----\r\n" + + "Version: GnuPG v1.4.2.1 (GNU/Linux)\r\n" + + "\r\n" + + "iQEVAwUBRCNS8+DPyHq93/cWAQi6SwgAj3ItmSLr/sd/ixAQLW7/12jzEjfNmFDt\r\n" + + "WOZpJFmXj0fnMzTrOILVnbxHv2Ru+U8Y1K6nhzFSR7d28n31/XGgFtdohDEaFJpx\r\n" + + "Fl+KvASKIonnpEDjFJsPIvT1/G/eCPalwO9IuxaIthmKj0z44SO1VQtmNKxdLAfK\r\n" + + "+xTnXGawXS1WUE4CQGPM45mIGSqXcYrLtJkAg3jtRa8YRUn2d7b2BtmWH+jVaVuC\r\n" + + "hNrXYv7iHFOu25yRWhUQJisvdC13D/gKIPRvARXPgPhAC2kovIy6VS8tDoyG6Hm5\r\n" + + "dMgLEGhmqsgaetVq1ZIuBZj5S4j2apBJCDpF6GBfpBOfwIZs0Tpmlw==\r\n" + + "=84Nd\r" + + "-----END PGP SIGNATURE-----\r\n"; + + public String getName() + { + return "PGPClearSignedSignature"; + } + + private void messageTest( + String message, + String type) + throws Exception + { + ArmoredInputStream aIn = new ArmoredInputStream(new ByteArrayInputStream(message.getBytes())); + + String[] headers = aIn.getArmorHeaders(); + + if (headers == null || headers.length != 1) + { + fail("wrong number of headers found"); + } + + if (!"Hash: SHA256".equals(headers[0])) + { + fail("header value wrong: " + headers[0]); + } + + // + // read the input, making sure we ingore the last newline. + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int ch; + + while ((ch = aIn.read()) >= 0 && aIn.isClearText()) + { + bOut.write((byte)ch); + } + + PGPPublicKeyRingCollection pgpRings = new PGPPublicKeyRingCollection(publicKey); + + PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + + sig.initVerify(pgpRings.getPublicKey(sig.getKeyID()), "SC"); + + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + InputStream sigIn = new ByteArrayInputStream(bOut.toByteArray()); + int lookAhead = readInputLine(lineOut, sigIn); + + processLine(sig, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + sig.update((byte)'\r'); + sig.update((byte)'\n'); + + processLine(sig, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + if (!sig.verify()) + { + fail("signature failed to verify in " + type); + } + } + + private PGPSecretKey readSecretKey( + InputStream in) + throws IOException, PGPException + { + PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(in); + + PGPSecretKey key = null; + + // + // iterate through the key rings. + // + Iterator rIt = pgpSec.getKeyRings(); + + while (key == null && rIt.hasNext()) + { + PGPSecretKeyRing kRing = (PGPSecretKeyRing)rIt.next(); + Iterator kIt = kRing.getSecretKeys(); + + while (key == null && kIt.hasNext()) + { + PGPSecretKey k = (PGPSecretKey)kIt.next(); + + if (k.isSigningKey()) + { + key = k; + } + } + } + + if (key == null) + { + throw new IllegalArgumentException("Can't find signing key in key ring."); + } + + return key; + } + + private void generateTest( + String message, + String type) + throws Exception + { + PGPSecretKey pgpSecKey = readSecretKey(new ByteArrayInputStream(secretKey)); + PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey("".toCharArray(), "SC"); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(pgpSecKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256, "SC"); + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + sGen.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + Iterator it = pgpSecKey.getPublicKey().getUserIDs(); + if (it.hasNext()) + { + spGen.setSignerUserID(false, (String)it.next()); + sGen.setHashedSubpackets(spGen.generate()); + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream aOut = new ArmoredOutputStream(bOut); + ByteArrayInputStream bIn = new ByteArrayInputStream(message.getBytes()); + + aOut.beginClearText(PGPUtil.SHA256); + + // + // note the last \n in the file is ignored + // + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, bIn); + + processLine(aOut, sGen, lineOut.toByteArray()); + + if (lookAhead != -1) + { + do + { + lookAhead = readInputLine(lineOut, lookAhead, bIn); + + sGen.update((byte)'\r'); + sGen.update((byte)'\n'); + + processLine(aOut, sGen, lineOut.toByteArray()); + } + while (lookAhead != -1); + } + + aOut.endClearText(); + + BCPGOutputStream bcpgOut = new BCPGOutputStream(aOut); + + sGen.generate().encode(bcpgOut); + + aOut.close(); + + messageTest(new String(bOut.toByteArray()), type); + } + + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException + { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException + { + bOut.reset(); + + int ch = lookAhead; + + do + { + bOut.write(ch); + if (ch == '\r' || ch == '\n') + { + lookAhead = readPassedEOL(bOut, ch, fIn); + break; + } + } + while ((ch = fIn.read()) >= 0); + + return lookAhead; + } + + private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException + { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') + { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + private static void processLine(PGPSignature sig, byte[] line) + throws SignatureException, IOException + { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sig.update(line, 0, length); + } + } + + private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line) + throws SignatureException, IOException + { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) + { + sGen.update(line, 0, length); + } + + aOut.write(line, 0, line.length); + } + + private static int getLengthWithoutWhiteSpace(byte[] line) + { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) + { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) + { + return b == '\r' || b == '\n' || b == '\t' || b == ' '; + } + + public void performTest() + throws Exception + { + messageTest(crOnlySignedMessage, "\\r"); + messageTest(nlOnlySignedMessage, "\\n"); + messageTest(crNlSignedMessage, "\\r\\n"); + messageTest(crNlSignedMessageTrailingWhiteSpace, "\\r\\n"); + + generateTest(nlOnlyMessage, "\\r"); + generateTest(crOnlyMessage, "\\n"); + generateTest(crNlMessage, "\\r\\n"); + } + + public static void main( + String[] args) + { + runTest(new PGPClearSignedSignatureTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPCompressionTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPCompressionTest.java new file mode 100644 index 000000000..6b884f901 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPCompressionTest.java @@ -0,0 +1,143 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Security; + +public class PGPCompressionTest + extends SimpleTest +{ + public void performTest() + throws Exception + { + testCompression(PGPCompressedData.UNCOMPRESSED); + testCompression(PGPCompressedData.ZIP); + testCompression(PGPCompressedData.ZLIB); + testCompression(PGPCompressedData.BZIP2); + + // + // new style - using stream close + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator cPacket = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + OutputStream out = cPacket.open(new UncloseableOutputStream(bOut), new byte[4]); + + out.write("hello world! !dlrow olleh".getBytes()); + + out.close(); + + validateData(bOut.toByteArray()); + + try + { + out.close(); + cPacket.close(); + } + catch (Exception e) + { + fail("Redundant close() should be ignored"); + } + + // + // new style - using generator close + // + bOut = new ByteArrayOutputStream(); + cPacket = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + out = cPacket.open(new UncloseableOutputStream(bOut), new byte[4]); + + out.write("hello world! !dlrow olleh".getBytes()); + + cPacket.close(); + + validateData(bOut.toByteArray()); + + try + { + out.close(); + cPacket.close(); + } + catch (Exception e) + { + fail("Redundant close() should be ignored"); + } + } + + private void validateData(byte[] data) + throws IOException, PGPException + { + PGPObjectFactory pgpFact = new PGPObjectFactory(data); + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + InputStream pIn = c1.getDataStream(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int ch; + while ((ch = pIn.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), "hello world! !dlrow olleh".getBytes())) + { + fail("compression test failed"); + } + } + + private void testCompression( + int type) + throws IOException, PGPException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPCompressedDataGenerator cPacket = new PGPCompressedDataGenerator(type); + + OutputStream out = cPacket.open(new UncloseableOutputStream(bOut)); + + out.write("hello world!".getBytes()); + + out.close(); + + PGPObjectFactory pgpFact = new PGPObjectFactory(bOut.toByteArray()); + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + InputStream pIn = c1.getDataStream(); + + bOut.reset(); + + int ch; + while ((ch = pIn.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), "hello world!".getBytes())) + { + fail("compression test failed"); + } + } + + public String getName() + { + return "PGPCompressionTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPCompressionTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java new file mode 100644 index 000000000..eba53b9fa --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java @@ -0,0 +1,552 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPDSAElGamalTest + extends SimpleTest +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + public void performTest() + throws Exception + { + try + { + PGPPublicKey pubKey = null; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + if (pubKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PGPPublicKey.DSA, PGPUtil.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(pubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key suitable for encryption + // + long pgpKeyID = 0; + PublicKey pKey = null; + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = pgpKey.getKey("SC"); + pgpKeyID = pgpKey.getKeyID(); + if (pgpKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // verify the key + // + + } + } + + Cipher c = Cipher.getInstance("ElGamal/None/PKCS1Padding", "SC"); + + c.init(Cipher.ENCRYPT_MODE, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.doFinal(in); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + c.init(Cipher.DECRYPT_MODE, pgpPrivKey.getKey()); + + out = c.doFinal(out); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.initVerify(pgpPub.getPublicKey(), "SC"); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.TRIPLE_DES, new SecureRandom(), "SC"); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(puK); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // use of PGPKeyPair + // + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ElGamal", "SC"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + kpg.initialize(elParams); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.ELGAMAL_GENERAL , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + + + // Test bug with ElGamal P size != 0 mod 8 (don't use these sizes at home!) + SecureRandom random = new SecureRandom(); + for (int pSize = 257; pSize < 264; ++pSize) + { + // Generate some parameters of the given size + AlgorithmParameterGenerator a = AlgorithmParameterGenerator.getInstance("ElGamal", "SC"); + a.init(pSize, new SecureRandom()); + AlgorithmParameters params = a.generateParameters(); + + DHParameterSpec elP = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal", "SC"); + + keyGen.initialize(elP); + + + // Run a short encrypt/decrypt test with random key for the given parameters + kp = keyGen.generateKeyPair(); + + PGPKeyPair elGamalKeyPair = new PGPKeyPair( + PublicKeyAlgorithmTags.ELGAMAL_GENERAL, kp, new Date()); + + cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.CAST5, random, "SC"); + + puK = elGamalKeyPair.getPublicKey(); + + cPk.addMethod(puK); + + cbOut = new ByteArrayOutputStream(); + + cOut = cPk.open(cbOut, text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = elGamalKeyPair.getPrivateKey(); + + // Note: This is where an exception would be expected if the P size causes problems + clear = encP.getDataStream(pgpPrivKey, "SC"); + + ByteArrayOutputStream dec = new ByteArrayOutputStream(); + + int b; + while ((b = clear.read()) >= 0) + { + dec.write(b); + } + + byte[] decText = dec.toByteArray(); + + if (!areEqual(text, decText)) + { + fail("decrypted message incorrect"); + } + } + + // check sub key encoding + + it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (!pgpKey.isMasterKey()) + { + byte[] kEnc = pgpKey.getEncoded(); + + PGPObjectFactory objF = new PGPObjectFactory(kEnc); + + PGPPublicKey k = (PGPPublicKey)objF.nextObject(); + + pKey = k.getKey("SC"); + pgpKeyID = k.getKeyID(); + if (k.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + if (objF.nextObject() != null) + { + fail("failed - stream not fully parsed."); + } + } + } + + } + catch (PGPException e) + { + fail("exception: " + e.getMessage(), e.getUnderlyingException()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPDSAElGamalTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSATest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSATest.java new file mode 100644 index 000000000..046b7fdb5 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPDSATest.java @@ -0,0 +1,628 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mQGiBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbLQzRXJpYyBFY2hp" + + "ZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExEC" + + "ABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j9enEyjRDAlwAn2rrom0s" + + "MhufWK5vIRwg7gj5qsLEAJ4vnT5dPBVblofsG+pDoCVeJXGGng=="); + + byte[] testPrivKey = + Base64.decode( + "lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC" + + "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu" + + "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh" + + "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j" + + "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD" + + "4nXkHg=="); + + byte[] testPrivKey2 = + Base64.decode( + "lQHhBEAnoewRBADRvKgDhbV6pMzqYfUgBsLxSHzmycpuxGbjMrpyKHDOEemj" + + "iQb6TyyBKUoR28/pfshFP9R5urtKIT7wjVrDuOkxYkgRhNm+xmPXW2Lw3D++" + + "MQrC5VWe8ywBltz6T9msmChsaKo2hDhIiRI/mg9Q6rH9pJKtVGi4R7CgGxM2" + + "STQ5fwCgub38qGS1W2O4hUsa+3gva5gaNZUEAItegda4/H4t88XdWxW3D8pv" + + "RnFz26/ADdImVaQlBoumD15VmcgYoT1Djizey7X8vfV+pntudESzLbn3GHlI" + + "6C09seH4e8eYP63t7KU/qbUCDomlSswd1OgQ/RxfN86q765K2t3K1i3wDSxe" + + "EgSRyGKee0VNvOBFOFhuWt+patXaBADE1riNkUxg2P4lBNWwu8tEZRmsl/Ys" + + "DBIzXBshoMzZCvS5PnNXMW4G3SAaC9OC9jvKSx9IEWhKjfjs3QcWzXR28mcm" + + "5na0bTxeOMlaPPhBdkTCmFl0IITWlH/pFlR2ah9WYoWYhZEL2tqB82wByzxH" + + "SkSeD9V5oeSCdCcqiqkEmv4DAwLeNsQ2XGJVRmA4lld+CR5vRxpT/+/2xklp" + + "lxVf/nx0+thrHDpro3u/nINIIObk0gh59+zaEEe3APlHqbQVYWFhIGJiYiA8" + + "Y2NjQGRkZC5lZWU+iFoEExECABoFAkAnoewFCwcDAgEDFQIDAxYCAQIeAQIX" + + "gAAKCRA5nBpCS63az85BAKCbPfU8ATrFvkXhzGNGlc1BJo6DWQCgnK125xVK" + + "lWLpt6ZJJ7TXcx3nkm6wAgAAnQFXBEAnoe0QBACsQxPvaeBcv2TkbgU/5Wc/" + + "tO222dPE1mxFbXjGTKfb+6ge96iyD8kTRLrKCkEEeVBa8AZqMSoXUVN6tV8j" + + "/zD8Bc76o5iJ6wgpg3Mmy2GxInVfsfZN6/G3Y2ukmouz+CDNvQdUw8cTguIb" + + "QoV3XhQ03MLbfVmNcHsku9F4CuKNWwADBQP0DSSe8v5PXF9CSCXOIxBDcQ5x" + + "RKjyYOveqoH/4lbOV0YNUbIDZq4RaUdotpADuPREFmWf0zTB6KV/WIiag8XU" + + "WU9zdDvLKR483Bo6Do5pDBcN+NqfQ+ntGY9WJ7BSFnhQ3+07i1K+NsfFTRfv" + + "hf9X3MP75rCf7MxAIWHTabEmUf4DAwLeNsQ2XGJVRmA8DssBUCghogG9n8T3" + + "qfBeKsplGyCcF+JjPeQXkKQaoYGJ0aJz36qFP9d8DuWtT9soQcqIxVf6mTa8" + + "kN1594hGBBgRAgAGBQJAJ6HtAAoJEDmcGkJLrdrPpMkAnRyjQSKugz0YJqOB" + + "yGasMLQLxd2OAKCEIlhtCarlufVQNGZsuWxHVbU8crACAAA="); + + byte[] sig1 = + Base64.decode( + "owGbwMvMwCR4VvnryyOnTJwZ10gncZSkFpfolVSU2Ltz78hIzcnJVyjPL8pJUeTq" + + "sGdmZQCJwpQLMq3ayTA/0Fj3xf4jbwPfK/H3zj55Z9L1n2k/GOapKJrvMZ4tLiCW" + + "GtP/XeDqX4fORDUA"); + + byte[] sig1crc = Base64.decode("OZa/"); + + byte[] testPubWithUserAttr = + Base64.decode( + "mQGiBD2Rqv0RBADqKCkhVEtB/lEEr/9CubuHEy2oN/yU5j+2GXSdcNdVnRI/rwFy" + + "fHEQIk3uU7zHSUKFrC59yDm0sODYyjEdE3BVb0xvEJ5LE/OdndcIMXT1DungZ1vB" + + "zIK/3lr33W/PHixYxv9jduH3WrTehBpiKkgMZp8XloSFj2Cnw9LDyfqB7QCg/8K1" + + "o2k75NkOd9ZjnA9ye7Ri3bEEAKyr61Mo7viPWBK1joWAEsxG0OBWM+iSlG7kwh31" + + "8efgC/7Os6x4Y0jzs8mpcbBjeZtZjS9lRbfp7RinhF269xL0TZ3JxIdtaAV/6yDQ" + + "9NXfZY9dskN++HIR/5GCEEgq/qTJZt6ti5k7aV19ZFfO6wiK3NUy08wOrVsdOkVE" + + "w9IcBADaplhpcel3201uU3OCboogJtw81R5MJMZ4Y9cKL/ca2jGISn0nA7KrAw9v" + + "ShheSixGO4BV9JECkLEbtg7i+W/j/De6S+x2GLNcphuTP3UmgtKbhs0ItRqzW561" + + "s6gLkqi6aWmgaFLd8E1pMJcd9DSY95P13EYB9VJIUxFNUopzo7QcUmFsZiBIYXVz" + + "ZXIgPGhhdXNlckBhY20ub3JnPokAWAQQEQIAGAUCPZGq/QgLAwkIBwIBCgIZAQUb" + + "AwAAAAAKCRAqIBiOh4JvOKg4AJ9j14yygOqqzqiLKeaasIzqT8LCIgCggx14WuLO" + + "wOUTUswTaVKMFnU7tseJAJwEEAECAAYFAj2Rqx8ACgkQ9aWTKMpUDFV+9QP/RiWT" + + "5FAF5Rgb7beaApsgXsME+Pw7HEYFtqGa6VcXEpbcUXO6rjaXsgMgY90klWlWCF1T" + + "HOyKITvj2FdhE+0j8NQn4vaGpiTwORW/zMf/BZ0abdSWQybp10Yjs8gXw30UheO+" + + "F1E524MC+s2AeUi2hwHMiS+AVYd4WhxWHmWuBpTRypP/AAALTgEQAAEBAAAAAQAA" + + "AAABAAAA/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQ" + + "Dg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/" + + "2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7" + + "Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozv/wAARCABqAF0DASIAAhEBAxEB/8QAHwAAAQUB" + + "AQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQID" + + "AAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0" + + "NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKT" + + "lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl" + + "5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL" + + "/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB" + + "CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpj" + + "ZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3" + + "uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIR" + + "AxEAPwD2aiiq9xcxWsRllcKqjOT06E/0oAsVm6jrmm6VGXvLuOPGflz8x+grzXxV" + + "8U51u5LXRgBGowZHXknnkc9OQcV51caneXdw9xPOXlckl2AJHY4J6cD1oA9J1z4p" + + "TRkrYQhRyQ0hIY5/2QRx7k9ulczN8SvEEshdZkX0UorDrznI759a5Mksckkknqec" + + "mkoA7WD4oavEoEttbTepYEZ+mCMVv6H8SLTULhbe/gFozAYkD5Unp3Ax/kV5XRQB" + + "9EAhgCDkHkEcgilryTwd4zn0m4WzvpTJZSMBuY5MfbueletKyugZWDKwyCOc/j3o" + + "AduyWLDeWB5Ynj8jSUUUAdFXn/xU15dO0RbGGYC5uWwUB6L1Jx+n413F1cJa2stz" + + "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" + + "7DGrzw=="); + + byte[] aesSecretKey = Base64.decode( + "lQHpBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyv4JAwKyWlhdblPudWBFXNkW5ydKn0AV2f51wEtj" + + "Zy0aLIeutVMSJf1ytLqjFqrnFe6pdJrHO3G00TE8OuFhftWosLGLbEGytDtF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gQUVTMjU2KSA8ZXJpY0Bib3Vu" + + "Y3ljYXN0bGUub3JnPohZBBMRAgAZBQJAUnSGBAsHAwIDFQIDAxYCAQIeAQIX" + + "gAAKCRBYt1NnUiCgeFKaAKCiqtOO+NQES1gJW6XuOGmSkXt8bQCfcuW7SXZH" + + "zxK1FfdcG2HEDs3YEVawAgAA"); + + byte[] aesPublicKey = Base64.decode( + "mQGiBEBSdIYRBADpd7MeIxRk4RsvyMnJNIYe4FiVv6i7I7+LPRvnIjDct0bN" + + "1gCV48QFej7g/PsvXRjYSowV3VIvchWX8OERd/5i10cLbcs7X52EP1vwYaLj" + + "uRfNUBg8Q51RQsKR+/rBmnVsi68rjU4yTH6wpo6FOO4pz4wFV+tWwGOwOitA" + + "K31L4wCgqh59eFFBrOlRFAbDvaL7emoCIR8EAOLxDKiLQJYQrKZfXdZnifeo" + + "dhEP0uuV4O5TG6nrqkhWffzC9cSoFD0BhMl979d8IB2Uft4FNvQc2u8hbJL5" + + "7OCGDCUAidlB9jSdu0/J+kfRaTGhYDjBgw7AA42576BBSMNouJg/aOOQENEN" + + "Nn4n7NxR3viBzIsL/OIeU8HSkBgaA/41PsvcgZ3kwpdltJ/FVRWhmMmv/q/X" + + "qp1YOnF8xPU9bv2ofELrxJfRsbS4GW1etzD+nXs/woW4Vfixs01x+cutR4iF" + + "3hw+eU+yLToMPmmo8D2LUvX1SRODJpx5yBBeRIYv6nz9H3sQRDx3kaLASxDV" + + "jTxKmrLYnZz5w5qyVpvRyrQ7RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IEFFUzI1NikgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ0" + + "hgQLBwMCAxUCAwMWAgECHgECF4AACgkQWLdTZ1IgoHhSmgCfU83BLBF2nCua" + + "zk2dXB9zO1l6XS8AnA07U4cq5W0GrKM6/kP9HWtPhgOFsAIAAA=="); + + byte[] twofishSecretKey = Base64.decode( + "lQHpBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV/4KAwLcGrxT3X0qR2CZyZYSVBdjXeNYKXuGBtOf" + + "ood26WOtwLw4+l9sHVoiXNv0LomkO58ndJRPGCeZWZEDMVrfkS7rcOlktDxF" + + "cmljIEguIEVjaGlkbmEgKHRlc3Qga2V5IC0gdHdvZmlzaCkgPGVyaWNAYm91" + + "bmN5Y2FzdGxlLm9yZz6IWQQTEQIAGQUCQFJ20gQLBwMCAxUCAwMWAgECHgEC" + + "F4AACgkQaCCMaHh9zR2+RQCghcQwlt4B4YmNxp2b3v6rP3E8M0kAn2Gspi4u" + + "A/ynoqnC1O8HNlbjPdlVsAIAAA=="); + + byte[] twofishPublicKey = Base64.decode( + "mQGiBEBSdtIRBACf7WfrqTl8F051+EbaljPf/8/ajFpAfMq/7p3Hri8OCsuc" + + "fJJIufEEOV1/Lt/wkN67MmSyrU0fUCsRbEckRiB4EJ0zGHVFfAnku2lzdgc8" + + "AVounqcHOmqA/gliFDEnhYOx3bOIAOav+yiOqfKVBhWRCpFdOTE+w/XoDM+p" + + "p8bH5wCgmP2FuWpzfSut7GVKp51xNEBRNuED/3t2Q+Mq834FVynmLKEmeXB/" + + "qtIz5reHEQR8eMogsOoJS3bXs6v3Oblj4in1gLyTVfcID5tku6kLP20xMRM2" + + "zx2oRbz7TyOCrs15IpRXyqqJxUWD8ipgJPkPXE7hK8dh4YSTUi4i5a1ug8xG" + + "314twlPzrchpWZiutDvZ+ks1rzOtBACHrEFG2frUu+qVkL43tySE0cV2bnuK" + + "LVhXbpzF3Qdkfxou2nuzsCbl6m87OWocJX8uYcQGlHLKv8Q2cfxZyieLFg6v" + + "06LSFdE9drGBWz7mbrT4OJjxPyvnkffPfLOOqae3PMYIIuscvswuhm4X5aoj" + + "KJs01YT3L6f0iIj03hCeV7Q8RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSAt" + + "IHR3b2Zpc2gpIDxlcmljQGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkFAkBS" + + "dtIECwcDAgMVAgMDFgIBAh4BAheAAAoJEGggjGh4fc0dvkUAn2QGdNk8Wrrd" + + "+DvKECrO5+yoPRx3AJ91DhCMme6uMrQorKSDYxHlgc7iT7ACAAA="); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Generated signature test + * + * @param sKey + * @param pgpPrivKey + */ + public void generateTest( + PGPSecretKeyRing sKey, + PGPPublicKey pgpPubKey, + PGPPrivateKey pgpPrivKey) + throws Exception + { + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + Iterator it = sKey.getSecretKey().getPublicKey().getUserIDs(); + String primaryUserID = (String)it.next(); + + spGen.setSignerUserID(true, primaryUserID); + + sGen.setHashedSubpackets(spGen.generate()); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + PGPObjectFactory pgpFact = new PGPObjectFactory(bOut.toByteArray()); + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(pgpPubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + } + + public void performTest() + throws Exception + { + String file = null; + KeyFactory fact = KeyFactory.getInstance("DSA", "SC"); + PGPPublicKey pubKey = null; + PrivateKey privKey = null; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey); + + pubKey = pgpPub.getPublicKey(); + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKey); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + // + // test signature message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + int ch; + + ops.initVerify(pubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + // + // signature generation + // + generateTest(sKey, pubKey, pgpPrivKey); + + // + // signature generation - canonical text + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PGPPublicKey.DSA, PGPUtil.SHA1, "SC"); + + sGen.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.TEXT, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature - canconical text + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.initVerify(pubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // Read the public key with user attributes + // + pgpPub = new PGPPublicKeyRing(testPubWithUserAttr); + + pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + sigs.next(); + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("Failed user attributes check"); + } + + byte[] pgpPubBytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(pgpPubBytes); + + pubKey = pgpPub.getPublicKey(); + + it = pubKey.getUserAttributes(); + count = 0; + while (it.hasNext()) + { + it.next(); + count++; + } + + if (count != 1) + { + fail("Failed user attributes reread"); + } + + // + // reading test extra data - key with edge condition for DSA key password. + // + char [] passPhrase = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + + sKey = new PGPSecretKeyRing(testPrivKey2); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(passPhrase, "SC"); + + byte[] bytes = pgpPrivKey.getKey().getEncoded(); + + // + // reading test - aes256 encrypted passphrase. + // + sKey = new PGPSecretKeyRing(aesSecretKey); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + bytes = pgpPrivKey.getKey().getEncoded(); + + // + // reading test - twofish encrypted passphrase. + // + sKey = new PGPSecretKeyRing(twofishSecretKey); + pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + bytes = pgpPrivKey.getKey().getEncoded(); + + // + // use of PGPKeyPair + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", "SC"); + + kpg.initialize(512); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.DSA , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + } + + public String getName() + { + return "PGPDSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDHTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDHTest.java new file mode 100644 index 000000000..d3f35cf93 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDHTest.java @@ -0,0 +1,313 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.security.spec.ECGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPECDHTest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB" + + "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l" + + "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV" + + "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi" + + "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr" + + "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy" + + "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI" + + "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS" + + "dmwmbysD192L3z4A/ReXEa0gtv8OFWjuALD1ovEK8TpDORLUb6IuUb5jUIzY"); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKs/gcDAo11YYCae/K2" + + "1uKGJ/uU4b4QHYnPIsAdYpuo5HIdoAOL/WwduRa8C6vSFrtMJLDqPK3BUpMz3CXN" + + "GyMhjuaHKP5MPbBZkIfgUGZO5qvU9+i0UFRlc3QgRUNEU0EtRUNESCAoS2V5IGFu" + + "ZCBzdWJrZXkgYXJlIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhLmVjZGhAZXhh" + + "bXBsZS5jb20+iHoEExMIACIFAlG+BsACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B" + + "AheAAAoJEPfAOVaMWj1Tk7YBAOLz007oltog5sLreMwxayIBpAesEI+QNkuJuA2/" + + "vcMuAP99qi4fhhkmz5w41AE9Wc/VtobU2lZHmmk3clQsVyuFyg=="); + + byte[] testMessage = + Base64.decode( + "hH4Dp5+FdoujIBwSAgMErx4BSvgXY3irwthgxU8zPoAoR+8rhmxdpwbw6ZJAO2GX" + + "azWJ85JNcobHKDeGeUq6wkTFu+g6yG99gIX8J5xJAjBRhyCRcaFgwbdDV4orWTe3" + + "iewiT8qs4BQ23e0c8t+thdKoK4thMsCJy7wSKqY0sJTSVAELroNbCOi2lcO15YmW" + + "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" + + "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg=="); + + private void generate() + throws Exception + { + // + // Generate a master key + // + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(new ECGenParameterSpec("P-256")); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // Generate an encryption key + // + keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(new ECGenParameterSpec("P-256")); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, + new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), + new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + keyRingGen.addSubKey(ecdhKeyPair); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + // TODO: add check of KdfParameters + doBasicKeyRingCheck(pubRing); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + private void testDecrypt(PGPSecretKeyRing secretKeyRing) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(testMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + PGPSecretKey secretKey = secretKeyRing.getSecretKey(); // secretKeyRing.getSecretKey(encP.getKeyID()); +// +// PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey()extractPrivateKey(null); +// +// clear = encP.getDataStream(pgpPrivKey, "SC"); +// +// bOut.reset(); +// +// while ((ch = clear.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// out = bOut.toByteArray(); +// +// if (!areEqual(out, text)) +// { +// fail("wrong plain text in generated packet"); +// } + } + + private void encryptDecryptTest() + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(new ECGenParameterSpec("P-256")); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + ByteArrayOutputStream ldOut = new ByteArrayOutputStream(); + OutputStream pOut = lData.open(ldOut, PGPLiteralDataGenerator.UTF8, PGPLiteralData.CONSOLE, text.length, new Date()); + + pOut.write(text); + + pOut.close(); + + byte[] data = ldOut.toByteArray(); + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("SC").setSecureRandom(new SecureRandom())); + + cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(ecdhKeyPair.getPublicKey()).setProvider("SC")); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), data.length); + + cOut.write(data); + + cOut.close(); + + PGPObjectFactory pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(ecdhKeyPair.getPrivateKey())); + + pgpF = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpF.nextObject(); + + clear = ld.getInputStream(); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int ch; + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + byte[] out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + doBasicKeyRingCheck(pubKeyRing); + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + testDecrypt(secretKeyRing); + + encryptDecryptTest(); + + generate(); + } + + private void doBasicKeyRingCheck(PGPPublicKeyRing pubKeyRing) + throws PGPException, SignatureException + { + for (Iterator it = pubKeyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + if (pubKey.isMasterKey()) + { + if (pubKey.isEncryptionKey()) + { + fail("master key showed as encryption key!"); + } + } + else + { + if (!pubKey.isEncryptionKey()) + { + fail("sub key not encryption key!"); + } + + for (Iterator sigIt = pubKeyRing.getPublicKey().getSignatures(); sigIt.hasNext();) + { + PGPSignature certification = (PGPSignature)sigIt.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("subkey certification does not verify"); + } + } + } + } + } + + public String getName() + { + return "PGPECDHTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDHTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDSATest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDSATest.java new file mode 100644 index 000000000..f09a809e3 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPECDSATest.java @@ -0,0 +1,159 @@ +package org.spongycastle.openpgp.test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Security; +import java.security.spec.ECGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; + +public class PGPECDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8FetDVFQ0RTQSAoS2V5" + + "IGlzIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhQGV4YW1wbGUuY29tPoh6BBMT" + + "CAAiBQJRvgeoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDqO46kgPLi" + + "vN1hAP4n0UApR36ziS5D8KUt7wEpBujQE4G3+efATJ+DMmY/SgEA+wbdDynFf/V8" + + "pQs0+FtCYQ9schzIur+peRvol7OrNnc="); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8Fe/gcDAqTWSUiFpEno" + + "1n8izmLaWTy8GYw5/lK4R2t6D347YGgTtIiXfoNPOcosmU+3OibyTm2hc/WyG4fL" + + "a0nxFtj02j0Bt/Fw0N4VCKJwKL/QJT+0NUVDRFNBIChLZXkgaXMgMjU2IGJpdHMg" + + "bG9uZykgPHRlc3QuZWNkc2FAZXhhbXBsZS5jb20+iHoEExMIACIFAlG+B6gCGwMG" + + "CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOo7jqSA8uK83WEA/ifRQClHfrOJ" + + "LkPwpS3vASkG6NATgbf558BMn4MyZj9KAQD7Bt0PKcV/9XylCzT4W0JhD2xyHMi6" + + "v6l5G+iXs6s2dw=="); + + private void generateAndSign() + throws Exception + { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(new ECGenParameterSpec("P-256")); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // try a signature + // + PGPSignatureGenerator signGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PGPPublicKey.ECDSA, HashAlgorithmTags.SHA256).setProvider("SC")); + + signGen.init(PGPSignature.BINARY_DOCUMENT, ecdsaKeyPair.getPrivateKey()); + + signGen.update("hello world!".getBytes()); + + PGPSignature sig = signGen.generate(); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), ecdsaKeyPair.getPublicKey()); + + sig.update("hello world!".getBytes()); + + if (!sig.verify()) + { + fail("signature failed to verify!"); + } + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + for (Iterator it = pubKeyRing.getPublicKey().getSignatures(); it.hasNext();) + { + PGPSignature certification = (PGPSignature)it.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("self certification does not verify"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + + generateAndSign(); + } + + public String getName() + { + return "PGPECDSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPKeyRingTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPKeyRingTest.java new file mode 100644 index 000000000..d22644df4 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPKeyRingTest.java @@ -0,0 +1,2674 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; + +public class PGPKeyRingTest + extends SimpleTest +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + char[] sec5pass1 = "12345678".toCharArray(); + + // + // Werner Koch "odd keys" + // + byte[] pub6 = Base64.decode( + "mQGiBDWiHh4RBAD+l0rg5p9rW4M3sKvmeyzhs2mDxhRKDTVVUnTwpMIR2kIA9pT4" + + "3No/coPajDvhZTaDM/vSz25IZDZWJ7gEu86RpoEdtr/eK8GuDcgsWvFs5+YpCDwW" + + "G2dx39ME7DN+SRvEE1xUm4E9G2Nnd2UNtLgg82wgi/ZK4Ih9CYDyo0a9awCgisn3" + + "RvZ/MREJmQq1+SjJgDx+c2sEAOEnxGYisqIKcOTdPOTTie7o7x+nem2uac7uOW68" + + "N+wRWxhGPIxsOdueMIa7U94Wg/Ydn4f2WngJpBvKNaHYmW8j1Q5zvZXXpIWRXSvy" + + "TR641BceGHNdYiR/PiDBJsGQ3ac7n7pwhV4qex3IViRDJWz5Dzr88x+Oju63KtxY" + + "urUIBACi7d1rUlHr4ok7iBRlWHYXU2hpUIQ8C+UOE1XXT+HB7mZLSRONQnWMyXnq" + + "bAAW+EUUX2xpb54CevAg4eOilt0es8GZMmU6c0wdUsnMWWqOKHBFFlDIvyI27aZ9" + + "quf0yvby63kFCanQKc0QnqGXQKzuXbFqBYW2UQrYgjXji8rd8bQnV2VybmVyIEtv" + + "Y2ggKGdudXBnIHNpZykgPGRkOWpuQGdudS5vcmc+iGUEExECAB0FAjZVoKYFCQht" + + "DIgDCwQDBRUDAgYBAxYCAQIXgAASCRBot6uJV1SNzQdlR1BHAAEBLj4AoId15gcy" + + "YpBX2YLtEQTlXPp3mtEGAJ9UxzJE/t3EHCHK2bAIOkBwIW8ItIkBXwMFEDWiHkMD" + + "bxG4/z6qCxADYzIFHR6I9Si9gzPQNRcFs2znrTp5pV5Mk6f1aqRgZxL3E4qUZ3xe" + + "PQhwAo3fSy3kCwLmFGqvzautSMHn8K5V1u+T5CSHqLFYKqj5FGtuB/xwoKDXH6UO" + + "P0+l5IP8H1RTjme3Fhqahec+zPG3NT57vc2Ru2t6PmuAwry2BMuSFMBs7wzXkyC3" + + "DbI54MV+IKPjHMORivK8uI8jmna9hdNVyBifCk1GcxkHBSCFvU8xJePsA/Q//zCe" + + "lvrnrIiMfY4CQTmKzke9MSzbAZQIRddgrGAsiX1tE8Z3YMd8lDpuujHLVEdWZo6s" + + "54OJuynHrtFFObdapu0uIrT+dEXSASMUbEuNCLL3aCnrEtGJCwxB2TPQvCCvR2BK" + + "zol6MGWxA+nmddeQib2r+GXoKXLdnHcpsAjA7lkXk3IFyJ7MLFK6uDrjGbGJs2FK" + + "SduUjS/Ib4hGBBARAgAGBQI1oic8AAoJEGx+4bhiHMATftYAn1fOaKDUOt+dS38r" + + "B+CJ2Q+iElWJAKDRPpp8q5GylbM8DPlMpClWN3TYqYhGBBARAgAGBQI27U5sAAoJ" + + "EF3iSZZbA1iiarYAn35qU3ZOlVECELE/3V6q98Q30eAaAKCtO+lacH0Qq1E6v4BP" + + "/9y6MoLIhohiBBMRAgAiAhsDBAsHAwIDFQIDAxYCAQIeAQIXgAUCP+mCaQUJDDMj" + + "ywAKCRBot6uJV1SNzaLvAJwLsPV1yfc2D+yT+2W11H/ftNMDvwCbBweORhCb/O/E" + + "Okg2UTXJBR4ekoCIXQQTEQIAHQMLBAMFFQMCBgEDFgIBAheABQI/6YJzBQkMMyPL" + + "AAoJEGi3q4lXVI3NgroAn2Z+4KgVo2nzW72TgCJwkAP0cOc2AJ0ZMilsOWmxmEG6" + + "B4sHMLkB4ir4GIhdBBMRAgAdAwsEAwUVAwIGAQMWAgECF4AFAj/pgnMFCQwzI8sA" + + "CgkQaLeriVdUjc2CugCfRrOIfllp3mSmGpHgIxvg5V8vtMcAn0BvKVehOn+12Yvn" + + "9BCHfg34jUZbiF0EExECAB0DCwQDBRUDAgYBAxYCAQIXgAUCP+mCcwUJDDMjywAK" + + "CRBot6uJV1SNzYK6AJ9x7R+daNIjkieNW6lJeVUIoj1UHgCeLZm025uULML/5DFs" + + "4tUvXs8n9XiZAaIENaIg8xEEALYPe0XNsPjx+inTQ+Izz527ZJnoc6BhWik/4a2b" + + "ZYENSOQXAMKTDQMv2lLeI0i6ceB967MNubhHeVdNeOWYHFSM1UGRfhmZERISho3b" + + "p+wVZvVG8GBVwpw34PJjgYU/0tDwnJaJ8BzX6j0ecTSTjQPnaUEtdJ/u/gmG9j02" + + "18TzAKDihdNoKJEU9IKUiSjdGomSuem/VwQArHfaucSiDmY8+zyZbVLLnK6UJMqt" + + "sIv1LvAg20xwXoUk2bY8H3tXL4UZ8YcoSXYozwALq3cIo5UZJ0q9Of71mI8WLK2i" + + "FSYVplpTX0WMClAdkGt3HgVb7xtOhGt1mEKeRQjNZ2LteUQrRDD9MTQ+XxcvEN0I" + + "pAj4kBJe9bR6HzAD/iecCmGwSlHUZZrgqWzv78o79XxDdcuLdl4i2fL7kwEOf9js" + + "De7hGs27yrdJEmAG9QF9TOF9LJFmE1CqkgW+EpKxsY01Wjm0BFJB1R7iPUaUtFRZ" + + "xYqfgXarmPjql2iBi+cVjLzGu+4BSojVAPgP/hhcnIowf4M4edPiICMP1GVjtCFX" + + "ZXJuZXIgS29jaCA8d2VybmVyLmtvY2hAZ3V1Zy5kZT6IYwQTEQIAGwUCNs8JNwUJ" + + "CCCxRAMLCgMDFQMCAxYCAQIXgAASCRBsfuG4YhzAEwdlR1BHAAEBaSAAn3YkpT5h" + + "xgehGFfnX7izd+c8jI0SAJ9qJZ6jJvXnGB07p60aIPYxgJbLmYkAdQMFEDWjdxQd" + + "GfTBDJhXpQEBPfMC/0cxo+4xYVAplFO0nIYyjQgP7D8O0ufzPsIwF3kvb7b5FNNj" + + "fp+DAhN6G0HOIgkL3GsWtCfH5UHali+mtNFIKDpTtr+F/lPpZP3OPzzsLZS4hYTq" + + "mMs1O/ACq8axKgAilYkBXwMFEDWiJw4DbxG4/z6qCxADB9wFH0i6mmn6rWYKFepJ" + + "hXyhE4wWqRPJAnvfoiWUntDp4aIQys6lORigVXIWo4k4SK/FH59YnzF7578qrTZW" + + "/RcA0bIqJqzqaqsOdTYEFa49cCjvLnBW4OebJlLTUs/nnmU0FWKW8OwwL+pCu8d7" + + "fLSSnggBsrUQwbepuw0cJoctFPAz5T1nQJieQKVsHaCNwL2du0XefOgF5ujB1jK1" + + "q3p4UysF9hEcBR9ltE3THr+iv4jtZXmC1P4at9W5LFWsYuwr0U3yJcaKSKp0v/wG" + + "EWe2J/gFQZ0hB1+35RrCZPgiWsEv87CHaG6XtQ+3HhirBCJsYhmOikVKoEan6PhU" + + "VR1qlXEytpAt389TBnvyceAX8hcHOE3diuGvILEgYes3gw3s5ZmM7bUX3jm2BrX8" + + "WchexUFUQIuKW2cL379MFXR8TbxpVxrsRYE/4jHZBYhGBBARAgAGBQI27U4LAAoJ" + + "EF3iSZZbA1iifJoAoLEsGy16hV/CfmDku6D1CBUIxXvpAJ9GBApdC/3OXig7sBrV" + + "CWOb3MQzcLkBjQQ2zwcIEAYA9zWEKm5eZpMMBRsipL0IUeSKEyeKUjABX4vYNurl" + + "44+2h6Y8rHn7rG1l/PNj39UJXBkLFj1jk8Q32v+3BQDjvwv8U5e/kTgGlf7hH3WS" + + "W38RkZw18OXYCvnoWkYneIuDj6/HH2bVNXmTac05RkBUPUv4yhqlaFpkVcswKGuE" + + "NRxujv/UWvVF+/2P8uSQgkmGp/cbwfMTkC8JBVLLBRrJhl1uap2JjZuSVklUUBez" + + "Vf3NJMagVzx47HPqLVl4yr4bAAMGBf9PujlH5I5OUnvZpz+DXbV/WQVfV1tGRCra" + + "kIj3mpN6GnUDF1LAbe6vayUUJ+LxkM1SqQVcmuy/maHXJ+qrvNLlPqUZPmU5cINl" + + "sA7bCo1ljVUp54J1y8PZUx6HxfEl/LzLVkr+ITWnyqeiRikDecUf4kix2teTlx6I" + + "3ecqT5oNqZSRXWwnN4SbkXtAd7rSgEptUYhQXgSEarp1pXJ4J4rgqFa49jKISDJq" + + "rn/ElltHe5Fx1bpfkCIYlYk45Cga9bOIVAQYEQIADAUCNs8HCAUJBvPJAAASCRBs" + + "fuG4YhzAEwdlR1BHAAEBeRUAoIGpCDmMy195TatlloHAJEjZu5KaAJwOvW989hOb" + + "8cg924YIFVA1+4/Ia7kBjQQ1oiE8FAYAkQmAlOXixb8wra83rE1i7LCENLzlvBZW" + + "KBXN4ONelZAnnkOm7IqRjMhtKRJN75zqVyKUaUwDKjpf9J5K2t75mSxBtnbNRqL3" + + "XodjHK93OcAUkz3ci7iuC/b24JI2q4XeQG/v4YR1VodM0zEQ1IC0JCq4Pl39QZyX" + + "JdZCrUFvMcXq5ruNSldztBqTFFUiFbkw1Fug/ZyXJve2FVcbsRXFrB7EEuy+iiU/" + + "kZ/NViKk0L4T6KRHVsEiriNlCiibW19fAAMFBf9Tbv67KFMDrLqQan/0oSSodjDQ" + + "KDGqtoh7KQYIKPXqfqT8ced9yd5MLFwPKf3t7AWG1ucW2x118ANYkPSU122UTndP" + + "sax0cY4XkaHxaNwpNFCotGQ0URShxKNpcqbdfvy+1d8ppEavgOyxnV1JOkLjZJLw" + + "K8bgxFdbPWcsJJnjuuH3Pwz87CzTgOSYQxMPnIwQcx5buZIV5NeELJtcbbd3RVua" + + "K/GQht8QJpuXSji8Nl1FihYDjACR8TaRlAh50GmIRgQoEQIABgUCOCv7gwAKCRBs" + + "fuG4YhzAE9hTAJ9cRHu+7q2hkxpFfnok4mRisofCTgCgzoPjNIuYiiV6+wLB5o11" + + "7MNWPZCIVAQYEQIADAUCNaIhPAUJB4TOAAASCRBsfuG4YhzAEwdlR1BHAAEBDfUA" + + "oLstR8cg5QtHwSQ3nFCOKEREUFIwAKDID3K3hM+b6jW1o+tNX9dnjb+YMZkAbQIw" + + "bYOUAAABAwC7ltmO5vdKssohwzXEZeYvDW2ll3CYD2I+ruiNq0ybxkfFBopq9cxt" + + "a0OvVML4LK/TH+60f/Fqx9wg2yk9APXyaomdLrXfWyfZ91YtNCfj3ElC4XB4qqm0" + + "HRn0wQyYV6UABRG0IVdlcm5lciBLb2NoIDx3ZXJuZXIua29jaEBndXVnLmRlPokA" + + "lQMFEDRfoOmOB31Gi6BmjQEBzwgD/2fHcdDXuRRY+SHvIVESweijstB+2/sVRp+F" + + "CDjR74Kg576sJHfTJCxtSSmzpaVpelb5z4URGJ/Byi5L9AU7hC75S1ZnJ+MjBT6V" + + "ePyk/r0uBrMkU/lMG7lk/y2By3Hll+edjzJsdwn6aoNPiyen4Ch4UGTEguxYsLq0" + + "HES/UvojiQEVAwUTNECE2gnp+QqKck5FAQH+1Af/QMlYPlLG+5E19qP6AilKQUzN" + + "kd1TWMenXTS66hGIVwkLVQDi6RCimhnLMq/F7ENA8bSbyyMuncaBz5dH4kjfiDp1" + + "o64LULcTmN1LW9ctpTAIeLLJZnwxoJLkUbLUYKADKqIBXHMt2B0zRmhFOqEjRN+P" + + "hI7XCcHeHWHiDeUB58QKMyeoJ/QG/7zLwnNgDN2PVqq2E72C3ye5FOkYLcHfWKyB" + + "Rrn6BdUphAB0LxZujSGk8ohZFbia+zxpWdE8xSBhZbjVGlwLurmS2UTjjxByBNih" + + "eUD6IC3u5P6psld0OfqnpriZofP0CBP2oTk65r529f/1lsy2kfWrVPYIFJXEnIkA" + + "lQMFEDQyneGkWMS9SnJfMQEBMBMD/1ADuhhuY9kyN7Oj6DPrDt5SpPQDGS0Jtw3y" + + "uIPoed+xyzlrEuL2HeaOj1O9urpn8XLN7V21ajkzlqsxnGkOuifbE9UT67o2b2vC" + + "ldCcY4nV5n+U1snMDwNv+RkcEgNa8ANiWkm03UItd7/FpHDQP0FIgbPEPwRoBN87" + + "I4gaebfRiQCVAwUQNDUSwxRNm5Suj3z1AQGMTAP/UaXXMhPzcjjLxBW0AccTdHUt" + + "Li+K+rS5PNxxef2nnasEhCdK4GkM9nwJgsP0EZxCG3ZSAIlWIgQ3MK3ZAV1Au5pL" + + "KolRjFyEZF420wAtiE7V+4lw3FCqNoXDJEFC3BW431kx1wAhDk9VaIHHadYcof4d" + + "dmMLQOW2cJ7LDEEBW/WJAJUDBRA0M/VQImbGhU33abUBARcoA/9eerDBZGPCuGyE" + + "mQBcr24KPJHWv/EZIKl5DM/Ynz1YZZbzLcvEFww34mvY0jCfoVcCKIeFFBMKiSKr" + + "OMtoVC6cQMKpmhE9hYRStw4E0bcf0BD/stepdVtpwRnG8SDP2ZbmtgyjYT/7T4Yt" + + "6/0f6N/0NC7E9qfq4ZlpU3uCGGu/44kAlQMFEDQz8kp2sPVxuCQEdQEBc5YD/Rix" + + "vFcLTO1HznbblrO0WMzQc+R4qQ50CmCpWcFMwvVeQHo/bxoxGggNMmuVT0bqf7Mo" + + "lZDSJNS96IAN32uf25tYHgERnQaMhmi1aSHvRDh4jxFu8gGVgL6lWit/vBDW/BiF" + + "BCH6sZJJrGSuSdpecTtaWC8OJGDoKTO9PqAA/HQRiQB1AwUQNDJSx011eFs7VOAZ" + + "AQGdKQL/ea3qD2OP3wVTzXvfjQL1CosX4wyKusBBhdt9u2vOT+KWkiRk1o35nIOG" + + "uZLHtSFQDY8CVDOkqg6g4sVbOcTl8QUwHA+A4AVDInwTm1m4Bk4oeCIwk4Bp6mDd" + + "W11g28k/iQEVAgUSNDIWPm/Y4wPDeaMxAQGvBQgAqGhzA/21K7oL/L5S5Xz//eO7" + + "J8hgvqqGXWd13drNy3bHbKPn7TxilkA3ca24st+6YPZDdSUHLMCqg16YOMyQF8gE" + + "kX7ZHWPacVoUpCmSz1uQ3p6W3+u5UCkRpgQN8wBbJx5ZpBBqeq5q/31okaoNjzA2" + + "ghEWyR5Ll+U0C87MY7pc7PlNHGCr0ZNOhhtf1jU+H9ag5UyT6exIYim3QqWYruiC" + + "LSUcim0l3wK7LMW1w/7Q6cWfAFQvl3rGjt3rg6OWg9J4H2h5ukf5JNiRybkupmat" + + "UM+OVMRkf93jzU62kbyZpJBHiQZuxxJaLkhpv2RgWib9pbkftwEy/ZnmjkxlIIkA" + + "lQMFEDQvWjh4313xYR8/NQEB37QEAIi9vR9h9ennz8Vi7RNU413h1ZoZjxfEbOpk" + + "QAjE/LrZ/L5WiWdoStSiyqCLPoyPpQafiU8nTOr1KmY4RgceJNgxIW4OiSMoSvrh" + + "c2kqP+skb8A2B4+47Aqjr5fSAVfVfrDMqDGireOguhQ/hf9BOYsM0gs+ROdtyLWP" + + "tMjRnFlviD8DBRAz8qQSj6lRT5YOKXIRAntSAJ9StSEMBoFvk8iRWpXb6+LDNLUW" + + "zACfT8iY3IxwvMF6jjCHrbuxQkL7chSJARUDBRA0MMO7569NIyeqD3EBATIAB/4t" + + "CPZ1sLWO07g2ZCpiP1HlYpf5PENaXtaasFvhWch7eUe3DksuMEPzB5GnauoQZAku" + + "hEGkoEfrfL3AXtXH+WMm2t7dIcTBD4p3XkeZ+PgJpKiASXDyul9rumXXvMxSL4KV" + + "7ar+F1ZJ0ycCx2r2au0prPao70hDAzLTy16hrWgvdHSK7+wwaYO5TPCL5JDmcB+d" + + "HKW72qNUOD0pxbe0uCkkb+gDxeVX28pZEkIIOMMV/eAs5bs/smV+eJqWT/EyfVBD" + + "o7heF2aeyJj5ecxNOODr88xKF7qEpqazCQ4xhvFY+Yn6+vNCcYfkoZbOn0XQAvqf" + + "a2Vab9woVIVSaDji/mlPiQB1AwUQNDC233FfeD4HYGBJAQFh6QL/XCgm5O3q9kWp" + + "gts1MHKoHoh7vxSSQGSP2k7flNP1UB2nv4sKvyGM8eJKApuROIodcTkccM4qXaBu" + + "XunMr5kJlvDJPm+NLzKyhtQP2fWI7xGYwiCiB29gm1GFMjdur4amiQEVAwUQNDBR" + + "9fjDdqGixRdJAQE+mAf+JyqJZEVFwNwZ2hSIMewekC1r7N97p924nqfZKnzn6weF" + + "pE80KIJSWtEVzI0XvHlVCOnS+WRxn7zxwrOTbrcEOy0goVbNgUsP5ypZa2/EM546" + + "uyyJTvgD0nwA45Q4bP5sGhjh0G63r9Vwov7itFe4RDBGM8ibGnZTr9hHo469jpom" + + "HSNeavcaUYyEqcr4GbpQmdpJTnn/H0A+fMl7ZHRoaclNx9ZksxihuCRrkQvUOb3u" + + "RD9lFIhCvNwEardN62dKOKJXmn1TOtyanZvnmWigU5AmGuk6FpsClm3p5vvlid64" + + "i49fZt9vW5krs2XfUevR4oL0IyUl+qW2HN0DIlDiAYkAlQMFEDQvbv2wcgJwUPMh" + + "JQEBVBID/iOtS8CQfMxtG0EmrfaeVUU8R/pegBmVWDBULAp8CLTtdfxjVzs/6DXw" + + "0RogXMRRl2aFfu1Yp0xhBYjII6Kque/FzAFXY9VNF1peqnPt7ADdeptYMppZa8sG" + + "n9BBRu9Fsw69z6JkyqvMiVxGcKy3XEpVGr0JHx8Xt6BYdrULiKr2iQB1AwUQNC68" + + "n6jZR/ntlUftAQFaYgL+NUYEj/sX9M5xq1ORX0SsVPMpNamHO3JBSmZSIzjiox5M" + + "AqoFOCigAkonuzk5aBy/bRHy1cmDBOxf4mNhzrH8N6IkGvPE70cimDnbFvr+hoZS" + + "jIqxtELNZsLuLVavLPAXiQCVAwUQNC6vWocCuHlnLQXBAQHb1gQAugp62aVzDCuz" + + "4ntfXsmlGbLY7o5oZXYIKdPP4riOj4imcJh6cSgYFL6OMzeIp9VW/PHo2mk8kkdk" + + "z5uif5LqOkEuIxgra7p1Yq/LL4YVhWGQeD8hwpmu+ulYoPOw40dVYS36PwrHIH9a" + + "fNhl8Or5O2VIHIWnoQ++9r6gwngFQOyJAJUDBRAzHnkh1sNKtX1rroUBAWphBACd" + + "huqm7GHoiXptQ/Y5F6BivCjxr9ch+gPSjaLMhq0kBHVO+TbXyVefVVGVgCYvFPjo" + + "zM8PEVykQAtY//eJ475aGXjF+BOAhl2z0IMkQKCJMExoEDHbcj0jIIMZ2/+ptgtb" + + "FSyJ2DQ3vvCdbw/1kyPHTPfP+L2u40GWMIYVBbyouokAlQMFEDMe7+UZsymln7HG" + + "2QEBzMED/3L0DyPK/u6PyAd1AdpjUODTkWTZjZ6XA2ubc6IXXsZWpmCgB/24v8js" + + "J3DIsvUD3Ke55kTr6xV+au+mAkwOQqWUTUWfQCkSrSDlbUJ1VPBzhyTpuzjBopte" + + "7o3R6XXfcLiC5jY6eCX0QtLGhKpLjTr5uRhf1fYODGsAGXmCByDviQB1AgUQMy6U" + + "MB0Z9MEMmFelAQHV4AMAjdFUIyFtpTr5jkyZSd3y//0JGO0z9U9hLVxeBBCwvdEQ" + + "xsrpeTtVdqpeKZxHN1GhPCYvgLFZAQlcPh/Gc8u9uO7wVSgJc3zYKFThKpQevdF/" + + "rzjTCHfgigf5Iui0qiqBiQCVAwUQMx22bAtzgG/ED06dAQFi0gQAkosqTMWy+1eU" + + "Xbi2azFK3RX5ERf9wlN7mqh7TvwcPXvVWzUARnwRv+4kk3uOWI18q5UPis7KH3KY" + + "OVeRrPd8bbp6SjhBh82ourTEQUXLBDQiI1V1cZZmwwEdlnAnhFnkXgMBNM2q7oBe" + + "fRHADfYDfGo90wXyrVVL+GihDNpzUwOJAJUDBRAzHUFnOWvfULwOR3EBAbOYA/90" + + "JIrKmxhwP6quaheFOjjPoxDGEZpGJEOwejEByYj+AgONCRmQS3BydtubA+nm/32D" + + "FeG8pe/dnFvGc+QgNW560hK21C2KJj72mhjRlg/na7jz4/MmBAv5k61Q7roWi0rw" + + "x+R9NSHxpshC8A92zmvo8w/XzVSogC8pJ04jcnY6YokAlQMFEDMdPtta9LwlvuSC" + + "3QEBvPMD/3TJGroHhHYjHhiEpDZZVszeRQ0cvVI/uLLi5yq3W4F6Jy47DF8VckA7" + + "mw0bXrOMNACN7Je7uyaU85qvJC2wgoQpFGdFlkjmkAwDAjR+koEysiE8FomiOHhv" + + "EpEY/SjSS4jj4IPmgV8Vq66XjPw+i7Z0RsPLOIf67yZHxypNiBiYiQCVAwUQMxxw" + + "pKrq6G7/78D5AQHo2QQAjnp6KxOl6Vvv5rLQ/4rj3OemvF7IUUq34xb25i/BSvGB" + + "UpDQVUmhv/qIfWvDqWGZedyM+AlNSfUWPWnP41S8OH+lcERH2g2dGKGl7kH1F2Bx" + + "ByZlqREHm2q624wPPA35RLXtXIx06yYjLtJ7b+FCAX6PUgZktZYk5gwjdoAGrC2J" + + "AJUDBRAzGvcCKC6c7f53PGUBAUozA/9l/qKmcqbi8RtLsKQSh3vHds9d22zcbkuJ" + + "PBSoOv2D7i2VLshaQFjq+62uYZGE6nU1WP5sZcBDuWjoX4t4NrffnOG/1R9D0t1t" + + "9F47D77HJzjvo+J52SN520YHcbT8VoHdPRoEOXPN4tzhvn2GapVVdaAlWM0MLloh" + + "NH3I9jap9okAdQMFEDMZlUAnyXglSykrxQEBnuwC/jXbFL+jzs2HQCuo4gyVrPlU" + + "ksQCLYZjNnZtw1ca697GV3NhBhSXR9WHLQH+ZWnpTzg2iL3WYSdi9tbPs78iY1FS" + + "d4EG8H9V700oQG8dlICF5W2VjzR7fByNosKM70WSXYkBFQMFEDMWBsGCy1t9eckW" + + "HQEBHzMH/jmrsHwSPrA5R055VCTuDzdS0AJ+tuWkqIyqQQpqbost89Hxper3MmjL" + + "Jas/VJv8EheuU3vQ9a8sG2SnlWKLtzFqpk7TCkyq/H3blub0agREbNnYhHHTGQFC" + + "YJb4lWjWvMjfP+N5jvlLcnDqQPloXfAOgy7W90POoqFrsvhxdpnXgoLrzyNNja1O" + + "1NRj+Cdv/GmJYNi6sQe43zmXWeA7syLKMw6058joDqEJFKndgSp3Zy/yXmObOZ/H" + + "C2OJwA3gzEaAu8Pqd1svwGIGznqtTNCn9k1+rMvJPaxglg7PXIJS282hmBl9AcJl" + + "wmh2GUCswl9/sj+REWTb8SgJUbkFcp6JAJUDBRAwdboVMPfsgxioXMEBAQ/LA/9B" + + "FTZ9T95P/TtsxeC7lm9imk2mpNQCBEvXk286FQnGFtDodGfBfcH5SeKHaUNxFaXr" + + "39rDGUtoTE98iAX3qgCElf4V2rzgoHLpuQzCg3U35dfs1rIxlpcSDk5ivaHpPV3S" + + "v+mlqWL049y+3bGaZeAnwM6kvGMP2uccS9U6cbhpw4hGBBARAgAGBQI3GtRfAAoJ" + + "EF3iSZZbA1iikWUAoIpSuXzuN/CI63dZtT7RL7c/KtWUAJ929SAtTr9SlpSgxMC8" + + "Vk1T1i5/SYkBFQMFEzccnFnSJilEzmrGwQEBJxwH/2oauG+JlUC3zBUsoWhRQwqo" + + "7DdqaPl7sH5oCGDKS4x4CRA23U15NicDI7ox6EizkwCjk0dRr1EeRK+RqL1b/2T4" + + "2B6nynOLhRG2A0BPHRRJLcoL4nKfoPSo/6dIC+3iVliGEl90KZZD5bnONrVJQkRj" + + "ZL8Ao+9IpmoYh8XjS5xMLEF9oAQqAkA93nVBm56lKmaL1kl+M3dJFtNKtVB8de1Z" + + "XifDs8HykD42qYVtcseCKxZXhC3UTG5YLNhPvgZKH8WBCr3zcR13hFDxuecUmu0M" + + "VhvEzoKyBYYt0rrqnyWrxwbv4gSTUWH5ZbgsTjc1SYKZxz6hrPQnfYWzNkznlFWJ" + + "ARUDBRM0xL43CdxwOTnzf10BATOCB/0Q6WrpzwPMofjHj54MiGLKVP++Yfwzdvns" + + "HxVpTZLZ5Ux8ErDsnLmvUGphnLVELZwEkEGRjln7a19h9oL8UYZaV+IcR6tQ06Fb" + + "1ldR+q+3nXtBYzGhleXdgJQSKLJkzPF72tvY0DHUB//GUV9IBLQMvfG8If/AFsih" + + "4iXi96DOtUAbeuIhnMlWwLJFeGjLLsX1u6HSX33xy4bGX6v/UcHbTSSYaxzb92GR" + + "/xpP2Xt332hOFRkDZL52g27HS0UrEJWdAVZbh25KbZEl7C6zX/82OZ5nTEziHo20" + + "eOS6Nrt2+gLSeA9X5h/+qUx30kTPz2LUPBQyIqLCJkHM8+0q5j9ciQCiAwUTNMS+" + + "HZFeTizbCJMJAQFrGgRlEAkG1FYU4ufTxsaxhFZy7xv18527Yxpls6mSCi1HL55n" + + "Joce6TI+Z34MrLOaiZljeQP3EUgzA+cs1sFRago4qz2wS8McmQ9w0FNQQMz4vVg9" + + "CVi1JUVd4EWYvJpA8swDd5b9+AodYFEsfxt9Z3aP+AcWFb10RlVVsNw9EhObc6IM" + + "nwAOHCEI9vp5FzzFiQCVAwUQNxyr6UyjTSyISdw9AQHf+wP+K+q6hIQ09tkgaYaD" + + "LlWKLbuxePXqM4oO72qi70Gkg0PV5nU4l368R6W5xgR8ZkxlQlg85sJ0bL6wW/Sj" + + "Mz7pP9hkhNwk0x3IFkGMTYG8i6Gt8Nm7x70dzJoiC+A496PryYC0rvGVf+Om8j5u" + + "TexBBjb/jpJhAQ/SGqeDeCHheOC0Lldlcm5lciBLb2NoIChtZWluIGFsdGVyIGtl" + + "eSkgPHdrQGNvbXB1dGVyLm9yZz6JAHUDBRM2G2MyHRn0wQyYV6UBASKKAv4wzmK7" + + "a9Z+g0KH+6W8ffIhzrQo8wDAU9X1WJKzJjS205tx4mmdnAt58yReBc/+5HXTI8IK" + + "R8IgF+LVXKWAGv5P5AqGhnPMeQSCs1JYdf9MPvbe34jD8wA1LTWFXn9e/cWIRgQQ" + + "EQIABgUCNxrUaQAKCRBd4kmWWwNYovRiAJ9dJBVfjx9lGARoFXmAieYrMGDrmwCZ" + + "AQyO4Wo0ntQ+iq4do9M3/FTFjiCZAaIENu1I6REEAJRGEqcYgXJch5frUYBj2EkD" + + "kWAbhRqVXnmiF3PjCEGAPMMYsTddiU7wcKfiCAqKWWXow7BjTJl6Do8RT1jdKpPO" + + "lBJXqqPYzsyBxLzE6mLps0K7SLJlSKTQqSVRcx0jx78JWYGlAlP0Kh9sPV2w/rPh" + + "0LrPeOKXT7lZt/DrIhfPAKDL/sVqCrmY3QfvrT8kSKJcgtLWfQP/cfbqVNrGjW8a" + + "m631N3UVA3tWfpgM/T9OjmKmw44NE5XfPJTAXlCV5j7zNMUkDeoPkrFF8DvbpYQs" + + "4XWYHozDjhR2Q+eI6gZ0wfmhLHqqc2eVVkEG7dT57Wp9DAtCMe7RZfhnarTQMqlY" + + "tOEa/suiHk0qLo59NsyF8eh68IDNCeYD/Apzonwaq2EQ1OEpfFlp6LcSnS34+UGZ" + + "tTO4BgJdmEjr/QrIPp6bJDstgho+/2oR8yQwuHGJwbS/8ADA4IFEpLduSpzrABho" + + "7RuNQcm96bceRY+7Hza3zf7pg/JGdWOb+bC3S4TIpK+3sx3YNWs7eURwpGREeJi5" + + "/Seic+GXlGzltBpXZXJuZXIgS29jaCA8d2tAZ251cGcub3JnPohjBBMRAgAbBQI3" + + "Gs+QBQkMyXyAAwsKAwMVAwIDFgIBAheAABIJEF3iSZZbA1iiB2VHUEcAAQFdwgCe" + + "O/s43kCLDMIsHCb2H3LC59clC5UAn1EyrqWk+qcOXLpQIrP6Qa3QSmXIiEYEEBEC" + + "AAYFAjca0T0ACgkQbH7huGIcwBOF9ACeNwO8G2G0ei03z0g/n3QZIpjbzvEAnRaE" + + "qX2PuBbClWoIP6h9yrRlAEbUiQB1AwUQNxrRYx0Z9MEMmFelAQHRrgL/QDNKPV5J" + + "gWziyzbHvEKfTIw/Ewv6El2MadVvQI8kbPN4qkPr2mZWwPzuc9rneCPQ1eL8AOdC" + + "8+ZyxWzx2vsrk/FcU5donMObva2ct4kqJN6xl8xjsxDTJhBSFRaiBJjxiEYEEBEC" + + "AAYFAjca0aMACgkQaLeriVdUjc0t+ACghK37H2vTYeXXieNJ8aZkiPJSte4An0WH" + + "FOotQdTW4NmZJK+Uqk5wbWlgiEYEEBECAAYFAjdPH10ACgkQ9u7fIBhLxNktvgCe" + + "LnQ5eOxAJz+Cvkb7FnL/Ko6qc5YAnjhWWW5c1o3onvKEH2Je2wQa8T6iiEYEEBEC" + + "AAYFAjenJv4ACgkQmDRl2yFDlCJ+yQCfSy1zLftEfLuIHZsUHis9U0MlqLMAn2EI" + + "f7TI1M5OKysQcuFLRC58CfcfiEUEEBECAAYFAjfhQTMACgkQNmdg8X0u14h55wCf" + + "d5OZCV3L8Ahi4QW/JoXUU+ZB0M0AmPe2uw7WYDLOzv48H76tm6cy956IRgQQEQIA" + + "BgUCOCpiDwAKCRDj8lhUEo8OeRsdAJ9FHupRibBPG2t/4XDqF+xiMLL/8ACfV5F2" + + "SR0ITE4k/C+scS1nJ1KZUDW0C1dlcm5lciBLb2NoiGMEExECABsFAjbtSOoFCQzJ" + + "fIADCwoDAxUDAgMWAgECF4AAEgkQXeJJllsDWKIHZUdQRwABAbXWAJ9SCW0ieOpL" + + "7AY6vF+OIaMmw2ZW1gCgkto0eWfgpjAuVg6jXqR1wHt2pQOJAh4EEBQDAAYFAjcv" + + "WdQACgkQbEwxpbHVFWcNxQf/bg14WGJ0GWMNSuuOOR0WYzUaNtzYpiLSVyLrreXt" + + "o8LBNwzbgzj2ramW7Ri+tYJAHLhtua8ZgSeibmgBuZasF8db1m5NN1ZcHBXGTysA" + + "jp+KnicTZ9Orj75D9o3oSmMyRcisEhr+gkj0tVhGfOAOC6eKbufVuyYFDVIyOyUB" + + "GlW7ApemzAzYemfs3DdjHn87lkjHMVESO4fM5rtLuSc7cBfL/e6ljaWQc5W8S0gI" + + "Dv0VtL39pMW4BlpKa25r14oJywuUpvWCZusvDm7ZJnqZ/WmgOHQUsyYudTROpGIb" + + "lsNg8iqC6huWpGSBRdu3oRQRhkqpfVdszz6BB/nAx01q2wf/Q+U9XId1jyzxUL1S" + + "GgaYMf6QdyjHQ1oxuFLNxzM6C/M069twbNgXJ71RsDDXVxFZfSTjSiH100AP9+9h" + + "b5mycaXLUOXYDvOSFzHBd/LsjFNVrrFbDs5Xw+cLGVHOIgR5IWAfgu5d1PAZU9uQ" + + "VgdGnQfmZg383RSPxvR3fnZz1rHNUGmS6w7x6FVbxa1QU2t38gNacIwHATAPcBpy" + + "JLfXoznbpg3ADbgCGyDjBwnuPQEQkYwRakbczRrge8IaPZbt2HYPoUsduXMZyJI8" + + "z5tvu7pUDws51nV1EX15BcN3++aY5pUyA1ItaaDymQVmoFbQC0BNMzMO53dMnFko" + + "4i42kohGBBARAgAGBQI3OvmjAAoJEHUPZJXInZM+hosAnRntCkj/70shGTPxgpUF" + + "74zA+EbzAKCcMkyHXIz2W0Isw3gDt27Z9ggsE4hGBBARAgAGBQI3NyPFAAoJEPbu" + + "3yAYS8TZh2UAoJVmzw85yHJzsXQ1vpO2IAPfv59NAJ9WY0oiYqb3q1MSxBRwG0gV" + + "iNCJ7YkBFQMFEDdD3tNSgFdEdlNAHQEByHEH/2JMfg71GgiyGJTKxCAymdyf2j2y" + + "fH6wI782JK4BWV4c0E/V38q+jpIYslihV9t8s8w1XK5niMaLwlCOyBWOkDP3ech6" + + "+GPPtfB3cmlL2hS896PWZ1adQHgCeQpB837n56yj0aTs4L1xarbSVT22lUwMiU6P" + + "wYdH2Rh8nh8FvN0IZsbln2nOj73qANQzNflmseUKF1Xh4ck8yLrRd4r6amhxAVAf" + + "cYFRJN4zdLL3cmhgkt0ADZlzAwXnEjwdHHy7SvAJk1ecNOA9pFsOJbvnzufd1afs" + + "/CbG78I+0JDhg75Z2Nwq8eKjsKqiO0zz/vG5yWSndZvWkTWz3D3b1xr1Id2IRgQQ" + + "EQIABgUCOCpiHgAKCRDj8lhUEo8OeQ+QAKCbOTscyUnWHSrDo4fIy0MThEjhOgCe" + + "L4Kb7TWkd/OHQScVBO8sTUz0+2g="); + + byte[] pub6check = Base64.decode("62O9"); + + // + // revoked master key + // + byte[] pub7 = Base64.decode( + "mQGiBFKQDEMRBACtcEzu15gGDrZKLuO2zgDJ9qFkweOxKyeO45LKIfUGBful" + + "lheoFHbsJIeNGjWbSOfWWtphTaSu9//BJt4xxg2pqVLYqzR+hEPpDy9kXxnZ" + + "LwwxjAP2TcOvuZKWe+JzoYQxDunOH4Zu9CPJhZhF3RNPw+tbv0jHfTV/chtb" + + "23Dj5wCg7eoM8bL9NYXacsAfkS//m+AB1MkD/jEZJqJSQHW8WVP7wKRrAZse" + + "N4l9b8+yY4RwLIodhD8wGsMYjkCF4yb/SQ5QlmLlvrHDLBofRzG+8oxldX4o" + + "GLZWvqPmW+BlS4QNSr+ZBu+OwnpClXG2pR+ExumXNoeArREyylrmOgD+0cUa" + + "8K2UbOxbJ8EioyOKxa7wjUVxmHmhBACAGQGLT/lpHA5zcU0g8AlSk8fsd+bB" + + "nwa/+9xdLqVsCTZdOWULtPOw9hbAdjjAy0L4M/MDAJYYtCEl9rB7aOc9PVdT" + + "h7CT9Ma6ltiSMKDlqWbDmogNEGx9Gz3GjiSGxAy/SN6JR1On4c60TAiTv6eE" + + "uEHszE6CH4qceK5W8HLLB4hJBCARAgAJBQJSkA0OAh0CAAoJEBCIvJhXZzdI" + + "X8wAn0qUP/jqAuPEX9g2XCr5jN3RKvKjAKDpx4NP7P1/yLN2ycFIgxKZ1plK" + + "CLACAAO0J3Jldm9rZSAoUmV2b2tlIFRlc3QpIDxyZXZva2VAdGVzdC50ZXN0" + + "PohiBBMRAgAiBQJSkAxDAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAK" + + "CRAQiLyYV2c3SKw0AJ9kufxvnorVOv6a+WM+I/bNP+mjLQCgtPKuwTyeZU2k" + + "Sec1fJZUssDL1hGwAgADuQENBFKQDEMQBACruJ54GuhUaXgqGzu/HsCrgGQA" + + "n86PZW1qPCX28E9sayEmVOgzSDA/OT5c05P0PVLhMNEguSnUGC249MpZfPK/" + + "9LVKMXATZLzEB6lFX1YJdfrrb9KrQ5kdDhOcFXm1GIGBwLzjUmXYRVPH+TsT" + + "4QFvDpfIANQZfKK4UV5BJzJV5wADBQP/ew75dy1x58KWpPqfeH1OVln/Ui6e" + + "EDyyISic147+FIfVDuLtWoxMFE0ZPg47+rEQrhWC96izaSKPW97y5bkWz5PG" + + "CMChnLcg+3M91x/QCGzNhzVMiVpY5VhBDFP+7iiOaKYRiN/x7oasf2482Ny8" + + "1oiykOZUm8FCUQnRmJzIlbiISQQYEQIACQUCUpAMQwIbDAAKCRAQiLyYV2c3" + + "SL04AJ9VceD1DtcEDaWPDzFPgpe3ZfiXiQCfe5azYl26JpHSJvNKZRLi0I8H" + + "shCwAgAD"); + + byte[] pub7sub = Base64.decode( + "mQGiBFKQFFURBAD7CTE4RYPD7O+ki7pl/vXSZsSR0kQhCD9BR4lwE/Iffzmr" + + "vK8tmr2yLKWoXyoc3VF0Gdg/VATDcawBnKSjuCIsFZ58Edacb7uVRl4+ACiu" + + "OsvCKl9JuZ54SQ/tbD+NFS+HWNyVlWn7vDv8l+37DWNxuQRIYtQR+drAnIwQ" + + "g0O4owCg5a9cGAaN0zNVssUo6GFEoAI8nE0EAJMxQMcHTlLQQN1c549Ub0+E" + + "LV4dRIxjO7O6yi6Bg5udwS9Un1XeHF4GM7fj95WHi7o9sgErr2evhuGWl337" + + "ySytE1npk2F/jqevhAJazQTuilEuyjMbCShV39qJlEKtU9uHQYxN8oqGT9Ot" + + "lOoXXtrgfHbsrouCVwm4Jk14kzCaA/4okwrQwGkPlXRpVFyLn4GwrGivG7eh" + + "enRbAd2SQBiNVKmMsKLxHT1avZ11qcx6OU3ixdw5wYmq7TNR+5FXiz/e2MIq" + + "m7VhKONN21F7WC7siHxXfqqI/uz2tTPrFoLbnr/j/RZZRUMh6qUQrWpv58ci" + + "Bh+xkWCRantLCL9khuvRSrQncmV2b2tlIChSZXZva2UgVGVzdCkgPHJldm9r" + + "ZUB0ZXN0LnRlc3Q+iGIEExECACIFAlKQFFUCGwMGCwkIBwMCBhUIAgkKCwQW" + + "AgMBAh4BAheAAAoJEDKzvtpHqpp2DN4AoNS9M634KdvZT25DclGpb2bCFjv0" + + "AKDYXl5fIRGi583vFJ9C/q8hNGyNc7ACAAO5AQ0EUpAUVRAEALusV5UIL4gB" + + "6qQk++h+czV9KS0yxwgZyR+dJza+duEG88aNv28Wmjpfr3ZkvIiUaOcxFoct" + + "LgVGtPJM1HhWJtoA94CRBFTGzLfUIfXHcyXSdAw8Qh96svRl2w2KM+/pJl1r" + + "A3CWIy48jQei0mLwElRELLG7HJKYJxjCbg4+ihYTAAMGA/42PgHTV5VpF7YC" + + "XodlLOyGDVOoRjsvu0Gu/P88QnVP2jN57MJcla224aN3pGprtcbTwyjt+dtf" + + "5IJlB+3RZLczyqvT5hw7j9h81mr3RDbg3cn57xdYwQNP+6b6Wf9QRmaE813s" + + "g3kF0IJ0oFvwZdHnjndQ0JCrKaPflGSO6msjIYhTBCgRAgATBQJSkBXdDB0B" + + "U3VwZXJzZWRlZAAKCRAys77aR6qadmZPAJ0eJzmgBLTWK9RIbVtRUFzm736I" + + "hACgsPGHdZmLUFhV80fvYnUtB7TYGeKwAgADiEkEGBECAAkFAlKQFFUCGwwA" + + "CgkQMrO+2keqmnZGIACfRTkdqi6b7fjqkWxx7DysKBedgS8An1TJrhhkeJVd" + + "smkOCYLILgjrBHq4sAIAAw=="); + + byte[] pub8 = Base64.decode( + "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" + + "9Uqz3fUvGoewAWA="); + + byte[] sec8 = Base64.decode( + "lQHpBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ/4JAwLXyWhb4pf4" + + "nmCmD0lDwoYvatLiR7UQVM2MamxClIiT0lCPN9C2AYIFgRWAJNS215Tjx7P/dh7e" + + "8sYfh5XEHErT3dMbsAGHtCFKaWEgWWl5dSA8eXlqaWFAbm93bWVkaWF0ZWNoLmNv" + + "bT6wAwP//4kAXQQQEQIAHQUCQRytpgcLCQgHAwIKAhkBBRsDAAAABR4BAAAAAAoJ" + + "EPT+Vvgr/2IUmWEAoJEyJ9B7CJfmjwVTT30UbdCctVRmAJ9akw6we6UYd82vF0FK" + + "QTlSuqHXKbABZ50CawRBHK2mEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlL" + + "OCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N" + + "286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/" + + "RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2O" + + "u1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqV" + + "DNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/crbApFLO" + + "QikN9lHbn62dDxuu/dNigNmjf/A3cX+vo8aPHTXnRDyDwQALRuqbitdbM6p1LFjG" + + "8g84Aabk/jL7zzjJLqPe8pxeK1Pu+P44NzalXAYMsOOsxGuixIrz3Jw8IrpH/mkP" + + "tG/iX0NaLZ+Au9eokdLb+6NR/a51r2qL3E0ycFciJ61HzZKKHi6d0VQ7CHozCz+j" + + "d5530Mh1qq/ZjYDDjfP+snpyNIZXcLtGR/3HzkBqNgDcvClLx3322AWKDa7pX8DX" + + "qiLr8znWhPRpH9kCTnSpRzhYGT25FRcvdj4/q/oUIET/Tp+BWW3BxAc7WgpgqEfn" + + "fa0Mv72Zbn91gf4JAwITijME9IlFBGAwH6YmBtWIlnDiRbsq/Pxozuhbnes831il" + + "KmdpUKXkiIfHY0MqrEWl3Dfn6PMJGTnhgqXMrDxx3uHrq0Jl2swRnAWIIO8gID7j" + + "uPetUqEviPiwAYeJAEwEGBECAAwFAkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg" + + "+JW8m5nF3R/oZGuG87bXQBszkjMAoLhGPncuGKowJXMRVc70/8qwXQJLsAFn"); + + char[] sec8pass = "qwertyui".toCharArray(); + + byte[] sec9 = Base64.decode( + "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" + + "6neEEX/i1Q=="); + + public char[] sec9pass = "foo".toCharArray(); + + // version 4 keys with expiry dates + byte[] pub10 = Base64.decode( + "mQGiBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHLQgdGVzdCBrZXkg" + + "KHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUCQqqJrQIbAwUJACTqAAYL" + + "CQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzLAJ42AeCRIBBjv8r8qw9y" + + "laNj2GZ1sACgiWYHVXMA6B1H9I1kS3YsCd3Oq7qwAgAAuM0EQqqJrhADAKWkix8l" + + "pJN7MMTXob4xFF1TvGll0UD1bDGOMMbes6aeXSbT9QXee/fH3GnijLY7wB+qTPv9" + + "ohubrSpnv3yen3CEBW6Q2YK+NlCskma42Py8YMV2idmYjtJi1ckvHFWt5wADBQL/" + + "fkB5Q5xSGgspMaTZmtmX3zG7ZDeZ0avP8e8mRL8UszCTpqs6vMZrXwyQLZPbtMYv" + + "PQpuRGEeKj0ysimwYRA5rrLQjnRER3nyuuEUUgc4j+aeRxPf9WVsJ/a1FCHtaAP1" + + "iE8EGBECAA8FAkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCfd66H7DL7kFGd" + + "IoS+NIp8JO+noxAAn25si4QAF7og8+4T5YQUuhIhx/NesAIAAA=="); + + byte[] sec10 = Base64.decode( + "lQHhBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHP4DAwLssmOjVC+d" + + "mWB783Lpzjb9evKzsxisTdx8/jHpUSS+r//6/Guyx3aA/zUw5bbftItW57mhuNNb" + + "JTu7WrQgdGVzdCBrZXkgKHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUC" + + "QqqJrQIbAwUJACTqAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzL" + + "AJ0cYPwKeoSReY14LqJtAjnkX7URHACgsRZWfpbalrSyDnq3TtZeGPUqGX+wAgAA" + + "nQEUBEKqia4QAwClpIsfJaSTezDE16G+MRRdU7xpZdFA9WwxjjDG3rOmnl0m0/UF" + + "3nv3x9xp4oy2O8Afqkz7/aIbm60qZ798np9whAVukNmCvjZQrJJmuNj8vGDFdonZ" + + "mI7SYtXJLxxVrecAAwUC/35AeUOcUhoLKTGk2ZrZl98xu2Q3mdGrz/HvJkS/FLMw" + + "k6arOrzGa18MkC2T27TGLz0KbkRhHio9MrIpsGEQOa6y0I50REd58rrhFFIHOI/m" + + "nkcT3/VlbCf2tRQh7WgD9f4DAwLssmOjVC+dmWDXVLRopzxbBGOvodp/LZoSDb56" + + "gNJjDMJ1aXqWW9qTAg1CFjBq73J3oFpVzInXZ8+Q8inxv7bnWiHbiE8EGBECAA8F" + + "AkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCgl2jw5hfk/JsyjulQqe1Nps1q" + + "Lx0AoMdnFMZmTMLHn8scUW2j9XO312tmsAIAAA=="); + + public char[] sec10pass = "test".toCharArray(); + + public byte[] subKeyBindingKey = Base64.decode( + "mQGiBDWagYwRBAD7UcH4TAIp7tmUoHBNxVxCVz2ZrNo79M6fV63riOiH2uDxfIpr" + + "IrL0cM4ehEKoqlhngjDhX60eJrOw1nC5BpYZRnDnyDYT4wTWRguxObzGq9pqA1dM" + + "oPTJhkFZVIBgFY99/ULRqaUYIhFGgBtnwS70J8/L/PGVc3DmWRLMkTDjSQCg/5Nh" + + "MCjMK++MdYMcMl/ziaKRT6EEAOtw6PnU9afdohbpx9CK4UvCCEagfbnUtkSCQKSk" + + "6cUp6VsqyzY0pai/BwJ3h4apFMMMpVrtBAtchVgqo4xTr0Sve2j0k+ase6FSImiB" + + "g+AR7hvTUTcBjwtIExBc8TuCTqmn4GG8F7UMdl5Z0AZYj/FfAQYaRVZYP/pRVFNx" + + "Lw65BAC/Fi3qgiGCJFvXnHIckTfcAmZnKSEXWY9NJ4YQb4+/nH7Vsw0wR/ZObUHR" + + "bWgTc9Vw1uZIMe0XVj6Yk1dhGRehUnrm3mE7UJxu7pgkBCbFECFSlSSqP4MEJwZV" + + "09YP/msu50kjoxyoTpt+16uX/8B4at24GF1aTHBxwDLd8X0QWrQsTWVycmlsbCBM" + + "eW5jaCBDTEVBUiBzeXN0ZW0gREggPGNsZWFyQG1sLmNvbT6JAEsEEBECAAsFAjWa" + + "gYwECwMBAgAKCRDyAGjiP47/XanfAKCs6BPURWVQlGh635VgL+pdkUVNUwCdFcNa" + + "1isw+eAcopXPMj6ACOapepu5Ag0ENZqBlBAIAPZCV7cIfwgXcqK61qlC8wXo+VMR" + + "OU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf" + + "3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2g" + + "pXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPA" + + "Q/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQD" + + "GcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH" + + "/RYtVo+HROZ6jrNjrATEwQm1fUQrk6n5+2dniN881lF0CNkB4NkHw1Xxz4Ejnu/0" + + "iLg8fkOAsmanOsKpOkRtqUnVpsVL5mLJpFEyCY5jbcfj+KY9/25bs0ga7kLHNZia" + + "zbCxJdF+W179z3nudQxRaXG/0XISIH7ziZbSVni69sKc1osk1+OoOMbSuZ86z535" + + "Pln4fXclkFE927HxfbWoO+60hkOLKh7x+8fC82b3x9vCETujEaxrscO2xS7/MYXP" + + "8t1ffriTDmhuIuQS2q4fLgeWdqrODrMhrD8Dq7e558gzp30ZCqpiS7EmKGczL7B8" + + "gXxbBCVSTxYMJheXt2xMXsuJAD8DBRg1moGU8gBo4j+O/10RAgWdAKCPhaFIXuC8" + + "/cdiNMxTDw9ug3De5QCfYXmDzRSFUu/nrCi8yz/l09wsnxo="); + + public byte[] subKeyBindingCheckSum = Base64.decode("3HU+"); + + // + // PGP8 with SHA1 checksum. + // + public byte[] rewrapKey = Base64.decode( + "lQOWBEUPOQgBCADdjPTtl8oOwqJFA5WU8p7oDK5KRWfmXeXUZr+ZJipemY5RSvAM" + + "rxqsM47LKYbmXOJznXCQ8+PPa+VxXAsI1CXFHIFqrXSwvB/DUmb4Ec9EuvNd18Zl" + + "hJAybzmV2KMkaUp9oG/DUvxZJqkpUddNfwqZu0KKKZWF5gwW5Oy05VCpaJxQVXFS" + + "whdbRfwEENJiNx4RB3OlWhIjY2p+TgZfgQjiGB9i15R+37sV7TqzBUZF4WWcnIRQ" + + "DnpUfxHgxQ0wO/h/aooyRHSpIx5i4oNpMYq9FNIyakEx/Bomdbs5hW9dFxhrE8Es" + + "UViAYITgTsyROxmgGatGG09dcmVDJVYF4i7JAAYpAAf/VnVyUDs8HrxYTOIt4rYY" + + "jIHToBsV0IiLpA8fEA7k078L1MwSwERVVe6oHVTjeR4A9OxE52Vroh2eOLnF3ftf" + + "6QThVVZr+gr5qeG3yvQ36N7PXNEVOlkyBzGmFQNe4oCA+NR2iqnAIspnekVmwJV6" + + "xVvPCjWw/A7ZArDARpfthspwNcJAp4SWfoa2eKzvUTznTyqFu2PSS5fwQZUgOB0P" + + "Y2FNaKeqV8vEZu4SUWwLOqXBQIZXiaLvdKNgwFvUe3kSHdCNsrVzW7SYxFwaEog2" + + "o6YLKPVPqjlGX1cMOponGp+7n9nDYkQjtEsGSSMQkQRDAcBdSVJmLO07kFOQSOhL" + + "WQQA49BcgTZyhyH6TnDBMBHsGCYj43FnBigypGT9FrQHoWybfX47yZaZFROAaaMa" + + "U6man50YcYZPwzDzXHrK2MoGALY+DzB3mGeXVB45D/KYtlMHPLgntV9T5b14Scbc" + + "w1ES2OUtsSIUs0zelkoXqjLuKnSIYK3mMb67Au7AEp6LXM8EAPj2NypvC86VEnn+" + + "FH0QHvUwBpmDw0EZe25xQs0brvAG00uIbiZnTH66qsIfRhXV/gbKK9J5DTGIqQ15" + + "DuPpz7lcxg/n2+SmjQLNfXCnG8hmtBjhTe+udXAUrmIcfafXyu68SAtebgm1ga56" + + "zUfqsgN3FFuMUffLl3myjyGsg5DnA/oCFWL4WCNClOgL6A5VkNIUait8QtSdCACT" + + "Y7jdSOguSNXfln0QT5lTv+q1AjU7zjRl/LsFNmIJ5g2qdDyK937FOXM44FEEjZty" + + "/4P2dzYpThUI4QUohIj8Qi9f2pZQueC5ztH6rpqANv9geZKcciAeAbZ8Md0K2TEU" + + "RD3Lh+RSBzILtBtUZXN0IEtleSA8dGVzdEBleGFtcGxlLmNvbT6JATYEEwECACAF" + + "AkUPOQgCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDYpknHeQaskD9NB/9W" + + "EbFuLaqZAl3yjLU5+vb75BdvcfL1lUs44LZVwobNp3/0XbZdY76xVPNZURtU4u3L" + + "sJfGlaF+EqZDE0Mqc+vs5SIb0OnCzNJ00KaUFraUtkByRV32T5ECHK0gMBjCs5RT" + + "I0vVv+Qmzl4+X1Y2bJ2mlpBejHIrOzrBD5NTJimTAzyfnNfipmbqL8p/cxXKKzS+" + + "OM++ZFNACj6lRM1W9GioXnivBRC88gFSQ4/GXc8yjcrMlKA27JxV+SZ9kRWwKH2f" + + "6o6mojUQxnHr+ZFKUpo6ocvTgBDlC57d8IpwJeZ2TvqD6EdA8rZ0YriVjxGMDrX1" + + "8esfw+iLchfEwXtBIRwS"); + + char[] rewrapPass = "voltage123".toCharArray(); + + byte[] pubWithX509 = Base64.decode( + "mQENBERabjABCACtmfyo6Nph9MQjv4nmCWjZrRYnhXbivomAdIwYkLZUj1bjqE+j"+ + "uaLzjZV8xSI59odZvrmOiqlzOc4txitQ1OX7nRgbOJ7qku0dvwjtIn46+HQ+cAFn"+ + "2mTi81RyXEpO2uiZXfsNTxUtMi+ZuFLufiMc2kdk27GZYWEuasdAPOaPJnA+wW6i"+ + "ZHlt0NfXIGNz864gRwhD07fmBIr1dMFfATWxCbgMd/rH7Z/j4rvceHD2n9yrhPze"+ + "YN7W4Nuhsr2w/Ft5Cm9xO7vXT/cpto45uxn8f7jERep6bnUwNOhH8G+6xLQgTLD0"+ + "qFBGVSIneK3lobs6+xn6VaGN8W0tH3UOaxA1ABEBAAG0D0NOPXFhLWRlZXBzaWdo"+ + "dIkFDgQQZAIFAQUCRFpuMAUDCWdU0gMF/3gCGwPELGQBAQQwggTkMIIDzKADAgEC"+ + "AhBVUMV/M6rIiE+IzmnPheQWMA0GCSqGSIb3DQEBBQUAMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDAeFw0wNjA1MDQyMTEyMTZaFw0xMTA1MDQyMTIwMDJaMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2Z/Kjo2mH0xCO/ieYJ"+ + "aNmtFieFduK+iYB0jBiQtlSPVuOoT6O5ovONlXzFIjn2h1m+uY6KqXM5zi3GK1DU"+ + "5fudGBs4nuqS7R2/CO0ifjr4dD5wAWfaZOLzVHJcSk7a6Jld+w1PFS0yL5m4Uu5+"+ + "IxzaR2TbsZlhYS5qx0A85o8mcD7BbqJkeW3Q19cgY3PzriBHCEPTt+YEivV0wV8B"+ + "NbEJuAx3+sftn+Piu9x4cPaf3KuE/N5g3tbg26GyvbD8W3kKb3E7u9dP9ym2jjm7"+ + "Gfx/uMRF6npudTA06Efwb7rEtCBMsPSoUEZVIid4reWhuzr7GfpVoY3xbS0fdQ5r"+ + "EDUCAwEAAaOCAXwwggF4MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G"+ + "A1UdDgQWBBSmFTRv5y65DHtTYae48zl0ExNWZzCCASUGA1UdHwSCARwwggEYMIIB"+ + "FKCCARCgggEMhoHFbGRhcDovLy9DTj1xYS1kZWVwc2lnaHQsQ049cWEtd3VtYW4x"+ + "LWRjLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNl"+ + "cyxDTj1Db25maWd1cmF0aW9uLERDPVdlYmZlLERDPXRtczAxLERDPXFhLERDPWNv"+ + "bT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM"+ + "RGlzdHJpYnV0aW9uUG9pbnSGQmh0dHA6Ly9xYS13dW1hbjEtZGMud2ViZmUudG1z"+ + "MDEucWEuY29tL0NlcnRFbnJvbGwvcWEtZGVlcHNpZ2h0LmNybDAQBgkrBgEEAYI3"+ + "FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAfuZCW3XlB7Eok35zQbvYt9rhAndT"+ + "DNw3wPNI4ZzD1nXoYWnwhNNvWRpsOt4ExOSNdaHErfgDXAMyyg66Sro0TkAx8eAj"+ + "fPQsyRAh0nm0glzFmJN6TdOZbj7hqGZjc4opQ6nZo8h/ULnaEwMIUW4gcSkZt0ww"+ + "CuErl5NUrN3DpkREeCG/fVvQZ8ays3ibQ5ZCZnYBkLYq/i0r3NLW34WfYhjDY48J"+ + "oQWtvFSAxvRfz2NGmqnrCHPQZxqlfdta97kDa4VQ0zSeBaC70gZkLmD1GJMxWoXW"+ + "6tmEcgPY5SghInUf+L2u52V55MjyAFzVp7kTK2KY+p7qw35vzckrWkwu8AAAAAAA"+ + "AQE="); + + private static byte[] secWithPersonalCertificate = Base64.decode( + "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" + + "cGdwbWltZQULBwgJAgIZAQUbAQAAAAUeAQAAAAIVAgAKCRDlTa3BE84gWVKW" + + "BACcoCFKvph9r9QiHT1Z3N4wZH36Uxqu/059EFALnBkEdVudX/p6S9mynGRk" + + "EfhmWFC1O6dMpnt+ZBEed/4XyFWVSLPwirML+6dxfXogdUsdFF1NCRHc3QGc" + + "txnNUT/zcZ9IRIQjUhp6RkIvJPHcyfTXKSbLviI+PxzHU2Padq8pV7ABZ7kA" + + "jQRIfg8tAQQAutJR/aRnfZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr" + + "5dg50wq3I4HOamRxUwHpdPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO" + + "8LUJ2VTbfPxoLFp539SQ0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0Ft" + + "JycAEQEAAbABj4kEzQQYAQIENwUCSMYtnAUJAeEzgMLFFAAAAAAAFwNleDUw" + + "OWNlcnRpZmljYXRlQHBncC5jb20wggNhMIICyqADAgECAgkA1AoCoRKJCgsw" + + "DQYJKoZIhvcNAQEFBQAwgakxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj" + + "aCBSZXB1YmxpYzESMBAGA1UEChQJQSYmTCBzb2Z0MSAwHgYDVQQLExdJbnRl" + + "cm5hbCBEZXZlbG9wbWVudCBDQTEqMCgGA1UEAxQhQSYmTCBzb2Z0IEludGVy" + + "bmFsIERldmVsb3BtZW50IENBMR8wHQYJKoZIhvcNAQkBFhBrYWRsZWNAYWxz" + + "b2Z0LmN6MB4XDTA4MDcxNjE1MDkzM1oXDTA5MDcxNjE1MDkzM1owaTELMAkG" + + "A1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMRIwEAYDVQQKFAlB" + + "JiZMIHNvZnQxFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5IeW5l" + + "ay1JbnRyYW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAutJR/aRn" + + "fZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr5dg50wq3I4HOamRxUwHp" + + "dPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO8LUJ2VTbfPxoLFp539SQ" + + "0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0FtJycCAwEAAaOBzzCBzDAJ" + + "BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD" + + "ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNaw7A6r10PtYZzAvr9CrSKeRYJgwHwYD" + + "VR0jBBgwFoAUmqSRM8rN3+T1+tkGiqef8S5suYgwGgYDVR0RBBMwEYEPaHlu" + + "ZWtAYWxzb2Z0LmN6MCgGA1UdHwQhMB8wHaAboBmGF2h0dHA6Ly9wZXRyazIv" + + "Y2EvY2EuY3JsMAsGA1UdDwQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQCUdOWd" + + "7mBLWj1/GSiYgfwgdTrgk/VZOJvMKBiiFyy1iFEzldz6Xx+mAexnFJKfZXZb" + + "EMEGWHfWPmgJzAtuTT0Jz6tUwDmeLH3MP4m8uOZtmyUJ2aq41kciV3rGxF0G" + + "BVlZ/bWTaOzHdm6cjylt6xxLt6MJzpPBA/9ZfybSBh1DaAUbDgAAAJ0gBBkB" + + "AgAGBQJIxi2bAAoJEAdYkEWLb2R2fJED/RK+JErZ98uGo3Z81cHkdP3rk8is" + + "DUL/PR3odBPFH2SIA5wrzklteLK/ZXmBUzcvxqHEgI1F7goXbsBgeTuGgZdx" + + "pINErxkNpcMl9FTldWKGiapKrhkZ+G8knDizF/Y7Lg6uGd2nKVxzutLXdHJZ" + + "pU89Q5nzq6aJFAZo5TBIcchQAAoJEOVNrcETziBZXvQD/1mvFqBfWqwXxoj3" + + "8fHUuFrE2pcp32y3ciO2i+uNVEkNDoaVVNw5eHQaXXWpllI/Pe6LnBl4vkyc" + + "n3pjONa4PKrePkEsCUhRbIySqXIHuNwZumDOlKzZHDpCUw72LaC6S6zwuoEf" + + "ucOcxTeGIUViANWXyTIKkHfo7HfigixJIL8nsAFn"); + + private static final byte[] umlautKeySig = Base64.decode( + "mI0ETdvOgQEEALoI2a39TRk1HReEB6DP9Bu3ShZUce+/Oeg9RIL9aUFuCsNdhu02" + + "REEHjO29Jz8daPgrnJDfFepNLD6iKKru2m9P30qnhsHMIAshO2Ozfh6wKwuHRqR3" + + "L4gBDu7cCB6SLwPoD8AYG0yQSM+Do10Td87RlStxCgxpMK6R3TsRkxcFABEBAAG0" + + "OlVNTEFVVFNUQVJUOsOEw6TDlsO2w5zDvMOfOlVNTEFURU5ERSA8YXNkbGFrc2Rs" + + "QGFrc2RqLmNvbT6IuAQTAQIAIgUCTdvOgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC" + + "HgECF4AACgkQP8kDwm8AOFiArAP/ZXrlZJB1jFEjyBb04ckpE6F/aJuSYIXf0Yx5" + + "T2eS+lA69vYuqKRC1qNROBrAn/WGNOQBFNEgGoy3F3gV5NgpIphnyIEZdZWGY2rv" + + "yjunKWlioZjWc/xbSbvpvJ3Q8RyfDXBOkDEB6uF1ksimw2eJSOUTkF9AQfS5f4rT" + + "5gs013G4jQRN286BAQQApVbjd8UhsQLB4TpeKn9+dDXAfikGgxDOb19XisjRiWxA" + + "+bKFxu5tRt6fxXl6BGSGT7DhoVbNkcJGVQFYcbR31UGKCVYcWSL3yfz+PiVuf1UB" + + "Rp44cXxxqxrLqKp1rk3dGvV4Ayy8lkk3ncDGPez6lIKvj3832yVtAzUOX1QOg9EA" + + "EQEAAYifBBgBAgAJBQJN286BAhsMAAoJED/JA8JvADhYQ80D/R3TX0FBMHs/xqEh" + + "tiS86XP/8pW6eMm2eaAYINxoDY3jmDMv2HFQ+YgrYXgqGr6eVGqDMNPj4W8VBoOt" + + "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + + // Key from http://www.angelfire.com/pr/pgpf/pgpoddities.html + private static final char[] v3KeyPass = "test@key.test".toCharArray(); + + private static final byte[] pubv3 = Base64.decode( + "mQENAzroPPgAAAEIANnTx/gHfag7qRMG6cVUnYZJjLcsdF6JSaVs+PUDCZ8l2+Z2" + + "V9tgxByp26bymIlq5qFFeoA5vCiKc8qzYiEVLJVVIIDjw/id2gq/TgmxoLAwiDQM" + + "TUKdCFa6pmR/uaxyrnJxfUA7+Qh0R0OjoCxNlrmyO3eiKstsJGqSUFIQq7GhcHc4" + + "nbV59zHhEWnH7DX7sDa9CgF11WxM3sjWp15iOoP1nixhmchDtQ7foUxLsCF36G/4" + + "ijcbN2NjiCDYMFburN8fXgrQzYHAIIiVFE0J+fbXNfPRmnbhQdaC8rIdiQ3tExBb" + + "N0qWhGPT9M4JOZd1yPdFMb9gbntd8VZkiPd6/3sABRG0FHRlc3QgPHRlc3RAa2V5" + + "LnRlc3Q+iQEVAwUQOug8+PFWZIj3ev97AQH7NQgAo3sH+KcsPtAbyp5U02J9h3Ro" + + "aiKpAYxg3rfUVo/RH6hmCWT/AlPHLPZZC/tKiPkuIm2V3Xqyum530N0sBYxNzgNp" + + "us8mK9QurYj2omKzf1ltN+uNHR8vjB8s7jEd/CDCARu81PqNoVq2b9JRFGpGbAde" + + "7kQ/a0r2/IsJ8fz0iSpCH0geoHt3sBk9MyEem4uG0e2NzlH2wBz4H8l8BNHRHBq0" + + "6tGH4h11ZhH3FiNzJWibT2AvzLCqar2qK+6pohKSvIp8zEP7Y/iQzCvkuOfHsUOH" + + "4Utgg85k09hRDZ3pRRL/4R+Z+/1uXb+n6yKbOmpmi7U7wc9IwZxtTlGXsNIf+Q==" + ); + + private static final byte[] privv3 = Base64.decode( + "lQOgAzroPPgAAAEIANnTx/gHfag7qRMG6cVUnYZJjLcsdF6JSaVs+PUDCZ8l2+Z2" + + "V9tgxByp26bymIlq5qFFeoA5vCiKc8qzYiEVLJVVIIDjw/id2gq/TgmxoLAwiDQM" + + "TUKdCFa6pmR/uaxyrnJxfUA7+Qh0R0OjoCxNlrmyO3eiKstsJGqSUFIQq7GhcHc4" + + "nbV59zHhEWnH7DX7sDa9CgF11WxM3sjWp15iOoP1nixhmchDtQ7foUxLsCF36G/4" + + "ijcbN2NjiCDYMFburN8fXgrQzYHAIIiVFE0J+fbXNfPRmnbhQdaC8rIdiQ3tExBb" + + "N0qWhGPT9M4JOZd1yPdFMb9gbntd8VZkiPd6/3sABREDXB5zk3GNdSkH/+/447Kq" + + "hR9uM+UnZz7wDkzmt+7xbNg9F2pr/tghVCM7D0PO1YjH4DBpU1ZRO+v1t/eBB/Jd" + + "3lJYdlWYHOefJkBi44gNAafZ8ysPOJk6OGOjas/sr+JRFiX9Mgzrs2IDiejmuA98" + + "DLuSuNtzFKbE2/DDdOBEizYUjqPLlCdn5sVEt+0WKWJiAv7YonCGguWS3RKfTaYk" + + "9IE9SbI+qph9JsuyTD22GLv+gTMvwCkC1DVaHIVgzURpdnlyYyz4DBh3pAgg0nh6" + + "gpUTsjnUmrvdh+r8qj3oXH7WBMhs6qKYvU1Go5iV3S1Cu4H/Z/+s6XUFgQShevVe" + + "VCy0QtmWSFeySekEACHLJIdBDa8K4dcM2wvccz587D4PtKvMG5j71raOcgVY+r1k" + + "e6au/fa0ACqLNvn6+vFHG+Rurn8RSKV31YmTpx7J5ixTOsB+wVcwTYbrw8uNlBWc" + + "+IkqPwHrtdK95GIYQykfPW95PRudsOBdxwQW4Ax/WCst3fbjo0SZww0Os+3WBADJ" + + "/Nv0mjikXRmqJIzfuI2yxxX4Wm6vqXJkPF7LGtSMB3VEJ3qPsysoai5TYboxA8C1" + + "4rQjIoQjA+87gxZ44PUVxrxBonITCLXJ3GsvDQ2PNhS6WQ9Cf89vtYW1vLW65Nex" + + "+7AuVRepKhx6Heqdf7S03m6UYliIglrEzgEWM1XrOwP/gLMsme4h0LjLgKfd0LBk" + + "qSMdu21VSl60TMTjxav149AdutzuCVa/yPBM/zLQdlvQoGYg2IbN4+7gDHKURcSx" + + "DgOAzCcEZxdMvRk2kaOI5RRf5gV9e+ErvEMzJ/xT8xWsi+aLOhaDMbwq2LLiK2L+" + + "tXV/Z3H/Ot4u3E7H+6fHPElFYbQUdGVzdCA8dGVzdEBrZXkudGVzdD4=" + ); + + public void test1() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + Iterator sIt = pubKey.getSignatures(); + while (sIt.hasNext()) + { + ((PGPSignature)sIt.next()).getSignatureType(); + } + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + // + // exact match + // + rIt = pubRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = pubRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on partial match 1"); + } + + // + // partial match 0 expected + // + rIt = pubRings.getKeyRings("XXX", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of public keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = pubRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on case-insensitive partial match"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + pk.getSignatures(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + + // + // exact match + // + rIt = secretRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = secretRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on partial match 1"); + } + + // + // exact match 0 expected + // + rIt = secretRings.getKeyRings("test", false); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of secret keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = secretRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on case-insensitive partial match"); + } + } + + public void test2() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + + keyCount++; + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + if (pk.getKeyID() == -1413891222336124627L) + { + int sCount = 0; + Iterator sIt = pk.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + while (sIt.hasNext()) + { + int type = ((PGPSignature)sIt.next()).getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING) + { + fail("failed to return correct signature type"); + } + sCount++; + } + + if (sCount != 1) + { + fail("failed to find binding signature"); + } + } + + pk.getSignatures(); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(sec2pass1)); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(sec2pass2)); + } + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 2) + { + fail("wrong number of secret keyrings"); + } + } + + public void test3() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubK = (PGPPublicKey)it.next(); + + pubK.getSignatures(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test4() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + fail("test4 - wrong number of secret keys"); + } + + keyCount = 0; + it = pgpSec.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey k = (PGPPublicKey)it.next(); // make sure it's what we think it is! + } + + if (keyCount != 2) + { + fail("test4 - wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test5() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + if (noIDEA()) + { + return; + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(sec5pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + private boolean noIDEA() + { + try + { + Cipher.getInstance("IDEA", "SC"); + + return false; + } + catch (Exception e) + { + return true; + } + } + + public void test6() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub6); + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.getKeyID() == 0x5ce086b5b5a18ff4L) + { + int count = 0; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test6."); + } + } + } + } + + byte[] encRing = pubRings.getEncoded(); + } + + public void revocationTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(pub7, new JcaKeyFingerprintCalculator()); + Iterator it = pgpPub.getPublicKeys(); + PGPPublicKey masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + } + + int count = 0; + PGPSignature sig = null; + Iterator sIt = masterKey.getSignaturesOfType(PGPSignature.KEY_REVOCATION); + + while (sIt.hasNext()) + { + sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test7."); + } + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), masterKey); + + if (!sig.verifyCertification(masterKey)) + { + fail("failed to verify revocation certification"); + } + + pgpPub = new PGPPublicKeyRing(pub7sub, new JcaKeyFingerprintCalculator()); + it = pgpPub.getPublicKeys(); + masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + + count = 0; + sig = null; + sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + + while (sIt.hasNext()) + { + sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test7 subkey."); + } + + if (sig.getSignatureType() != PGPSignature.SUBKEY_REVOCATION) + { + fail("wrong signature found"); + } + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), masterKey); + + if (!sig.verifyCertification(masterKey, k)) + { + fail("failed to verify revocation certification of subkey"); + } + } + } + + public void test8() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub8); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec8); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(sec8pass)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test9() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec9); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new JcaKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + PGPPrivateKey pKey = k.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(sec9pass)); + if (keyCount == 1 && pKey != null) + { + fail("primary secret key found, null expected"); + } + } + + if (keyCount != 3) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test10() + throws Exception + { + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new JcaKeyFingerprintCalculator()); + Iterator secretKeys = secretRing.getSecretKeys(); + + while (secretKeys.hasNext()) + { + PGPPublicKey pubKey = ((PGPSecretKey)secretKeys.next()).getPublicKey(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on secret key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on secret key ring"); + } + } + + PGPPublicKeyRing publicRing = new PGPPublicKeyRing(pub10); + Iterator publicKeys = publicRing.getPublicKeys(); + + while (publicKeys.hasNext()) + { + PGPPublicKey pubKey = (PGPPublicKey)publicKeys.next(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on public key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on public key ring"); + } + } + } + + public void generateTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.initVerify(vKey, "SC"); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void insertMasterTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + + rsaKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing1 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair2, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing2 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing2 = keyRingGen.generatePublicKeyRing(); + + try + { + PGPPublicKeyRing.insertPublicKey(pubRing1, pubRing2.getPublicKey()); + fail("adding second master key (public) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in public test"); + } + } + + try + { + PGPSecretKeyRing.insertSecretKey(secRing1, secRing2.getSecretKey()); + fail("adding second master key (secret) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in secret test"); + } + } + } + + public void generateSha1Test() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(elParams); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, true, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(passPhrase)); + + if (!keyRing.getSecretKey().getPublicKey().equals(keyRing.getPublicKey())) + { + fail("secret key public key mismatch"); + } + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + // check key id fetch + if (keyRing.getPublicKey(vKey.getKeyID()).getKeyID() != vKey.getKeyID()) + { + fail("secret key public key mismatch - vKey"); + } + + if (keyRing.getPublicKey(sKey.getKeyID()).getKeyID() != sKey.getKeyID()) + { + fail("secret key public key mismatch - sKey"); + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.initVerify(vKey, "SC"); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void test11() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(subKeyBindingKey); + Iterator it = pubRing.getPublicKeys(); + + while (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + if (key.getValidSeconds() != 0) + { + fail("expiration time non-zero"); + } + } + } + + private void rewrapTest() + throws Exception + { + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + char[] newPass = "fred".toCharArray(); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv= (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("SC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + } + } + + private void rewrapTestV3() + throws Exception + { + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(privv3)); + char[] newPass = "fred".toCharArray(); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(v3KeyPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5, new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build().get(HashAlgorithmTags.MD5)).setProvider("SC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build()).setProvider("SC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + } + } + + private void rewrapTestMD5() + throws Exception + { + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + char[] newPass = "fred".toCharArray(); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv= (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder().setProvider("SC").build(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new JcePBESecretKeyDecryptorBuilder(calcProvider).setProvider("SC").build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5, calcProvider.get(HashAlgorithmTags.MD5)).setProvider("SC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(calcProvider).setProvider("SC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + } + } + + private void testPublicKeyRingWithX509() + throws Exception + { + checkPublicKeyRingWithX509(pubWithX509); + + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(pubWithX509); + + checkPublicKeyRingWithX509(pubRing.getEncoded()); + } + + private void testSecretKeyRingWithPersonalCertificate() + throws Exception + { + checkSecretKeyRingWithPersonalCertificate(secWithPersonalCertificate); + PGPSecretKeyRingCollection secRing = new PGPSecretKeyRingCollection(secWithPersonalCertificate); + checkSecretKeyRingWithPersonalCertificate(secRing.getEncoded()); + } + + private void testUmlaut() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(umlautKeySig); + + PGPPublicKey pub = pubRing.getPublicKey(); + String userID = (String)pub.getUserIDs().next(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.initVerify(pub, "SC"); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID test"); + } + } + } + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + char[] passPhrase = "passwd".toCharArray(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + userID, PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + + pub = pubRing1.getPublicKey(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.initVerify(pub, "SC"); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID creation test"); + } + } + } + } + + private void checkSecretKeyRingWithPersonalCertificate(byte[] keyRing) + throws Exception + { + PGPSecretKeyRingCollection secCol = new PGPSecretKeyRingCollection(keyRing); + + + int count = 0; + + for (Iterator rIt = secCol.getKeyRings(); rIt.hasNext();) + { + PGPSecretKeyRing ring = (PGPSecretKeyRing)rIt.next(); + + for (Iterator it = ring.getExtraPublicKeys(); it.hasNext();) + { + it.next(); + count++; + } + } + + if (count != 1) + { + fail("personal certificate data subkey not found - count = " + count); + } + } + + private void checkPublicKeyRingWithX509(byte[] keyRing) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(keyRing); + Iterator it = pubRing.getPublicKeys(); + + if (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + Iterator sIt = key.getSignatures(); + + if (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + if (sig.getKeyAlgorithm() != 100) + { + fail("experimental signature not found"); + } + if (!areEqual(sig.getSignature(), Hex.decode("000101"))) + { + fail("experimental encoding check failed"); + } + } + else + { + fail("no signature found"); + } + } + else + { + fail("no key found"); + } + } + + public void performTest() + throws Exception + { + try + { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + revocationTest(); + test8(); + test9(); + test10(); + test11(); + generateTest(); + generateSha1Test(); + rewrapTest(); + rewrapTestV3(); + rewrapTestMD5(); + testPublicKeyRingWithX509(); + testSecretKeyRingWithPersonalCertificate(); + insertMasterTest(); + testUmlaut(); + } + catch (PGPException e) + { + if (((PGPException)e).getUnderlyingException() != null) + { + Exception ex = ((PGPException)e).getUnderlyingException(); + fail("exception: " + ex, ex); + } + else + { + fail("exception: " + e, e); + } + } + } + + public String getName() + { + return "PGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPKeyRingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPMarkerTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPMarkerTest.java new file mode 100644 index 000000000..6b078b1f1 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPMarkerTest.java @@ -0,0 +1,105 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPMarker; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTestResult; +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class PGPMarkerTest + implements Test +{ + private byte[] message1 = Base64.decode( + "qANQR1DBwU4DdrlXatQSHgoQCADWlhY3bWWaOTm4t2espRWPFQmETeinnieHce64" + + "lmEIFzaryEWeSdQc8XGfDzcb7sxq7b5b9Hm6OrACcCbSp2KGEJNG5kJmo2A16UPq" + + "JdK4xNelpJRh3KcJPv+N/9VJrMdj4C+DRnGNFg1hTQf3RKsX+ms2V0OBC5vGlOZY" + + "zX+XZz/7hl1PXVLN23u4npZI/1xETI2VtRoM76S6oykGXxMtT3+sGU1fAVEKVS45" + + "pyQHWbBqApkWrURq0xBqpVfDwOgGw09dJxt2igW9hjvNAd9tJiMGrMF5o2OLlub7" + + "c7FiK+dWLLcw+nx7Hl6FQmo9E8qyW8x1Cb78HjR/JXMgH/ngB/4gba6xX+s5TJkW" + + "H2Wpp5ePTw39EqHosUMrm05R+C0ha3EyyaJIvKj2WWmImKu5PWo1t37Pi6KHFNC3" + + "wsYJMRKnnNtd34luMTOgLpDcdgClzfp2p6EqHMoB7Uj3etlLmbN+vpGgz9qkLBRV" + + "7MpR1yE9qrZNeGgbkry6N31w5E7HoAHu5JNcwxgzbJoj2lI8uvs6Gf7fEoQOuAPE" + + "W/SGlfR2BdBPiJ1yErMElc2O8LVS0wTwwifHpEsMV+1ntl1EC5d052lo+6q7zNqD" + + "uYt1/2if6h9W9fe+S9mzr0ZAtxIN2ZGOFJJRnqzjDQ4siB9nnwr6YgvUVRSr/lQB" + + "hDTd0bmjyWacCt0PPMJWchO6A5tzqKUpTWSYibpdks80kLQogQHsJTZd/kpS0I6f" + + "gD0HYYlMssZwhg2J2TWwXDpDTgQ6mzFKbGSdOSk/deTJj2+EubzxaZcxZEocCJA8" + + "bppCj4kLBnCj1LjYx7A="); + + private byte[] message2 = Base64.decode( + "qANQR1DBwU4DZlTzKj+E4aMQCADruFAojUIlHGcnswLIekvhbVnaHnbCt6Kp" + + "IL2zppmEIYJ9n1xCO1k+3Y5j9vNATbqCVWs1HD0aAL3PRI1eZ1l8GkIBCd2z" + + "tcZpSI/uyI/JCzVW2stCH0gpP2V7zcjk8HaIuBz4ZsyU9m7v6LwCDPB4CTrb" + + "Z5nn5Jm3eowonQsRL/3TpJtG+IjTaw29NbCBNNX8quM5LwfIsfWovqNv28r1" + + "aX8FsqoTRsWEfQ7dMV/swVGqv0PgKxqErdnZVJ2yOJqjLk+lBJT6zhqPijGV" + + "10pc68hdZxxLU1KZq25DAjS12xcAgagjRkOmYE/H1oEjGZlXfS4y/xQ7skHa" + + "HI+b04vECACTpQPwCXhxYiNWnf4XhJPONIGyrsXVtsTNwzOShFPmeUvpipP4" + + "HknakBkBuUY49xcffQogW/NlGCZnQOulDLE6fCH/krkSmI8WVP5Vhf6bM1Qm" + + "92dHZFoTrrcQ9NVGaCNHHWf7KXkNfKdTkE23LdggoVrVAzO4WcdqVc6s/or7" + + "jQYP9zXLeu8+GGFMxe/9FCtoIWbujGQHsdDEkCK4h+D44EVDPzbvWj39ZB4w" + + "hHoab8RLHd7njcrPeoCPdYkFVCKOSuLdxxYZDbbmgpISaafrafwefkkESeGu" + + "JzbNhmyS8zfOiejWzndaLYWUSE/sqISK9Pg+xKundnFPk04+AhIRyYEoUjG3" + + "LgGVyM49mrM8E7QwAGU0m/VCJLoOu+N74Z1rp1wFdA5yCllFlONNM4Czhd1D" + + "ZMyLFqGXiKlyVCPlUTN2uVisYQGr6iNGYSPxpKjwiAzdeeQBPOETG0vd3nTO" + + "MN4BMKcG+kRJd5FU72SRfmbGwPPjd1gts9xFvtj4Tvpkam8="); + + public TestResult perform() + { + try + { + // + // test encrypted message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(message1); + + Object o; + + if (pgpFact.nextObject() instanceof PGPMarker) + { + if (pgpFact.nextObject() instanceof PGPEncryptedDataList) + { + return new SimpleTestResult(true, getName() + ": Okay"); + } + else + { + return new SimpleTestResult(false, getName() + ": error processing after marker."); + } + } + + pgpFact = new PGPObjectFactory(message2); + + if (pgpFact.nextObject() instanceof PGPMarker) + { + if (pgpFact.nextObject() instanceof PGPEncryptedDataList) + { + return new SimpleTestResult(true, getName() + ": Okay"); + } + else + { + return new SimpleTestResult(false, getName() + ": error processing after marker."); + } + } + + return new SimpleTestResult(false, getName() + ": marker not found"); + } + catch (Exception e) + { + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public String getName() + { + return "PGPMarkerTest"; + } + + public static void main( + String[] args) + { + Test test = new PGPMarkerTest(); + TestResult result = test.perform(); + + System.out.println(result.toString()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPNoPrivateKeyTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPNoPrivateKeyTest.java new file mode 100644 index 000000000..e4761fa4f --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPNoPrivateKeyTest.java @@ -0,0 +1,167 @@ +package org.spongycastle.openpgp.test; + +import java.security.Security; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; + +public class PGPNoPrivateKeyTest + extends SimpleTest +{ + String pgpOldPass = "test"; + String pgpNewPass = "newtest"; + String BOUNCY_CASTLE_PROVIDER_NAME = "SC"; + + byte[] pgpPrivateEmpty = Base64.decode( + "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" + + "+b3JLdM="); + + byte[] pgpPrivateFull = Base64.decode( + "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"); + + public void performTest() + throws Exception + { + PGPSecretKeyRing pgpSecRing = new PGPSecretKeyRing(pgpPrivateFull, new JcaKeyFingerprintCalculator()); + PGPSecretKey pgpSecKey = pgpSecRing.getSecretKey(); + boolean isFullEmpty = pgpSecKey.isPrivateKeyEmpty(); + + pgpSecRing = new PGPSecretKeyRing(pgpPrivateEmpty, new JcaKeyFingerprintCalculator()); + pgpSecKey = pgpSecRing.getSecretKey(); + boolean isEmptyEmpty = pgpSecKey.isPrivateKeyEmpty(); + + // + // Check isPrivateKeyEmpty() is public + // + + if (isFullEmpty || !isEmptyEmpty) + { + fail("Empty private keys not detected correctly."); + } + + // + // Check copyWithNewPassword doesn't throw an exception for secret + // keys without private keys (PGPException: unknown S2K type: 101). + // + + try + { + PGPSecretKey pgpChangedKey = PGPSecretKey.copyWithNewPassword(pgpSecKey, + new JcePBESecretKeyDecryptorBuilder( + new JcaPGPDigestCalculatorProviderBuilder().setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(pgpOldPass.toCharArray()), + new JcePBESecretKeyEncryptorBuilder(pgpSecKey.getKeyEncryptionAlgorithm()).build(pgpNewPass.toCharArray())); + } + catch (PGPException e) + { + if (!e.getMessage().equals("no private key in this SecretKey - public key present only.")) + { + fail("wrong exception."); + } + } + } + + public String getName() + { + return "PGPNoPrivateKeyTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPNoPrivateKeyTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPBETest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPBETest.java new file mode 100644 index 000000000..67e759a7a --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPBETest.java @@ -0,0 +1,396 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; + +public class PGPPBETest + extends SimpleTest +{ + private static final Date TEST_DATE = new Date(1062200111000L); + + byte[] enc1 = Base64.decode( + "jA0EAwMC5M5wWBP2HBZgySvUwWFAmMRLn7dWiZN6AkQMvpE3b6qwN3SSun7zInw2" + + "hxxdgFzVGfbjuB8w"); + + byte[] enc1crc = Base64.decode("H66L"); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + /** + * Message with both PBE and symmetric + */ + byte[] testPBEAsym = Base64.decode( + "hQIOA/ZlQEFWB5vuEAf/covEUaBve7NlWWdiO5NZubdtTHGElEXzG9hyBycp9At8" + + "nZGi27xOZtEGFQo7pfz4JySRc3O0s6w7PpjJSonFJyNSxuze2LuqRwFWBYYcbS8/" + + "7YcjB6PqutrT939OWsozfNqivI9/QyZCjBvFU89pp7dtUngiZ6MVv81ds2I+vcvk" + + "GlIFcxcE1XoCIB3EvbqWNaoOotgEPT60unnB2BeDV1KD3lDRouMIYHfZ3SzBwOOI" + + "6aK39sWnY5sAK7JjFvnDAMBdueOiI0Fy+gxbFD/zFDt4cWAVSAGTC4w371iqppmT" + + "25TM7zAtCgpiq5IsELPlUZZnXKmnYQ7OCeysF0eeVwf+OFB9fyvCEv/zVQocJCg8" + + "fWxfCBlIVFNeNQpeGygn/ZmRaILvB7IXDWP0oOw7/F2Ym66IdYYIp2HeEZv+jFwa" + + "l41w5W4BH/gtbwGjFQ6CvF/m+lfUv6ZZdzsMIeEOwhP5g7rXBxrbcnGBaU+PXbho" + + "gjDqaYzAWGlrmAd6aPSj51AGeYXkb2T1T/yoJ++M3GvhH4C4hvitamDkksh/qRnM" + + "M/s8Nku6z1+RXO3M6p5QC1nlAVqieU8esT43945eSoC77K8WyujDNbysDyUCUTzt" + + "p/aoQwe/HgkeOTJNelKR9y2W3xinZLFzep0SqpNI/e468yB/2/LGsykIyQa7JX6r" + + "BYwuBAIDAkOKfv5rK8v0YDfnN+eFqwhTcrfBj5rDH7hER6nW3lNWcMataUiHEaMg" + + "o6Q0OO1vptIGxW8jClTD4N1sCNwNu9vKny8dKYDDHbCjE06DNTv7XYVW3+JqTL5E" + + "BnidvGgOmA=="); + + /** + * decrypt the passed in message stream + */ + private byte[] decryptMessage( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(pass, "SC"); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + int ch; + + while ((ch = unc.read()) >= 0) + { + bOut.write(ch); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + private byte[] decryptMessageBuffered( + byte[] message, + Date date) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(message); + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); + + InputStream clear = pbe.getDataStream(pass, "SC"); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + PGPCompressedData cData = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(cData.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + if (!ld.getFileName().equals("test.txt") + && !ld.getFileName().equals("_CONSOLE")) + { + fail("wrong filename in packet"); + } + if (!ld.getModificationTime().equals(date)) + { + fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); + } + + InputStream unc = ld.getInputStream(); + byte[] buf = new byte[1024]; + int len; + + while ((len = unc.read(buf)) >= 0) + { + bOut.write(buf, 0, len); + } + + if (pbe.isIntegrityProtected() && !pbe.verify()) + { + fail("integrity check failed"); + } + + return bOut.toByteArray(); + } + + public void performTest() + throws Exception + { + byte[] out = decryptMessage(enc1, TEST_DATE); + + if (out[0] != 'h' || out[1] != 'e' || out[2] != 'l') + { + fail("wrong plain text in packet"); + } + + // + // create a PBE encrypted message and read it back. + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // encryption step - convert to literal data, compress, encode. + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + Date cDate = new Date((System.currentTimeMillis() / 1000) * 1000); + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + OutputStream comOut = comData.open(new UncloseableOutputStream(bOut)); + OutputStream ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + text.length, + cDate); + + ldOut.write(text); + + ldOut.close(); + + comOut.close(); + + // + // encrypt - with stream close + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, new SecureRandom(), "SC"); + + cPk.addMethod(pass); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - with generator close + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, new SecureRandom(), "SC"); + + cPk.addMethod(pass); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(bOut.toByteArray()); + + cPk.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // encrypt - partial packet style. + // + SecureRandom rand = new SecureRandom(); + byte[] test = new byte[1233]; + + rand.nextBytes(test); + + bOut = new ByteArrayOutputStream(); + + comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + comOut = comData.open(bOut); + lData = new PGPLiteralDataGenerator(); + + ldOut = lData.open(new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, TEST_DATE, + new byte[16]); + + + ldOut.write(test); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, rand, "SC"); + + cPk.addMethod(pass); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // with integrity packet + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, rand, "SC"); + + cPk.addMethod(pass); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), TEST_DATE); + if (!areEqual(out, test)) + { + fail("wrong plain text in buffer generated packet"); + } + + // + // sample message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPBEAsym); + + PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpFact.nextObject(); + + PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(1); + + InputStream clear = pbe.getDataStream("password".toCharArray(), "SC"); + + pgpFact = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + InputStream unc = ld.getInputStream(); + int ch; + + while ((ch = unc.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), Hex.decode("5361742031302e30322e30370d0a"))) + { + fail("data mismatch on combined PBE"); + } + + // + // with integrity packet - one byte message + // + byte[] msg = new byte[1]; + bOut = new ByteArrayOutputStream(); + + comData = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + lData = new PGPLiteralDataGenerator(); + comOut = comData.open(new UncloseableOutputStream(bOut)); + ldOut = lData.open( + new UncloseableOutputStream(comOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + msg.length, + cDate); + + ldOut.write(msg); + + ldOut.close(); + + comOut.close(); + + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, rand, "SC"); + + cPk.addMethod(pass); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), new byte[16]); + + cOut.write(bOut.toByteArray()); + + cOut.close(); + + out = decryptMessage(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in generated packet"); + } + + // + // decrypt with buffering + // + out = decryptMessageBuffered(cbOut.toByteArray(), cDate); + if (!areEqual(out, msg)) + { + fail("wrong plain text in buffer generated packet"); + } + } + + public String getName() + { + return "PGPPBETest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPPBETest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPacketTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPacketTest.java new file mode 100644 index 000000000..084024f5f --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPPacketTest.java @@ -0,0 +1,103 @@ +package org.spongycastle.openpgp.test; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Security; +import java.util.Date; +import java.util.Random; + +public class PGPPacketTest + extends SimpleTest +{ + private static int MAX = 32000; + + private void readBackTest( + PGPLiteralDataGenerator generator) + throws IOException + { + Random rand = new Random(); + byte[] buf = new byte[MAX]; + + rand.nextBytes(buf); + + for (int i = 1; i <= 200; i++) + { + bufferTest(generator, buf, i); + } + + bufferTest(generator, buf, 8382); + bufferTest(generator, buf, 8383); + bufferTest(generator, buf, 8384); + bufferTest(generator, buf, 8385); + + for (int i = 200; i < MAX; i += 100) + { + bufferTest(generator, buf, i); + } + } + + private void bufferTest( + PGPLiteralDataGenerator generator, + byte[] buf, + int i) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream out = generator.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + PGPLiteralData.CONSOLE, + i, + new Date()); + + out.write(buf, 0, i); + + generator.close(); + + PGPObjectFactory fact = new PGPObjectFactory(bOut.toByteArray()); + PGPLiteralData data = (PGPLiteralData)fact.nextObject(); + InputStream in = data.getInputStream(); + + for (int count = 0; count != i; count++) + { + if (in.read() != (buf[count] & 0xff)) + { + fail("failed readback test - length = " + i); + } + } + } + + public void performTest() + throws IOException + { + PGPLiteralDataGenerator oldGenerator = new PGPLiteralDataGenerator(true); + + readBackTest(oldGenerator); + + PGPLiteralDataGenerator newGenerator = new PGPLiteralDataGenerator(false); + + readBackTest(newGenerator); + } + + public String getName() + { + return "PGPPacketTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPPacketTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPParsingTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPParsingTest.java new file mode 100644 index 000000000..27ddb8ece --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPParsingTest.java @@ -0,0 +1,31 @@ +package org.spongycastle.openpgp.test; + +import java.security.Security; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.util.test.SimpleTest; + +public class PGPParsingTest + extends SimpleTest +{ + public void performTest() + throws Exception + { + PGPPublicKeyRingCollection pubRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(this.getClass().getResourceAsStream("bigpub.asc"))); + } + + public String getName() + { + return "PGPParsingTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPParsingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPRSATest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPRSATest.java new file mode 100644 index 000000000..74e83fdeb --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPRSATest.java @@ -0,0 +1,1471 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.attr.ImageAttribute; +import org.spongycastle.bcpg.sig.Features; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.PGPV3SignatureGenerator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPRSATest + extends SimpleTest +{ + byte[] testPubKey = Base64.decode( + "mIsEPz2nJAEEAOTVqWMvqYE693qTgzKv/TJpIj3hI8LlYPC6m1dk0z3bDLwVVk9F" + + "FAB+CWS8RdFOWt/FG3tEv2nzcoNdRvjv9WALyIGNawtae4Ml6oAT06/511yUzXHO" + + "k+9xK3wkXN5jdzUhf4cA2oGpLSV/pZlocsIDL+jCUQtumUPwFodmSHhzAAYptC9F" + + "cmljIEVjaGlkbmEgKHRlc3Qga2V5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPoi4" + + "BBMBAgAiBQI/PackAhsDBQkAg9YABAsHAwIDFQIDAxYCAQIeAQIXgAAKCRA1WGFG" + + "/fPzc8WMA/9BbjuB8E48QAlxoiVf9U8SfNelrz/ONJA/bMvWr/JnOGA9PPmFD5Uc" + + "+kV/q+i94dEMjsC5CQ1moUHWSP2xlQhbOzBP2+oPXw3z2fBs9XJgnTH6QWMAAvLs" + + "3ug9po0loNHLobT/D/XdXvcrb3wvwvPT2FptZqrtonH/OdzT9JdfrA=="); + + byte[] testPrivKey = Base64.decode( + "lQH8BD89pyQBBADk1aljL6mBOvd6k4Myr/0yaSI94SPC5WDwuptXZNM92wy8FVZP" + + "RRQAfglkvEXRTlrfxRt7RL9p83KDXUb47/VgC8iBjWsLWnuDJeqAE9Ov+ddclM1x" + + "zpPvcSt8JFzeY3c1IX+HANqBqS0lf6WZaHLCAy/owlELbplD8BaHZkh4cwAGKf4D" + + "AwKbLeIOVYTEdWD5v/YgW8ERs0pDsSIfBTvsJp2qA798KeFuED6jGsHUzdi1M990" + + "6PRtplQgnoYmYQrzEc6DXAiAtBR4Kuxi4XHx0ZR2wpVlVxm2Ypgz7pbBNWcWqzvw" + + "33inl7tR4IDsRdJOY8cFlN+1tSCf16sDidtKXUVjRjZNYJytH18VfSPlGXMeYgtw" + + "3cSGNTERwKaq5E/SozT2MKTiORO0g0Mtyz+9MEB6XVXFavMun/mXURqbZN/k9BFb" + + "z+TadpkihrLD1xw3Hp+tpe4CwPQ2GdWKI9KNo5gEnbkJgLrSMGgWalPhknlNHRyY" + + "bSq6lbIMJEE3LoOwvYWwweR1+GrV9farJESdunl1mDr5/d6rKru+FFDwZM3na1IF" + + "4Ei4FpqhivZ4zG6pN5XqLy+AK85EiW4XH0yAKX1O4YlbmDU4BjxhiwTdwuVMCjLO" + + "5++jkz5BBQWdFX8CCMA4FJl36G70IbGzuFfOj07ly7QvRXJpYyBFY2hpZG5hICh0" + + "ZXN0IGtleSkgPGVyaWNAYm91bmN5Y2FzdGxlLm9yZz6IuAQTAQIAIgUCPz2nJAIb" + + "AwUJAIPWAAQLBwMCAxUCAwMWAgECHgECF4AACgkQNVhhRv3z83PFjAP/QW47gfBO" + + "PEAJcaIlX/VPEnzXpa8/zjSQP2zL1q/yZzhgPTz5hQ+VHPpFf6voveHRDI7AuQkN" + + "ZqFB1kj9sZUIWzswT9vqD18N89nwbPVyYJ0x+kFjAALy7N7oPaaNJaDRy6G0/w/1" + + "3V73K298L8Lz09habWaq7aJx/znc0/SXX6w="); + + byte[] testPubKeyV3 = Base64.decode( + "mQCNAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURtApGSVhDSVRZX1FBiQCVAwUQP7O+UZ6Fwdi2fBUJAQFMwwQA" + + "qRnFsdg4xQnB8Y5d4cOpXkIn9AZgYS3cxtuSJB84vG2CgC39nfv4c+nlLkWP" + + "4puG+mZuJNgVoE84cuAF4I//1anKjlU7q1M6rFQnt5S4uxPyG3dFXmgyU1b4" + + "PBOnA0tIxjPzlIhJAMsPCGGA5+5M2JP0ad6RnzqzE3EENMX+GqY="); + + byte[] testPrivKeyV3 = Base64.decode( + "lQHfAz+zvlEAAAEEAMS22jgXbOZ/D3xWgM2kauSdzrwlU7Ms5hDW05ObqQyO" + + "FfQoKKMhfupyoa7J3x04VVBKu6Eomvr1es+VImH0esoeWFFahNOYq/I+jRRB" + + "woOhAGZ5UB2/hRd7rFmxqp6sCXi8wmLO2tAorlTzAiNNvl7xF4cQZpc0z56F" + + "wdi2fBUJAAURAXWwRBZQHNikA/f0ScLLjrXi4s0hgQecg+dkpDow94eu5+AR" + + "0DzZnfurpgfUJCNiDi5W/5c3Zj/xyrfMAgkbCgJ1m6FZqAQh7Mq73l7Kfu4/" + + "XIkyDF3tDgRuZNezB+JuElX10tV03xumHepp6M6CfhXqNJ15F33F99TA5hXY" + + "CPYD7SiSOpIhQkCOAgDAA63imxbpuKE2W7Y4I1BUHB7WQi8ZdkZd04njNTv+" + + "rFUuOPapQVfbWG0Vq8ld3YmJB4QWsa2mmqn+qToXbwufAgBpXkjvqK5yPiHF" + + "Px2QbFc1VqoCJB6PO5JRIqEiUZBFGdDlLxt3VSyqz7IZ/zEnxZq+tPCGGGSm" + + "/sAGiMvENcHVAfy0kTXU42TxEAYJyyNyqjXOobDJpEV1mKhFskRXt7tbMfOS" + + "Yf91oX8f6xw6O2Nal+hU8dS0Bmfmk5/enHmvRLHQocO0CkZJWENJVFlfUUE="); + + byte[] sig1 = Base64.decode( + "owGbwMvMwMRoGpHo9vfz52LGNTJJnBmpOTn5eiUVJfb23JvAHIXy/KKcFEWuToap" + + "zKwMIGG4Bqav0SwMy3yParsEKi2LMGI9xhh65sBxb05n5++ZLcWNJ/eLFKdWbm95" + + "tHbDV7GMwj/tUctUpFUXWPYFCLdNsDiVNuXbQvZtdXV/5xzY+9w1nCnijH9JoNiJ" + + "22n2jo0zo30/TZLo+jDl2vTzIvPeLEsPM3ZUE/1Ytqs4SG2TxIQbH7xf3uzcYXq2" + + "5Fw9AA=="); + + byte[] sig1crc = Base64.decode("+3i0"); + + byte[] subKey = Base64.decode( + "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" + + "CphiUYWnsC0mQ+J15B4="); + + byte[] enc1 = Base64.decode( + "hIwDKwfQexPJboABA/4/7prhYYMORTiQ5avQKx0XYpCLujzGefYjnyuWZnx3Iev8" + + "Pmsguumm+OLLvtXhhkXQmkJRXbIg6Otj2ubPYWflRPgpJSgOrNOreOl5jeABOrtw" + + "bV6TJb9OTtZuB7cTQSCq2gmYiSZkluIiDjNs3R3mEanILbYzOQ3zKSggKpzlv9JQ" + + "AZUqTyDyJ6/OUbJF5fI5uiv76DCsw1zyMWotUIu5/X01q+AVP5Ly3STzI7xkWg/J" + + "APz4zUHism7kSYz2viAQaJx9/bNnH3AM6qm1Fuyikl4="); + + byte[] enc1crc = Base64.decode("lv4o"); + + byte[] enc2 = Base64.decode( + "hIwDKwfQexPJboABBAC62jcJH8xKnKb1neDVmiovYON04+7VQ2v4BmeHwJrdag1g" + + "Ya++6PeBlQ2Q9lSGBwLobVuJmQ7cOnPUJP727JeSGWlMyFtMbBSHekOaTenT5lj7" + + "Zk7oRHxMp/hByzlMacIDzOn8LPSh515RHM57eDLCOwqnAxGQwk67GRl8f5dFH9JQ" + + "Aa7xx8rjCqPbiIQW6t5LqCNvPZOiSCmftll6+se1XJhFEuq8WS4nXtPfTiJ3vib4" + + "3soJdHzGB6AOs+BQ6aKmmNTVAxa5owhtSt1Z/6dfSSk="); + + byte[] subPubKey = Base64.decode( + "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"); + + byte[] subPubCrc = Base64.decode("rikt"); + + byte[] pgp8Key = Base64.decode( + "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF" + + "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd" + + "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy" + + "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y" + + "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7" + + "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO" + + "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP" + + "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY" + + "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb" + + "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4" + + "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj" + + "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I" + + "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH" + + "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt" + + "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j" + + "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw" + + "AgAA"); + + char[] pgp8Pass = "2002 Buffalo Sabres".toCharArray(); + + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + byte[] fingerprintKey = Base64.decode( + "mQEPA0CiJdUAAAEIAMI+znDlPd2kQoEcnxqxLcRz56Z7ttFKHpnYp0UkljZdquVc" + + "By1jMfXGVV64xN1IvMcyenLXUE0IUeUBCQs6tHunFRAPSeCxJ3FdFe1B5MpqQG8A" + + "BnEpAds/hAUfRDZD5y/lolk1hjvFMrRh6WXckaA/QQ2t00NmTrJ1pYUpkw9tnVQb" + + "LUjWJhfZDBBcN0ADtATzgkugxMtcDxR6I5x8Ndn+IilqIm23kxGIcmMd/BHOec4c" + + "jRwJXXDb7u8tl+2knAf9cwhPHp3+Zy4uGSQPdzQnXOhBlA+4WDa0RROOevWgq8uq" + + "8/9Xp/OlTVL+OoIzjsI6mJP1Joa4qmqAnaHAmXcAEQEAAbQoQk9BM1JTS1kgPEJP" + + "QSBNb25pdG9yaW5nIEAgODg4LTI2OS01MjY2PokBFQMFEECiJdWqaoCdocCZdwEB" + + "0RsH/3HPxoUZ3G3K7T3jgOnJUckTSHWU3XspHzMVgqOxjTrcexi5IsAM5M+BulfW" + + "T2aO+Kqf5w8cKTKgW02DNpHUiPjHx0nzDE+Do95zbIErGeK+Twkc4O/aVsvU9GGO" + + "81VFI6WMvDQ4CUAUnAdk03MRrzI2nAuhn4NJ5LQS+uJrnqUJ4HmFAz6CQZQKd/kS" + + "Xgq+A6i7aI1LG80YxWa9ooQgaCrb9dwY/kPQ+yC22zQ3FExtv+Fv3VtAKTilO3vn" + + "BA4Y9uTHuObHfI+1yxUS2PrlRUX0m48ZjpIX+cEN3QblGBJudI/A1QSd6P0LZeBr" + + "7F1Z1aF7ZDo0KzgiAIBvgXkeTpw="); + + byte[] fingerprintCheck = Base64.decode("CTv2"); + + byte[] expiry60and30daysSig13Key = Base64.decode( + "mQGiBENZt/URBAC5JccXiwe4g6MuviEC8NI/x0NaVkGFAOY04d5E4jeIycBP" + + "SrpOPrjETuigqhrj8oqed2+2yUqfnK4nhTsTAjyeJ3PpWC1pGAKzJgYmJk+K" + + "9aTLq0BQWiXDdv5RG6fDmeq1umvOfcXBqGFAguLPZC+U872bSLnfe3lqGNA8" + + "jvmY7wCgjhzVQVm10NN5ST8nemPEcSjnBrED/R494gHL6+r5OgUgXnNCDejA" + + "4InoDImQCF+g7epp5E1MB6CMYSg2WSY2jHFuHpwnUb7AiOO0ZZ3UBqM9rYnK" + + "kDvxkFCxba7Ms+aFj9blRNmy3vG4FewDcTdxzCtjUk6dRfu6UoARpqlTE/q7" + + "Xo6EQP1ncwJ+UTlcHkTBvg/usI/yBACGjBqX8glb5VfNaZgNHMeS/UIiUiuV" + + "SVFojiSDOHcnCe/6y4M2gVm38zz1W9qhoLfLpiAOFeL0yj6wzXvsjjXQiKQ8" + + "nBE4Mf+oeH2qiQ/LfzQrGpI5eNcMXrzK9nigmz2htYO2GjQfupEnu1RHBTH8" + + "NjofD2AShL9IO73plRuExrQgVGVzdCBLZXkgPHRlc3RAYm91bmN5Y2FzdGxl" + + "Lm9yZz6IZAQTEQIAJAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAUCQ1m4DgUJ" + + "AE8aGQAKCRD8QP1QuU7Kqw+eAJ0dZ3ZAqr73X61VmCkbyPoszLQMAQCfdFs2" + + "YMDeUvX34Q/8Ba0KgO5f3RSwAgADuM0EQ1m39hADAIHpVGcLqS9UkmQaWBvH" + + "WP6TnN7Y1Ha0TJOuxpbFjBW+CmVh/FjcsnavFXDXpo2zc742WT+vrHBSa/0D" + + "1QEBsnCaX5SRRVp7Mqs8q+aDhjcHMIP8Sdxf7GozXDORkrRaJwADBQL9HLYm" + + "7Rr5iYWDcvs+Pi6O1zUyb1tjkxEGaV/rcozl2MMmr2mzJ6x/Bz8SuhZEJS0m" + + "bB2CvAA39aQi9jHlV7q0SV73NOkd2L/Vt2UZhzlUdvrJ37PgYDv+Wd9Ufz6g" + + "MzLSiE8EGBECAA8FAkNZt/YCGwwFCQAnjQAACgkQ/ED9ULlOyqsTqQCcDnAZ" + + "7YymCfhm1yJiuFQg3qiX6Z4An19OSEgeSKugVcH49g1sxUB0zNdIsAIAAw=="); + + byte[] jpegImage = Base64.decode( + "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"); + + byte[] embeddedJPEGKey = Base64.decode( + "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" + + "Qvmtc8X7lqK/zLRVkSSBItik2IIhsAIAAw=="); + + + private void fingerPrintTest() + throws Exception + { + // + // version 3 + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(fingerprintKey); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("4FFB9F0884266C715D1CEAC804A3BBFA"))) + { + fail("version 3 fingerprint test failed"); + } + + // + // version 4 + // + pgpPub = new PGPPublicKeyRing(testPubKey); + + pubKey = pgpPub.getPublicKey(); + + if (!areEqual(pubKey.getFingerprint(), Hex.decode("3062363c1046a01a751946bb35586146fdf3f373"))) + { + fail("version 4 fingerprint test failed"); + } + } + + private void mixedTest(PGPPrivateKey pgpPrivKey, PGPPublicKey pgpPubKey) + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + // + // literal data + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, text.length, new Date()); + + lOut.write(text); + + lGen.close(); + + byte[] bytes = bOut.toByteArray(); + + PGPObjectFactory f = new PGPObjectFactory(bytes); + checkLiteralData((PGPLiteralData)f.nextObject(), text); + + ByteArrayOutputStream bcOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.AES_128, true, new SecureRandom(), "SC"); + + encGen.addMethod(pgpPubKey); + + encGen.addMethod("password".toCharArray()); + + OutputStream cOut = encGen.open(bcOut, bytes.length); + + cOut.write(bytes); + + cOut.close(); + + byte[] encData = bcOut.toByteArray(); + + // + // asymmetric + // + PGPObjectFactory pgpF = new PGPObjectFactory(encData); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(pgpPrivKey, "SC"); + + PGPObjectFactory pgpFact = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpFact.nextObject(), text); + + // + // PBE + // + pgpF = new PGPObjectFactory(encData); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPBEEncryptedData encPbe = (PGPPBEEncryptedData)encList.get(1); + + clear = encPbe.getDataStream("password".toCharArray(), "SC"); + + pgpF = new PGPObjectFactory(clear); + + checkLiteralData((PGPLiteralData)pgpF.nextObject(), text); + } + + private void checkLiteralData(PGPLiteralData ld, byte[] data) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals(PGPLiteralData.CONSOLE)) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + int ch; + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), data)) + { + fail("wrong plain text in decrypted packet"); + } + } + + private void existingEmbeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(embeddedJPEGKey); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + Iterator it = pubKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = pubKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature sig = (PGPSignature)sigs.next(); + + sig.initVerify(pubKey, "SC"); + + if (!sig.verifyCertification(attributes, pubKey)) + { + fail("signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find user attributes"); + } + } + + private void embeddedJpegTest() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + PGPSecretKeyRing pgpSec = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + PGPPublicKey pubKey = pgpPub.getPublicKey(); + + PGPUserAttributeSubpacketVectorGenerator vGen = new PGPUserAttributeSubpacketVectorGenerator(); + + vGen.setImageAttribute(ImageAttribute.JPEG, jpegImage); + + PGPUserAttributeSubpacketVector uVec = vGen.generate(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, "SC"); + + sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, pgpSec.getSecretKey().extractPrivateKey(pass, "SC")); + + PGPSignature sig = sGen.generateCertification(uVec, pubKey); + + PGPPublicKey nKey = PGPPublicKey.addCertification(pubKey, uVec, sig); + + Iterator it = nKey.getUserAttributes(); + int count = 0; + while (it.hasNext()) + { + PGPUserAttributeSubpacketVector attributes = (PGPUserAttributeSubpacketVector)it.next(); + + Iterator sigs = nKey.getSignaturesForUserAttribute(attributes); + int sigCount = 0; + while (sigs.hasNext()) + { + PGPSignature s = (PGPSignature)sigs.next(); + + s.initVerify(pubKey, "SC"); + + if (!s.verifyCertification(attributes, pubKey)) + { + fail("added signature failed verification"); + } + + sigCount++; + } + + if (sigCount != 1) + { + fail("Failed added user attributes signature check"); + } + count++; + } + + if (count != 1) + { + fail("didn't find added user attributes"); + } + + nKey = PGPPublicKey.removeCertification(nKey, uVec); + count = 0; + for (it = nKey.getUserAttributes(); it.hasNext();) + { + count++; + } + if (count != 0) + { + fail("found attributes where none expected"); + } + } + + private void sigsubpacketTest() + throws Exception + { + char[] passPhrase = "test".toCharArray(); + String identity = "TEST <test@test.org>"; + Date date = new Date(); + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC"); + kpg.initialize(2048); + KeyPair kpSgn = kpg.generateKeyPair(); + KeyPair kpEnc = kpg.generateKeyPair(); + + PGPKeyPair sgnKeyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_SIGN, kpSgn, date); + PGPKeyPair encKeyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpEnc, date); + + PGPSignatureSubpacketVector unhashedPcks = null; + PGPSignatureSubpacketGenerator svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setPrimaryUserID(true, true); + int[] encAlgs = {SymmetricKeyAlgorithmTags.AES_256, + SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.TRIPLE_DES}; + svg.setPreferredSymmetricAlgorithms(true, encAlgs); + int[] hashAlgs = {HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA512, + HashAlgorithmTags.SHA384, + HashAlgorithmTags.SHA256, + HashAlgorithmTags.RIPEMD160}; + svg.setPreferredHashAlgorithms(true, hashAlgs); + int[] comprAlgs = {CompressionAlgorithmTags.ZLIB, + CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP}; + svg.setPreferredCompressionAlgorithms(true, comprAlgs); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + svg.setKeyFlags(true, KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA); + PGPSignatureSubpacketVector hashedPcks = svg.generate(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + sgnKeyPair, identity, PGPEncryptedData.AES_256, passPhrase, + true, hashedPcks, unhashedPcks, new SecureRandom(), "SC"); + + svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setKeyFlags(true, KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); + svg.setPrimaryUserID(true, false); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + hashedPcks = svg.generate(); + + keyRingGen.addSubKey(encKeyPair, hashedPcks, unhashedPcks); + + byte[] encodedKeyRing = keyRingGen.generatePublicKeyRing().getEncoded(); + + PGPPublicKeyRing keyRing = new PGPPublicKeyRing(encodedKeyRing); + + for (Iterator it = keyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pKey = (PGPPublicKey)it.next(); + + if (pKey.isEncryptionKey()) + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (v.getKeyExpirationTime() != 86400L * 366 * 2) + { + fail("key expiration time wrong"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.isPrimaryUserID()) + { + fail("primary userID flag wrong"); + } + if (v.getKeyFlags() != KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE) + { + fail("keyFlags wrong"); + } + } + } + else + { + for (Iterator sit = pKey.getSignatures(); sit.hasNext();) + { + PGPSignature sig = (PGPSignature)sit.next(); + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (!Arrays.areEqual(v.getPreferredSymmetricAlgorithms(), encAlgs)) + { + fail("preferred encryption algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredHashAlgorithms(), hashAlgs)) + { + fail("preferred hash algs don't match"); + } + if (!Arrays.areEqual(v.getPreferredCompressionAlgorithms(), comprAlgs)) + { + fail("preferred compression algs don't match"); + } + if (!v.getFeatures().supportsFeature(Features.FEATURE_MODIFICATION_DETECTION)) + { + fail("features wrong"); + } + if (v.getKeyFlags() != KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA) + { + fail("keyFlags wrong"); + } + } + } + } + } + + private void multipleExpiryTest() + throws Exception + { + char[] passPhrase = "test".toCharArray(); + String identity = "TEST <test@test.org>"; + Date date = new Date(); + Security.addProvider(new BouncyCastleProvider()); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC"); + kpg.initialize(2048); + KeyPair kpSgn = kpg.generateKeyPair(); + KeyPair kpEnc = kpg.generateKeyPair(); + + PGPKeyPair sgnKeyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_SIGN, kpSgn, date); + PGPKeyPair encKeyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpEnc, date); + + PGPSignatureSubpacketVector unhashedPcks = null; + PGPSignatureSubpacketGenerator svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setPrimaryUserID(true, true); + int[] encAlgs = {SymmetricKeyAlgorithmTags.AES_256, + SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.TRIPLE_DES}; + svg.setPreferredSymmetricAlgorithms(true, encAlgs); + int[] hashAlgs = {HashAlgorithmTags.SHA1, + HashAlgorithmTags.SHA512, + HashAlgorithmTags.SHA384, + HashAlgorithmTags.SHA256, + HashAlgorithmTags.RIPEMD160}; + svg.setPreferredHashAlgorithms(true, hashAlgs); + int[] comprAlgs = {CompressionAlgorithmTags.ZLIB, + CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP}; + svg.setPreferredCompressionAlgorithms(true, comprAlgs); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + svg.setKeyFlags(true, KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA); + + PGPSignatureSubpacketVector hashedPcks = svg.generate(); + + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + sgnKeyPair, identity, + sha1Calc, hashedPcks, unhashedPcks, new JcaPGPContentSignerBuilder(sgnKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).setProvider("SC").build(passPhrase)); + + svg = new PGPSignatureSubpacketGenerator(); + svg.setKeyExpirationTime(true, 86400L * 366 * 2); + svg.setKeyFlags(true, KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); + svg.setPrimaryUserID(true, false); + svg.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + hashedPcks = svg.generate(); + + keyRingGen.addSubKey(encKeyPair, hashedPcks, unhashedPcks); + + byte[] encodedKeyRing = keyRingGen.generatePublicKeyRing().getEncoded(); + + PGPPublicKeyRing keyRing = new PGPPublicKeyRing(encodedKeyRing, new JcaKeyFingerprintCalculator()); + + for (Iterator it = keyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pKey = (PGPPublicKey)it.next(); + + PGPSignatureGenerator keySigGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PGPPublicKey.RSA_SIGN, HashAlgorithmTags.SHA1).setProvider("SC")); + + if (pKey.isMasterKey()) + { + keySigGen.init(PGPSignature.POSITIVE_CERTIFICATION, sgnKeyPair.getPrivateKey()); + } + else + { + keySigGen.init(PGPSignature.SUBKEY_BINDING, sgnKeyPair.getPrivateKey()); + } + + svg = new PGPSignatureSubpacketGenerator(); + + svg.setKeyExpirationTime(true, 86400L * 366 * 3); + + keySigGen.setHashedSubpackets(svg.generate()); + + pKey = PGPPublicKey.addCertification(pKey, pKey.isMasterKey() ? keySigGen.generateCertification(pKey) : keySigGen.generateCertification(sgnKeyPair.getPublicKey(), pKey)); + + if (pKey.isEncryptionKey()) + { + if (pKey.getValidSeconds() != 86400L * 366 * 3) + { + fail("key expiration time wrong"); + } + } + else + { + if (pKey.getValidSeconds() != 86400L * 366 * 3) + { + fail("key expiration time wrong"); + } + } + } + } + + public void performTest() + throws Exception + { + PublicKey pubKey = null; + + // + // Read the public key + // + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(testPubKey); + + pubKey = pgpPub.getPublicKey().getKey("SC"); + + Iterator it = pgpPub.getPublicKey().getUserIDs(); + + String uid = (String)it.next(); + + it = pgpPub.getPublicKey().getSignaturesForID(uid); + + PGPSignature sig = (PGPSignature)it.next(); + + sig.initVerify(pgpPub.getPublicKey(), "SC"); + + if (!sig.verifyCertification(uid, pgpPub.getPublicKey())) + { + fail("failed to verify certification"); + } + + // + // write a public key + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut); + + pgpPub.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPubKey)) + { + fail("public key rewrite failed"); + } + + // + // Read the public key + // + PGPPublicKeyRing pgpPubV3 = new PGPPublicKeyRing(testPubKeyV3); + PublicKey pubKeyV3 = pgpPub.getPublicKey().getKey("SC"); + + // + // write a V3 public key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPubV3.encode(pOut); + + // + // Read a v3 private key + // + char[] passP = "FIXCITY_QA".toCharArray(); + + if (!noIDEA()) + { + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKeyV3, new JcaKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(passP, "SC"); + + // + // write a v3 private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKeyV3)) + { + fail("private key V3 rewrite failed"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = pgpPriv.getSecretKey().extractPrivateKey(pass, "SC"); + + // + // write a private key + // + bOut = new ByteArrayOutputStream(); + pOut = new BCPGOutputStream(bOut); + + pgpPriv.encode(pOut); + + if (!areEqual(bOut.toByteArray(), testPrivKey)) + { + fail("private key rewrite failed"); + } + + + // + // test encryption + // + Cipher c = Cipher.getInstance("RSA", "SC"); + + c.init(Cipher.ENCRYPT_MODE, pubKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.doFinal(in); + + c.init(Cipher.DECRYPT_MODE, pgpPrivKey.getKey()); + + out = c.doFinal(out); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // test signature message + // + PGPObjectFactory pgpFact = new PGPObjectFactory(sig1); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + int ch; + + ops.initVerify(pgpPub.getPublicKey(ops.getKeyID()), "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + // + // encrypted message - read subkey + // + pgpPriv = new PGPSecretKeyRing(subKey, new JcaKeyFingerprintCalculator()); + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(enc1); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(pass, "SC"); + + InputStream clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt - short message + // + byte[] shortText = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o' }; + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.CAST5, new SecureRandom(), "SC"); + PGPPublicKey puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(puK); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), shortText.length); + + cOut.write(shortText); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(pass, "SC"); + + if (encP.getSymmetricAlgorithm(pgpPrivKey, "SC") != SymmetricKeyAlgorithmTags.CAST5) + { + fail("symmetric algorithm mismatch"); + } + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, shortText)) + { + fail("wrong plain text in generated short text packet"); + } + + // + // encrypt + // + cbOut = new ByteArrayOutputStream(); + cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.CAST5, new SecureRandom(), "SC"); + puK = pgpPriv.getSecretKey(encP.getKeyID()).getPublicKey(); + + cPk.addMethod(puK); + + cOut = cPk.open(new UncloseableOutputStream(cbOut), text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = pgpPriv.getSecretKey(encP.getKeyID()).extractPrivateKey(pass, "SC"); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // read public key with sub key. + // + pgpF = new PGPObjectFactory(subPubKey); + Object o; + + while ((o = pgpFact.nextObject()) != null) + { + // System.out.println(o); + } + + // + // key pair generation - CAST5 encryption + // + char[] passPhrase = "hello".toCharArray(); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC"); + + kpg.initialize(1024); + + KeyPair kp = kpg.generateKeyPair(); + + PGPSecretKey secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, PublicKeyAlgorithmTags.RSA_GENERAL, kp.getPublic(), kp.getPrivate(), new Date(), "fred", SymmetricKeyAlgorithmTags.CAST5, passPhrase, null, null, new SecureRandom(), "SC"); + + PGPPublicKey key = secretKey.getPublicKey(); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.initVerify(key, "SC"); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(passPhrase, "SC"); + + key = PGPPublicKey.removeCertification(key, uid, sig); + + if (key == null) + { + fail("failed certification removal"); + } + + byte[] keyEnc = key.getEncoded(); + + key = PGPPublicKey.addCertification(key, uid, sig); + + keyEnc = key.getEncoded(); + + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, "SC"); + + sGen.initSign(PGPSignature.KEY_REVOCATION, secretKey.extractPrivateKey(passPhrase, "SC")); + + sig = sGen.generateCertification(key); + + key = PGPPublicKey.addCertification(key, sig); + + keyEnc = key.getEncoded(); + + PGPPublicKeyRing tmpRing = new PGPPublicKeyRing(keyEnc); + + key = tmpRing.getPublicKey(); + + Iterator sgIt = key.getSignaturesOfType(PGPSignature.KEY_REVOCATION); + + sig = (PGPSignature)sgIt.next(); + + sig.initVerify(key, "SC"); + + if (!sig.verifyCertification(key)) + { + fail("failed to verify revocation certification"); + } + + // + // use of PGPKeyPair + // + PGPKeyPair pgpKp = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL , kp, new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + k1.getEncoded(); + + mixedTest(k2, k1); + + // + // key pair generation - AES_256 encryption. + // + kp = kpg.generateKeyPair(); + + secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, PublicKeyAlgorithmTags.RSA_GENERAL, kp.getPublic(), kp.getPrivate(), new Date(), "fred", SymmetricKeyAlgorithmTags.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + secretKey.extractPrivateKey(passPhrase, "SC"); + + secretKey.encode(new ByteArrayOutputStream()); + + // + // secret key password changing. + // + String newPass = "newPass"; + + secretKey = PGPSecretKey.copyWithNewPassword(secretKey, passPhrase, newPass.toCharArray(), secretKey.getKeyEncryptionAlgorithm(), new SecureRandom(), "SC"); + + secretKey.extractPrivateKey(newPass.toCharArray(), "SC"); + + secretKey.encode(new ByteArrayOutputStream()); + + key = secretKey.getPublicKey(); + + key.encode(new ByteArrayOutputStream()); + + it = key.getUserIDs(); + + uid = (String)it.next(); + + it = key.getSignaturesForID(uid); + + sig = (PGPSignature)it.next(); + + sig.initVerify(key, "SC"); + + if (!sig.verifyCertification(uid, key)) + { + fail("failed to verify certification"); + } + + pgpPrivKey = secretKey.extractPrivateKey(newPass.toCharArray(), "SC"); + + // + // signature generation + // + String data = "hello world!"; + + bOut = new ByteArrayOutputStream(); + + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + + sGen = new PGPSignatureGenerator(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bcOut); + + bcOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved: " + p2.getModificationTime() + " " + testDate); + } + + dIn = p2.getInputStream(); + + ops.initVerify(secretKey.getPublicKey(), "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // signature generation - version 3 + // + bOut = new ByteArrayOutputStream(); + + testIn = new ByteArrayInputStream(data.getBytes()); + PGPV3SignatureGenerator sGenV3 = new PGPV3SignatureGenerator(PGPPublicKey.RSA_GENERAL, PGPUtil.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + bcOut = new BCPGOutputStream(cGen.open(bOut)); + + sGen.generateOnePassVersion(false).encode(bcOut); + + lGen = new PGPLiteralDataGenerator(); + lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.close(); + + sGen.generate().encode(bcOut); + + bcOut.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + dIn = p2.getInputStream(); + + ops.initVerify(secretKey.getPublicKey(), "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed v3 generated signature check"); + } + + // + // extract PGP 8 private key + // + pgpPriv = new PGPSecretKeyRing(pgp8Key); + + secretKey = pgpPriv.getSecretKey(); + + pgpPrivKey = secretKey.extractPrivateKey(pgp8Pass, "SC"); + + // + // expiry + // + testExpiry(expiry60and30daysSig13Key, 60, 30); + + fingerPrintTest(); + existingEmbeddedJpegTest(); + embeddedJpegTest(); + sigsubpacketTest(); + multipleExpiryTest(); + } + + private void testExpiry( + byte[] encodedRing, + int masterDays, + int subKeyDays) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(encodedRing); + PGPPublicKey k = pubRing.getPublicKey(); + + if (k.getValidDays() != masterDays) + { + fail("mismatch on master valid days."); + } + + Iterator it = pubRing.getPublicKeys(); + + it.next(); + + k = (PGPPublicKey)it.next(); + + if (k.getValidDays() != subKeyDays) + { + fail("mismatch on subkey valid days."); + } + } + + private boolean noIDEA() + { + try + { + Cipher.getInstance("IDEA", "SC"); + + return false; + } + catch (Exception e) + { + return true; + } + } + + public String getName() + { + return "PGPRSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPRSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPSignatureTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPSignatureTest.java new file mode 100644 index 000000000..4bb686d47 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPSignatureTest.java @@ -0,0 +1,905 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.SignatureException; +import java.util.Date; + +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.bcpg.sig.NotationData; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPV3SignatureGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.io.Streams; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPSignatureTest + extends SimpleTest +{ + private static final int[] NO_PREFERENCES = null; + private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.TRIPLE_DES }; + private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA256 }; + private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] { CompressionAlgorithmTags.ZLIB }; + + private static final int TEST_EXPIRATION_TIME = 10000; + private static final String TEST_USER_ID = "test user id"; + private static final byte[] TEST_DATA = "hello world!\nhello world!\n".getBytes(); + private static final byte[] TEST_DATA_WITH_CRLF = "hello world!\r\nhello world!\r\n".getBytes(); + + byte[] dsaKeyRing = Base64.decode( + "lQHhBD9HBzURBACzkxRCVGJg5+Ld9DU4Xpnd4LCKgMq7YOY7Gi0EgK92gbaa6+zQ" + + "oQFqz1tt3QUmpz3YVkm/zLESBBtC1ACIXGggUdFMUr5I87+1Cb6vzefAtGt8N5VV" + + "1F/MXv1gJz4Bu6HyxL/ncfe71jsNhav0i4yAjf2etWFj53zK6R+Ojg5H6wCgpL9/" + + "tXVfGP8SqFvyrN/437MlFSUEAIN3V6j/MUllyrZglrtr2+RWIwRrG/ACmrF6hTug" + + "Ol4cQxaDYNcntXbhlTlJs9MxjTH3xxzylyirCyq7HzGJxZzSt6FTeh1DFYzhJ7Qu" + + "YR1xrSdA6Y0mUv0ixD5A4nPHjupQ5QCqHGeRfFD/oHzD4zqBnJp/BJ3LvQ66bERJ" + + "mKl5A/4uj3HoVxpb0vvyENfRqKMmGBISycY4MoH5uWfb23FffsT9r9KL6nJ4syLz" + + "aRR0gvcbcjkc9Z3epI7gr3jTrb4d8WPxsDbT/W1tv9bG/EHawomLcihtuUU68Uej" + + "6/wZot1XJqu2nQlku57+M/V2X1y26VKsipolPfja4uyBOOyvbP4DAwIDIBTxWjkC" + + "GGAWQO2jy9CTvLHJEoTO7moHrp1FxOVpQ8iJHyRqZzLllO26OzgohbiPYz8u9qCu" + + "lZ9Xn7QzRXJpYyBFY2hpZG5hIChEU0EgVGVzdCBLZXkpIDxlcmljQGJvdW5jeWNh" + + "c3RsZS5vcmc+iFkEExECABkFAj9HBzUECwcDAgMVAgMDFgIBAh4BAheAAAoJEM0j" + + "9enEyjRDAlwAnjTjjt57NKIgyym7OTCwzIU3xgFpAJ0VO5m5PfQKmGJRhaewLSZD" + + "4nXkHg=="); + + char[] dsaPass = "hello world".toCharArray(); + + byte[] rsaKeyRing = Base64.decode( + "lQIEBEBXUNMBBADScQczBibewnbCzCswc/9ut8R0fwlltBRxMW0NMdKJY2LF" + + "7k2COeLOCIU95loJGV6ulbpDCXEO2Jyq8/qGw1qD3SCZNXxKs3GS8Iyh9Uwd" + + "VL07nMMYl5NiQRsFB7wOb86+94tYWgvikVA5BRP5y3+O3GItnXnpWSJyREUy" + + "6WI2QQAGKf4JAwIVmnRs4jtTX2DD05zy2mepEQ8bsqVAKIx7lEwvMVNcvg4Y" + + "8vFLh9Mf/uNciwL4Se/ehfKQ/AT0JmBZduYMqRU2zhiBmxj4cXUQ0s36ysj7" + + "fyDngGocDnM3cwPxaTF1ZRBQHSLewP7dqE7M73usFSz8vwD/0xNOHFRLKbsO" + + "RqDlLA1Cg2Yd0wWPS0o7+qqk9ndqrjjSwMM8ftnzFGjShAdg4Ca7fFkcNePP" + + "/rrwIH472FuRb7RbWzwXA4+4ZBdl8D4An0dwtfvAO+jCZSrLjmSpxEOveJxY" + + "GduyR4IA4lemvAG51YHTHd4NXheuEqsIkn1yarwaaj47lFPnxNOElOREMdZb" + + "nkWQb1jfgqO24imEZgrLMkK9bJfoDnlF4k6r6hZOp5FSFvc5kJB4cVo1QJl4" + + "pwCSdoU6luwCggrlZhDnkGCSuQUUW45NE7Br22NGqn4/gHs0KCsWbAezApGj" + + "qYUCfX1bcpPzUMzUlBaD5rz2vPeO58CDtBJ0ZXN0ZXIgPHRlc3RAdGVzdD6I" + + "sgQTAQIAHAUCQFdQ0wIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQs8JyyQfH" + + "97I1QgP8Cd+35maM2cbWV9iVRO+c5456KDi3oIUSNdPf1NQrCAtJqEUhmMSt" + + "QbdiaFEkPrORISI/2htXruYn0aIpkCfbUheHOu0sef7s6pHmI2kOQPzR+C/j" + + "8D9QvWsPOOso81KU2axUY8zIer64Uzqc4szMIlLw06c8vea27RfgjBpSCryw" + + "AgAA"); + + char[] rsaPass = "2002 Buffalo Sabres".toCharArray(); + + byte[] nullPacketsSubKeyBinding = Base64.decode( + "iDYEGBECAAAAACp9AJ9PlJCrFpi+INwG7z61eku2Wg1HaQCgl33X5Egj+Kf7F9CXIWj2iFCvQDo="); + + public void performTest() + throws Exception + { + // + // RSA tests + // + PGPSecretKeyRing pgpPriv = new PGPSecretKeyRing(rsaKeyRing, new JcaKeyFingerprintCalculator()); + PGPSecretKey secretKey = pgpPriv.getSecretKey(); + PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(rsaPass)); + + try + { + testSig(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + + fail("RSA wrong key test failed."); + } + catch (PGPException e) + { + // expected + } + + try + { + testSigV3(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + + fail("RSA V3 wrong key test failed."); + } + catch (PGPException e) + { + // expected + } + + // + // certifications + // + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.KEY_REVOCATION, pgpPrivKey); + + PGPSignature sig = sGen.generateCertification(secretKey.getPublicKey()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), secretKey.getPublicKey()); + + if (!sig.verifyCertification(secretKey.getPublicKey())) + { + fail("revocation verification failed."); + } + + PGPSecretKeyRing pgpDSAPriv = new PGPSecretKeyRing(dsaKeyRing, new JcaKeyFingerprintCalculator()); + PGPSecretKey secretDSAKey = pgpDSAPriv.getSecretKey(); + PGPPrivateKey pgpPrivDSAKey = secretDSAKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(dsaPass)); + + sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.SUBKEY_BINDING, pgpPrivDSAKey); + + PGPSignatureSubpacketGenerator unhashedGen = new PGPSignatureSubpacketGenerator(); + PGPSignatureSubpacketGenerator hashedGen = new PGPSignatureSubpacketGenerator(); + + hashedGen.setSignatureExpirationTime(false, TEST_EXPIRATION_TIME); + hashedGen.setSignerUserID(true, TEST_USER_ID); + hashedGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS); + hashedGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS); + hashedGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS); + + sGen.setHashedSubpackets(hashedGen.generate()); + sGen.setUnhashedSubpackets(unhashedGen.generate()); + + sig = sGen.generateCertification(secretDSAKey.getPublicKey(), secretKey.getPublicKey()); + + byte[] sigBytes = sig.getEncoded(); + + PGPObjectFactory f = new PGPObjectFactory(sigBytes); + + sig = ((PGPSignatureList) f.nextObject()).get(0); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), secretDSAKey.getPublicKey()); + + if (!sig.verifyCertification(secretDSAKey.getPublicKey(), secretKey.getPublicKey())) + { + fail("subkey binding verification failed."); + } + + PGPSignatureSubpacketVector hashedPcks = sig.getHashedSubPackets(); + PGPSignatureSubpacketVector unhashedPcks = sig.getUnhashedSubPackets(); + + if (hashedPcks.size() != 6) + { + fail("wrong number of hashed packets found."); + } + + if (unhashedPcks.size() != 1) + { + fail("wrong number of unhashed packets found."); + } + + if (!hashedPcks.getSignerUserID().equals(TEST_USER_ID)) + { + fail("test userid not matching"); + } + + if (hashedPcks.getSignatureExpirationTime() != TEST_EXPIRATION_TIME) + { + fail("test signature expiration time not matching"); + } + + if (unhashedPcks.getIssuerKeyID() != secretDSAKey.getKeyID()) + { + fail("wrong issuer key ID found in certification"); + } + + int[] prefAlgs = hashedPcks.getPreferredCompressionAlgorithms(); + preferredAlgorithmCheck("compression", PREFERRED_COMPRESSION_ALGORITHMS, prefAlgs); + + prefAlgs = hashedPcks.getPreferredHashAlgorithms(); + preferredAlgorithmCheck("hash", PREFERRED_HASH_ALGORITHMS, prefAlgs); + + prefAlgs = hashedPcks.getPreferredSymmetricAlgorithms(); + preferredAlgorithmCheck("symmetric", PREFERRED_SYMMETRIC_ALGORITHMS, prefAlgs); + + int[] criticalHashed = hashedPcks.getCriticalTags(); + + if (criticalHashed.length != 1) + { + fail("wrong number of critical packets found."); + } + + if (criticalHashed[0] != SignatureSubpacketTags.SIGNER_USER_ID) + { + fail("wrong critical packet found in tag list."); + } + + // + // no packets passed + // + sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.SUBKEY_BINDING, pgpPrivDSAKey); + + sGen.setHashedSubpackets(null); + sGen.setUnhashedSubpackets(null); + + sig = sGen.generateCertification(TEST_USER_ID, secretKey.getPublicKey()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), secretDSAKey.getPublicKey()); + + if (!sig.verifyCertification(TEST_USER_ID, secretKey.getPublicKey())) + { + fail("subkey binding verification failed."); + } + + hashedPcks = sig.getHashedSubPackets(); + + if (hashedPcks.size() != 1) + { + fail("found wrong number of hashed packets"); + } + + unhashedPcks = sig.getUnhashedSubPackets(); + + if (unhashedPcks.size() != 1) + { + fail("found wrong number of unhashed packets"); + } + + try + { + sig.verifyCertification(secretKey.getPublicKey()); + + fail("failed to detect non-key signature."); + } + catch (PGPException e) + { + // expected + } + + // + // override hash packets + // + sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1).setProvider("SC")); + + sGen.init(PGPSignature.SUBKEY_BINDING, pgpPrivDSAKey); + + hashedGen = new PGPSignatureSubpacketGenerator(); + + hashedGen.setSignatureCreationTime(false, new Date(0L)); + + sGen.setHashedSubpackets(hashedGen.generate()); + + sGen.setUnhashedSubpackets(null); + + sig = sGen.generateCertification(TEST_USER_ID, secretKey.getPublicKey()); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), secretDSAKey.getPublicKey()); + + if (!sig.verifyCertification(TEST_USER_ID, secretKey.getPublicKey())) + { + fail("subkey binding verification failed."); + } + + hashedPcks = sig.getHashedSubPackets(); + + if (hashedPcks.size() != 1) + { + fail("found wrong number of hashed packets in override test"); + } + + if (!hashedPcks.hasSubpacket(SignatureSubpacketTags.CREATION_TIME)) + { + fail("hasSubpacket test for creation time failed"); + } + + if (!hashedPcks.getSignatureCreationTime().equals(new Date(0L))) + { + fail("creation of overriden date failed."); + } + + prefAlgs = hashedPcks.getPreferredCompressionAlgorithms(); + preferredAlgorithmCheck("compression", NO_PREFERENCES, prefAlgs); + + prefAlgs = hashedPcks.getPreferredHashAlgorithms(); + preferredAlgorithmCheck("hash", NO_PREFERENCES, prefAlgs); + + prefAlgs = hashedPcks.getPreferredSymmetricAlgorithms(); + preferredAlgorithmCheck("symmetric", NO_PREFERENCES, prefAlgs); + + if (hashedPcks.getKeyExpirationTime() != 0) + { + fail("unexpected key expiration time found"); + } + + if (hashedPcks.getSignatureExpirationTime() != 0) + { + fail("unexpected signature expiration time found"); + } + + if (hashedPcks.getSignerUserID() != null) + { + fail("unexpected signer user ID found"); + } + + criticalHashed = hashedPcks.getCriticalTags(); + + if (criticalHashed.length != 0) + { + fail("critical packets found when none expected"); + } + + unhashedPcks = sig.getUnhashedSubPackets(); + + if (unhashedPcks.size() != 1) + { + fail("found wrong number of unhashed packets in override test"); + } + + // + // general signatures + // + testSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA256, secretKey.getPublicKey(), pgpPrivKey); + testSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA384, secretKey.getPublicKey(), pgpPrivKey); + testSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA512, secretKey.getPublicKey(), pgpPrivKey); + testSigV3(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + testTextSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF); + testTextSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF); + testTextSigV3(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF); + testTextSigV3(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF); + + // + // DSA Tests + // + pgpPriv = new PGPSecretKeyRing(dsaKeyRing, new JcaKeyFingerprintCalculator()); + secretKey = pgpPriv.getSecretKey(); + pgpPrivKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("SC").build(dsaPass)); + + try + { + testSig(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + + fail("DSA wrong key test failed."); + } + catch (PGPException e) + { + // expected + } + + try + { + testSigV3(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + + fail("DSA V3 wrong key test failed."); + } + catch (PGPException e) + { + // expected + } + + testSig(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + testSigV3(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey); + testTextSig(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF); + testTextSig(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF); + testTextSigV3(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA_WITH_CRLF, TEST_DATA_WITH_CRLF); + testTextSigV3(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1, secretKey.getPublicKey(), pgpPrivKey, TEST_DATA, TEST_DATA_WITH_CRLF); + + // special cases + // + testMissingSubpackets(nullPacketsSubKeyBinding); + + testMissingSubpackets(generateV3BinarySig(pgpPrivKey, PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1)); + + // keyflags + testKeyFlagsValues(); + + testSubpacketGenerator(); + } + + private void testSubpacketGenerator() + { + PGPSignatureSubpacketGenerator sGen = new PGPSignatureSubpacketGenerator(); + + String name1 = genString(64); + String value1 = genString(72); + + sGen.setNotationData(true, true, name1, value1); + + PGPSignatureSubpacketVector sVec = sGen.generate(); + + NotationData[] nd = sVec.getNotationDataOccurences(); + + if (nd.length != 1 || !nd[0].isHumanReadable()) + { + fail("length and readability test 1 failed"); + } + + if (!nd[0].getNotationName().equals(name1) || !nd[0].getNotationValue().equals(value1)) + { + fail("name/value test 1 failed"); + } + + String name2 = genString(256); + String value2 = genString(264); + + sGen.setNotationData(true, false, name2, value2); + + sVec = sGen.generate(); + + nd = sVec.getNotationDataOccurences(); + + if (nd.length != 2 || !nd[0].isHumanReadable() || nd[1].isHumanReadable()) + { + fail("length and readability test 2 failed"); + } + + if (!nd[0].getNotationName().equals(name1) || !nd[0].getNotationValue().equals(value1)) + { + fail("name/value test 2.1 failed"); + } + + if (!nd[1].getNotationName().equals(name2) || !nd[1].getNotationValue().equals(value2)) + { + fail("name/value test 2.2 failed"); + } + + String name3 = genString(0xffff); + String value3 = genString(0xffff); + + sGen.setNotationData(true, false, name3, value3); + + sVec = sGen.generate(); + + nd = sVec.getNotationDataOccurences(); + + if (nd.length != 3 || !nd[0].isHumanReadable() || nd[1].isHumanReadable() || nd[2].isHumanReadable()) + { + fail("length and readability test 3 failed"); + } + + if (!nd[0].getNotationName().equals(name1) || !nd[0].getNotationValue().equals(value1)) + { + fail("name/value test 3.1 failed"); + } + + if (!nd[1].getNotationName().equals(name2) || !nd[1].getNotationValue().equals(value2)) + { + fail("name/value test 3.2 failed"); + } + + if (!nd[2].getNotationName().equals(name3) || !nd[2].getNotationValue().equals(value3)) + { + fail("name/value test 3.3 failed"); + } + + String name4 = genString(0xffff1); + String value4 = genString(0xfffff); + + try + { + sGen.setNotationData(true, false, name4, value4); + fail("truncation occurs silently"); + } + catch (IllegalArgumentException e) + { + if (!"notationName exceeds maximum length.".equals(e.getMessage())) + { + fail("wrong message"); + } + } + + try + { + sGen.setNotationData(true, false, name3, value4); + fail("truncation occurs silently"); + } + catch (IllegalArgumentException e) + { + if (!"notationValue exceeds maximum length.".equals(e.getMessage())) + { + fail("wrong message"); + } + } + } + + private String genString(int length) + { + char[] chars = new char[length]; + + for (int i = 0; i != length; i++) + { + chars[i] = (char)('a' + (i % 26)); + } + + return new String(chars); + } + + private void testKeyFlagsValues() + { + checkValue(KeyFlags.CERTIFY_OTHER, 0x01); + checkValue(KeyFlags.SIGN_DATA, 0x02); + checkValue(KeyFlags.ENCRYPT_COMMS, 0x04); + checkValue(KeyFlags.ENCRYPT_STORAGE, 0x08); + checkValue(KeyFlags.SPLIT, 0x10); + checkValue(KeyFlags.AUTHENTICATION, 0x20); + checkValue(KeyFlags.SHARED, 0x80); + + // yes this actually happens + checkValue(new byte[] { 4, 0, 0, 0 }, 0x04); + checkValue(new byte[] { 4, 0, 0 }, 0x04); + checkValue(new byte[] { 4, 0 }, 0x04); + checkValue(new byte[] { 4 }, 0x04); + } + + private void checkValue(int flag, int value) + { + KeyFlags f = new KeyFlags(true, flag); + + if (f.getFlags() != value) + { + fail("flag value mismatch"); + } + } + + private void checkValue(byte[] flag, int value) + { + KeyFlags f = new KeyFlags(true, flag); + + if (f.getFlags() != value) + { + fail("flag value mismatch"); + } + } + + private void testMissingSubpackets(byte[] signature) + throws IOException + { + PGPObjectFactory f = new PGPObjectFactory(signature); + Object obj = f.nextObject(); + + while (!(obj instanceof PGPSignatureList)) + { + obj = f.nextObject(); + if (obj instanceof PGPLiteralData) + { + InputStream in = ((PGPLiteralData)obj).getDataStream(); + Streams.drain(in); + } + } + + PGPSignature sig = ((PGPSignatureList)obj).get(0); + + if (sig.getVersion() > 3) + { + PGPSignatureSubpacketVector v = sig.getHashedSubPackets(); + + if (v.getKeyExpirationTime() != 0) + { + fail("key expiration time not zero for missing subpackets"); + } + + if (!sig.hasSubpackets()) + { + fail("hasSubpackets() returns false with packets"); + } + } + else + { + if (sig.getHashedSubPackets() != null) + { + fail("hashed sub packets found when none expected"); + } + if (sig.getUnhashedSubPackets() != null) + { + fail("unhashed sub packets found when none expected"); + } + + if (sig.hasSubpackets()) + { + fail("hasSubpackets() returns true with no packets"); + } + } + } + + private void preferredAlgorithmCheck( + String type, + int[] expected, + int[] prefAlgs) + { + if (expected == null) + { + if (prefAlgs != null) + { + fail("preferences for " + type + " found when none expected"); + } + } + else + { + if (prefAlgs.length != expected.length) + { + fail("wrong number of preferred " + type + " algorithms found"); + } + + for (int i = 0; i != expected.length; i++) + { + if (expected[i] != prefAlgs[i]) + { + fail("wrong algorithm found for " + type + ": expected " + expected[i] + " got " + prefAlgs[i]); + } + } + } + } + + private void testSig( + int encAlgorithm, + int hashAlgorithm, + PGPPublicKey pubKey, + PGPPrivateKey privKey) + throws Exception + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(TEST_DATA); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(encAlgorithm, hashAlgorithm).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, privKey); + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + TEST_DATA.length * 2, + new Date()); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.write(TEST_DATA); + sGen.update(TEST_DATA); + + lGen.close(); + + sGen.generate().encode(bOut); + + verifySignature(bOut.toByteArray(), hashAlgorithm, pubKey, TEST_DATA); + } + + private void testTextSig( + int encAlgorithm, + int hashAlgorithm, + PGPPublicKey pubKey, + PGPPrivateKey privKey, + byte[] data, + byte[] canonicalData) + throws Exception + { + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(encAlgorithm, HashAlgorithmTags.SHA1).setProvider("SC")); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data); + Date creationTime = new Date(); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privKey); + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.TEXT, + "_CONSOLE", + data.length * 2, + creationTime); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.write(data); + sGen.update(data); + + lGen.close(); + + PGPSignature sig = sGen.generate(); + + if (sig.getCreationTime().getTime() == 0) + { + fail("creation time not set in v4 signature"); + } + + sig.encode(bOut); + + verifySignature(bOut.toByteArray(), hashAlgorithm, pubKey, canonicalData); + } + + private void testSigV3( + int encAlgorithm, + int hashAlgorithm, + PGPPublicKey pubKey, + PGPPrivateKey privKey) + throws Exception + { + byte[] bytes = generateV3BinarySig(privKey, encAlgorithm, hashAlgorithm); + + verifySignature(bytes, hashAlgorithm, pubKey, TEST_DATA); + } + + private byte[] generateV3BinarySig(PGPPrivateKey privKey, int encAlgorithm, int hashAlgorithm) + throws Exception + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(TEST_DATA); + PGPV3SignatureGenerator sGen = new PGPV3SignatureGenerator(new JcaPGPContentSignerBuilder(encAlgorithm, hashAlgorithm).setProvider("SC")); + + sGen.init(PGPSignature.BINARY_DOCUMENT, privKey); + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.BINARY, + "_CONSOLE", + TEST_DATA.length * 2, + new Date()); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.write(TEST_DATA); + sGen.update(TEST_DATA); + + lGen.close(); + + sGen.generate().encode(bOut); + + return bOut.toByteArray(); + } + + private void testTextSigV3( + int encAlgorithm, + int hashAlgorithm, + PGPPublicKey pubKey, + PGPPrivateKey privKey, + byte[] data, + byte[] canonicalData) + throws Exception + { + PGPV3SignatureGenerator sGen = new PGPV3SignatureGenerator(new JcaPGPContentSignerBuilder(encAlgorithm, HashAlgorithmTags.SHA1).setProvider("SC")); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data); + + sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privKey); + sGen.generateOnePassVersion(false).encode(bOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bOut), + PGPLiteralData.TEXT, + "_CONSOLE", + data.length * 2, + new Date()); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lOut.write(data); + sGen.update(data); + + lGen.close(); + + PGPSignature sig = sGen.generate(); + + if (sig.getCreationTime().getTime() == 0) + { + fail("creation time not set in v3 signature"); + } + + sig.encode(bOut); + + verifySignature(bOut.toByteArray(), hashAlgorithm, pubKey, canonicalData); + } + + private void verifySignature( + byte[] encodedSig, + int hashAlgorithm, + PGPPublicKey pubKey, + byte[] original) + throws IOException, PGPException, NoSuchProviderException, SignatureException + { + PGPObjectFactory pgpFact = new PGPObjectFactory(encodedSig); + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + PGPOnePassSignature ops = p1.get(0); + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + InputStream dIn = p2.getInputStream(); + + ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKey); + + int ch; + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + + Date creationTime = sig.getCreationTime(); + Date now = new Date(); + + // Check creationTime is recent + if (creationTime.after(now) + || creationTime.before(new Date(now.getTime() - 10 * 60 * 1000))) + { + fail("bad creation time in signature: " + creationTime); + } + + if (sig.getKeyID() != pubKey.getKeyID()) + { + fail("key id mismatch in signature"); + } + + if (!ops.verify(sig)) + { + fail("Failed generated signature check - " + hashAlgorithm); + } + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKey); + + for (int i = 0; i != original.length; i++) + { + sig.update(original[i]); + } + + sig.update(original); + + if (!sig.verify()) + { + fail("Failed generated signature check against original data"); + } + } + + public String getName() + { + return "PGPSignatureTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPSignatureTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPUnicodeTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPUnicodeTest.java new file mode 100644 index 000000000..27f9c2dea --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/PGPUnicodeTest.java @@ -0,0 +1,183 @@ +package org.spongycastle.openpgp.test; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.security.Security; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; + +public class PGPUnicodeTest + extends TestCase +{ + private static final String TEST_DATA_HOME = "bc.test.data.home"; + + public void setUp() + { + if (Security.getProvider("SC") == null) + { + Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); + } + } + + public void test_key(BigInteger keyId, String passphrase) + throws Exception + { + + PGPSecretKeyRingCollection secretKeyRing = loadSecretKeyCollection("secring.gpg"); + + PGPSecretKeyRing secretKey = secretKeyRing.getSecretKeyRing(keyId.longValue()); + assertNotNull("Could not locate secret keyring with Id=" + keyId.toString(16), secretKey); + + PGPSecretKey key = secretKey.getSecretKey(); + assertNotNull("Could not locate secret key!", key); + + try + { + PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(); + + PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(calcProvider) + .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passphrase.toCharArray()); + + PGPPrivateKey privateKey = key.extractPrivateKey(decryptor); + + assertTrue(privateKey.getKeyID() == keyId.longValue()); + + } + catch (PGPException e) + { + throw new PGPException("Password incorrect!", e); + } + + // all fine! + } + + public void test_UmlautPassphrase() + { + + try + { + BigInteger keyId = new BigInteger("362961283C48132B9F14C5C3EC87272EFCB986D2", 16); + + String passphrase = new String("Händle".getBytes("UTF-16"), "UTF-16"); +// FileInputStream passwordFile = new FileInputStream("testdata/passphrase_for_test.txt"); +// byte[] password = new byte[passwordFile.available()]; +// passwordFile.read(password); +// passwordFile.close(); +// String passphrase = new String(password); + + test_key(keyId, passphrase); + + // all fine! + + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + public void test_ASCIIPassphrase() + { + + try + { + BigInteger keyId = new BigInteger("A392B7310C64026022405257AA2AAAC7CB417459", 16); + + String passphrase = "Admin123"; + + test_key(keyId, passphrase); + + // all fine! + + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + public void test_CyrillicPassphrase() + { + + try + { + BigInteger keyId = new BigInteger("B7773AF32BE4EC1806B1BACC4680E7F3960C44E7", 16); + + // XXX The password text file must not have the UTF-8 BOM ! + // Ref: http://stackoverflow.com/questions/2223882/whats-different-between-utf-8-and-utf-8-without-bom + + FileInputStream passwordFile = new FileInputStream(getDataHome() + "passphrase_cyr.txt"); + Reader reader = new InputStreamReader(passwordFile, Charset.forName("UTF-8")); + BufferedReader in = new BufferedReader(reader); + String passphrase = in.readLine(); + in.close(); + passwordFile.close(); + + test_key(keyId, passphrase); + + // all fine! + + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + private PGPSecretKeyRingCollection loadSecretKeyCollection( + String keyName) + throws Exception + { + FileInputStream fIn = new FileInputStream(getDataHome() + keyName); + + return new PGPSecretKeyRingCollection(fIn); + } + + private String getDataHome() + { + String dataHome = System.getProperty(TEST_DATA_HOME); + + if (dataHome == null) + { + throw new IllegalStateException(TEST_DATA_HOME + " property not set"); + } + + return dataHome + "/openpgp/unicode/"; + } + + public static void main (String[] args) + throws Exception + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("Unicode Password tests"); + + suite.addTestSuite(PGPUnicodeTest.class); + + return suite; + } +} diff --git a/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/RegressionTest.java b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/RegressionTest.java new file mode 100644 index 000000000..b9e931956 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/java/org/spongycastle/openpgp/test/RegressionTest.java @@ -0,0 +1,49 @@ +package org.spongycastle.openpgp.test; + +import java.security.Security; + +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class RegressionTest +{ + public static Test[] tests = { + new BcPGPKeyRingTest(), + new PGPKeyRingTest(), + new BcPGPRSATest(), + new PGPRSATest(), + new BcPGPDSATest(), + new PGPDSATest(), + new BcPGPDSAElGamalTest(), + new PGPDSAElGamalTest(), + new BcPGPPBETest(), + new PGPPBETest(), + new PGPMarkerTest(), + new PGPPacketTest(), + new PGPArmoredTest(), + new PGPSignatureTest(), + new PGPClearSignedSignatureTest(), + new PGPCompressionTest(), + new PGPNoPrivateKeyTest(), + new PGPECDSATest(), + new PGPECDHTest(), + new PGPParsingTest() + }; + + public static void main( + String[] args) + { + Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); + + for (int i = 0; i != tests.length; i++) + { + TestResult result = tests[i].perform(); + System.out.println(result); + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + } + } +} + diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java new file mode 100644 index 000000000..bd98aecdb --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPDSAElGamalTest.java @@ -0,0 +1,564 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.AlgorithmParameterGenerator; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.spec.DHParameterSpec; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.encodings.PKCS1Encoding; +import org.spongycastle.crypto.engines.ElGamalEngine; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPKeyConverter; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class BcPGPDSAElGamalTest + extends SimpleTest +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + public void performTest() + throws Exception + { + try + { + PGPPublicKey pubKey; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + if (pubKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing, new BcKeyFingerprintCalculator()); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.DSA, PGPUtil.SHA1)); + + sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream( + cGen.open(new UncloseableOutputStream(bOut))); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + + Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); + OutputStream lOut = lGen.open( + new UncloseableOutputStream(bcOut), + PGPLiteralData.BINARY, + "_CONSOLE", + data.getBytes().length, + testDate); + + int ch; + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + lGen.close(); + + sGen.generate().encode(bcOut); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + if (!p2.getModificationTime().equals(testDate)) + { + fail("Modification time not preserved"); + } + + InputStream dIn = p2.getInputStream(); + + ops.init(new BcPGPContentVerifierBuilderProvider(), pubKey); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key suitable for encryption + // + long pgpKeyID = 0; + AsymmetricKeyParameter pKey = null; + BcPGPKeyConverter keyConverter = new BcPGPKeyConverter(); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = keyConverter.getPublicKey(pgpKey); + pgpKeyID = pgpKey.getKeyID(); + if (pgpKey.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + // + // verify the key + // + + } + } + + AsymmetricBlockCipher c = new PKCS1Encoding(new ElGamalEngine()); + + c.init(true, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.processBlock(in, 0, in.length); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + c.init(false, keyConverter.getPrivateKey(pgpPrivKey)); + + out = c.processBlock(out, 0, out.length); + + if (!areEqual(in, out)) + { + fail("decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPub.getPublicKey()); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + + if (!areEqual(bOut.toByteArray(), text)) + { + fail("wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom())); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass)); + + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + + // + // use of PGPKeyPair + // + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ElGamal", "SC"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + kpg.initialize(512); + + KeyPair kp = kpg.generateKeyPair(); + + PGPKeyPair pgpKp = new PGPKeyPair(PGPPublicKey.ELGAMAL_GENERAL , kp.getPublic(), kp.getPrivate(), new Date()); + + PGPPublicKey k1 = pgpKp.getPublicKey(); + + PGPPrivateKey k2 = pgpKp.getPrivateKey(); + + + + // Test bug with ElGamal P size != 0 mod 8 (don't use these sizes at home!) + SecureRandom random = new SecureRandom(); + for (int pSize = 257; pSize < 264; ++pSize) + { + // Generate some parameters of the given size + AlgorithmParameterGenerator a = AlgorithmParameterGenerator.getInstance("ElGamal", "SC"); + a.init(pSize, new SecureRandom()); + AlgorithmParameters params = a.generateParameters(); + + DHParameterSpec elP = (DHParameterSpec)params.getParameterSpec(DHParameterSpec.class); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal", "SC"); + + keyGen.initialize(512); + + + // Run a short encrypt/decrypt test with random key for the given parameters + kp = keyGen.generateKeyPair(); + + PGPKeyPair elGamalKeyPair = new PGPKeyPair( + PublicKeyAlgorithmTags.ELGAMAL_GENERAL, kp, new Date()); + + cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(random)); + + puK = elGamalKeyPair.getPublicKey(); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(puK)); + + cbOut = new ByteArrayOutputStream(); + + cOut = cPk.open(cbOut, text.length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = elGamalKeyPair.getPrivateKey(); + + // Note: This is where an exception would be expected if the P size causes problems + clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(pgpPrivKey)); + + ByteArrayOutputStream dec = new ByteArrayOutputStream(); + + int b; + while ((b = clear.read()) >= 0) + { + dec.write(b); + } + + byte[] decText = dec.toByteArray(); + + if (!areEqual(text, decText)) + { + fail("decrypted message incorrect"); + } + } + + // check sub key encoding + + it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (!pgpKey.isMasterKey()) + { + byte[] kEnc = pgpKey.getEncoded(); + + PGPObjectFactory objF = new PGPObjectFactory(kEnc); + + PGPPublicKey k = (PGPPublicKey)objF.nextObject(); + + pKey = keyConverter.getPublicKey(k); + pgpKeyID = k.getKeyID(); + if (k.getBitStrength() != 1024) + { + fail("failed - key strength reported incorrectly."); + } + + if (objF.nextObject() != null) + { + fail("failed - stream not fully parsed."); + } + } + } + + } + catch (PGPException e) + { + fail("exception: " + e.getMessage(), e.getUnderlyingException()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPDSAElGamalTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java new file mode 100644 index 000000000..ce4e5ac25 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/BcPGPKeyRingTest.java @@ -0,0 +1,2362 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.encoders.Hex; +import org.spongycastle.util.test.SimpleTest; + +public class BcPGPKeyRingTest + extends SimpleTest +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "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" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + char[] sec5pass1 = "12345678".toCharArray(); + + // + // Werner Koch "odd keys" + // + byte[] pub6 = Base64.decode( + "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" + + "L4Kb7TWkd/OHQScVBO8sTUz0+2g="); + + byte[] pub6check = Base64.decode("62O9"); + + // + // revoked sub key + // + byte[] pub7 = Base64.decode( + "mQGiBEFOsIwRBADcjRx7nAs4RaWsQU6p8/ECLZD9sSeYc6CN6UDI96RKj0/hCzMs" + + "qlA0+9fzGZ7ZEJ34nuvDKlhKGC7co5eOiE0a9EijxgcrZU/LClZWa4YfyNg/ri6I" + + "yTyfOfrPQ33GNQt2iImDf3FKp7XKuY9nIxicGQEaW0kkuAmbV3oh0+9q8QCg/+fS" + + "epDEqEE/+nKONULGizKUjMED/RtL6RThRftZ9DOSdBytGYd48z35pca/qZ6HA36K" + + "PVQwi7V77VKQyKFLTOXPLnVyO85hyYB/Nv4DFHN+vcC7/49lfoyYMZlN+LarckHi" + + "NL154wmmzygB/KKysvWBLgkErEBCD0xBDd89iTQNlDtVQAWGORVffl6WWjOAkliG" + + "3dL6A/9A288HfFRnywqi3xddriV6wCPmStC3dkCS4vHk2ofS8uw4ZNoRlp1iEPna" + + "ai2Xa9DX1tkhaGk2k96MqqbBdGpbW8sMA9otJ9xdMjWEm/CgJUFUFQf3zaVy3mkM" + + "S2Lvb6P4Wc2l/diEEIyK8+PqJItSh0OVU3K9oM7ngHwVcalKILQVUkV2b2tlZCA8" + + "UmV2b2tlZEB0ZWQ+iQBOBBARAgAOBQJBTrCMBAsDAgECGQEACgkQvglkcFA/c63+" + + "QgCguh8rsJbPTtbhZcrqBi5Mo1bntLEAoPZQ0Kjmu2knRUpHBeUemHDB6zQeuQIN" + + "BEFOsIwQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz" + + "0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRP" + + "xfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvN" + + "ILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dD" + + "ox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMI" + + "PWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/93zriSvSHqsi1FeEmUBo431Jkh" + + "VerIzb6Plb1j6FIq+s3vyvx9K+dMvjotZqylWZj4GXpH+2xLJTjWkrGSfUZVI2Nk" + + "nyOFxUCKLLqaqVBFAQIjULfvQfGEWiGQKk9aRLkdG+D+8Y2N9zYoBXoQ9arvvS/t" + + "4mlOsiuaTe+BZ4x+BXTpF4b9sKZl7V8QP/TkoJWUdydkvxciHdWp7ssqyiKOFRhG" + + "818knDfFQ3cn2w/RnOb+7AF9wDncXDPYLfpPv9b2qZoLrXcyvlLffGDUdWs553ut" + + "1F5AprMURs8BGmY9BnjggfVubHdhTUoA4gVvrdaf+D9NwZAl0xK/5Y/oPuMZiQBG" + + "BBgRAgAGBQJBTrCMAAoJEL4JZHBQP3Ot09gAoMmLKloVDP+WhDXnsM5VikxysZ4+" + + "AKCrJAUO+lYAyPYwEwgK+bKmUGeKrIkARgQoEQIABgUCQU6wpQAKCRC+CWRwUD9z" + + "rQK4AJ98kKFxGU6yhHPr6jYBJPWemTNOXgCfeGB3ox4PXeS4DJDuLy9yllytOjo="); + + byte[] pub7check = Base64.decode("f/YQ"); + + byte[] pub8 = Base64.decode( + "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" + + "9Uqz3fUvGoewAWA="); + + byte[] sec8 = Base64.decode( + "lQHpBEEcraYRBADFYj+uFOhHz5SdECvJ3Z03P47gzmWLQ5HH8fPYC9rrv7AgqFFX" + + "aWlJJVMLua9e6xoCiDWJs/n4BbZ/weL/11ELg6XqUnzFhYyz0H2KFsPgQ/b9lWLY" + + "MtcPMFy5jE33hv/ixHgYLFqoNaAIbg0lzYEW/otQ9IhRl16fO1Q/CQZZrQCg/9M2" + + "V2BTmm9RYog86CXJtjawRBcD/RIqU0zulxZ2Zt4javKVxrGIwW3iBU935ebmJEIK" + + "Y5EVkGKBOCvsApZ+RGzpYeR2uMsTnQi8RJgiAnjaoVPCdsVJE7uQ0h8XuJ5n5mJ2" + + "kLCFlF2hj5ViicZzse+crC12CGtgRe8z23ubLRcd6IUGhVutK8/b5knZ22vE14JD" + + "ykKdA/96ObzJQdiuuPsEWN799nUUCaYWPAoLAmiXuICSP4GEnxLbYHWo8zhMrVMT" + + "9Q5x3h8cszUz7Acu2BXjP1m96msUNoxPOZtt88NlaFz1Q/JSbQTsVOMd9b/IRN6S" + + "A/uU0BiKEMHXuT8HUHVPK49oCKhZrGFP3RT8HZxDKLmR/qrgZ/4JAwLXyWhb4pf4" + + "nmCmD0lDwoYvatLiR7UQVM2MamxClIiT0lCPN9C2AYIFgRWAJNS215Tjx7P/dh7e" + + "8sYfh5XEHErT3dMbsAGHtCFKaWEgWWl5dSA8eXlqaWFAbm93bWVkaWF0ZWNoLmNv" + + "bT6wAwP//4kAXQQQEQIAHQUCQRytpgcLCQgHAwIKAhkBBRsDAAAABR4BAAAAAAoJ" + + "EPT+Vvgr/2IUmWEAoJEyJ9B7CJfmjwVTT30UbdCctVRmAJ9akw6we6UYd82vF0FK" + + "QTlSuqHXKbABZ50CawRBHK2mEAgA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlL" + + "OCDaAadWoxTpj0BV89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N" + + "286Z4VeSWc39uK50T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/" + + "RgBYK+X0iP1YTknbzSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2O" + + "u1WMuF040zT9fBdXQ6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqV" + + "DNmWn6vQClCbAkbTCD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwACAgf/crbApFLO" + + "QikN9lHbn62dDxuu/dNigNmjf/A3cX+vo8aPHTXnRDyDwQALRuqbitdbM6p1LFjG" + + "8g84Aabk/jL7zzjJLqPe8pxeK1Pu+P44NzalXAYMsOOsxGuixIrz3Jw8IrpH/mkP" + + "tG/iX0NaLZ+Au9eokdLb+6NR/a51r2qL3E0ycFciJ61HzZKKHi6d0VQ7CHozCz+j" + + "d5530Mh1qq/ZjYDDjfP+snpyNIZXcLtGR/3HzkBqNgDcvClLx3322AWKDa7pX8DX" + + "qiLr8znWhPRpH9kCTnSpRzhYGT25FRcvdj4/q/oUIET/Tp+BWW3BxAc7WgpgqEfn" + + "fa0Mv72Zbn91gf4JAwITijME9IlFBGAwH6YmBtWIlnDiRbsq/Pxozuhbnes831il" + + "KmdpUKXkiIfHY0MqrEWl3Dfn6PMJGTnhgqXMrDxx3uHrq0Jl2swRnAWIIO8gID7j" + + "uPetUqEviPiwAYeJAEwEGBECAAwFAkEcraYFGwwAAAAACgkQ9P5W+Cv/YhShrgCg" + + "+JW8m5nF3R/oZGuG87bXQBszkjMAoLhGPncuGKowJXMRVc70/8qwXQJLsAFn"); + + char[] sec8pass = "qwertyui".toCharArray(); + + byte[] sec9 = Base64.decode( + "lQGqBEHCokERBAC9rh5SzC1sX1y1zoFuBB/v0SGhoKMEvLYf8Qv/j4deAMrc" + + "w5dxasYoD9oxivIUfTbZKo8cqr+dKLgu8tycigTM5b/T2ms69SUAxSBtj2uR" + + "LZrh4vjC/93kF+vzYJ4fNaBs9DGfCnsTouKjXqmfN3SlPMKNcGutO7FaUC3d" + + "zcpYfwCg7qyONHvXPhS0Iw4QL3mJ/6wMl0UD/0PaonqW0lfGeSjJSM9Jx5Bt" + + "fTSlwl6GmvYmI8HKvOBXAUSTZSbEkMsMVcIgf577iupzgWCgNF6WsNqQpKaq" + + "QIq1Kjdd0Y00xU1AKflOkhl6eufTigjviM+RdDlRYsOO5rzgwDTRTu9giErs" + + "XIyJAIZIdu2iaBHX1zHTfJ1r7nlAA/9H4T8JIhppUk/fLGsoPNZzypzVip8O" + + "mFb9PgvLn5GmuIC2maiocT7ibbPa7XuXTO6+k+323v7PoOUaKD3uD93zHViY" + + "Ma4Q5pL5Ajc7isnLXJgJb/hvvB1oo+wSDo9vJX8OCSq1eUPUERs4jm90/oqy" + + "3UG2QVqs5gcKKR4o48jTiv4DZQJHTlUBtB1mb28ga2V5IDxmb28ua2V5QGlu" + + "dmFsaWQuY29tPoheBBMRAgAeBQJBwqJCAhsDBgsJCAcDAgMVAgMDFgIBAh4B" + + "AheAAAoJEOKcXvehtw4ajJMAoK9nLfsrRY6peq56l/KzmjzuaLacAKCXnmiU" + + "waI7+uITZ0dihJ3puJgUz50BWARBwqJDEAQA0DPcNIn1BQ4CDEzIiQkegNPY" + + "mkYyYWDQjb6QFUXkuk1WEB73TzMoemsA0UKXwNuwrUgVhdpkB1+K0OR/e5ik" + + "GhlFdrDCqyT+mw6dRWbJ2i4AmFXZaRKO8AozZeWojsfP1/AMxQoIiBEteMFv" + + "iuXnZ3pGxSfZYm2+33IuPAV8KKMAAwUD/0C2xZQXgVWTiVz70HUviOmeTQ+f" + + "b1Hj0U9NMXWB383oQRBZCvQDM12cqGsvPZuZZ0fkGehGAIoyXtIjJ9lejzZN" + + "1TE9fnXZ9okXI4yCl7XLSE26OAbNsis4EtKTNScNaU9Dk3CS5XD/pkRjrkPN" + + "2hdUFtshuGmYkqhb9BIlrwE7/gMDAglbVSwecr9mYJcDYCH62U9TScWDTzsQ" + + "NFEfhMez3hGnNHNfHe+7yN3+Q9/LIhbba3IJEN5LsE5BFvudLbArp56EusIn" + + "JCxgiEkEGBECAAkFAkHCokMCGwwACgkQ4pxe96G3Dho2UQCeN3VPwx3dROZ+" + + "4Od8Qj+cLrBndGEAn0vaQdy6eIGeDw2I9u3Quwy6JnROnQHhBEHCozMRBADH" + + "ZBlB6xsAnqFYtYQOHr4pX6Q8TrqXCiHHc/q56G2iGbI9IlbfykQzaPHgWqZw" + + "9P0QGgF/QZh8TitiED+imLlGDqj3nhzpazqDh5S6sg6LYkQPqhwG/wT5sZQQ" + + "fzdeupxupjI5YN8RdIqkWF+ILOjk0+awZ4z0TSY/f6OSWpOXlwCgjIquR3KR" + + "tlCLk+fBlPnOXaOjX+kEAJw7umykNIHNaoY/2sxNhQhjqHVxKyN44y6FCSv9" + + "jRyW8Q/Qc8YhqBIHdmlcXoNWkDtlvErjdYMvOKFqKB1e2bGpjvhtIhNVQWdk" + + "oHap9ZuM1nV0+fD/7g/NM6D9rOOVCahBG2fEEeIwxa2CQ7zHZYfg9Umn3vbh" + + "TYi68R3AmgLOA/wKIVkfFKioI7iX4crQviQHJK3/A90SkrjdMQwLoiUjdgtk" + + "s7hJsTP1OPb2RggS1wCsh4sv9nOyDULj0T0ySGv7cpyv5Nq0FY8gw2oogHs5" + + "fjUnG4VeYW0zcIzI8KCaJT4UhR9An0A1jF6COrYCcjuzkflFbQLtQb9uNj8a" + + "hCpU4/4DAwIUxXlRMYE8uWCranzPo83FnBPRnGJ2aC9SqZWJYVUKIn4Vf2nu" + + "pVvCGFja0usl1WfV72hqlNKEONq7lohJBBgRAgAJBQJBwqMzAhsCAAoJEOKc" + + "Xvehtw4afisAoME/t8xz/rj/N7QRN9p8Ji8VPGSqAJ9K8eFJ+V0mxR+octJr" + + "6neEEX/i1Q=="); + + public char[] sec9pass = "foo".toCharArray(); + + // version 4 keys with expiry dates + byte[] pub10 = Base64.decode( + "mQGiBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHLQgdGVzdCBrZXkg" + + "KHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUCQqqJrQIbAwUJACTqAAYL" + + "CQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzLAJ42AeCRIBBjv8r8qw9y" + + "laNj2GZ1sACgiWYHVXMA6B1H9I1kS3YsCd3Oq7qwAgAAuM0EQqqJrhADAKWkix8l" + + "pJN7MMTXob4xFF1TvGll0UD1bDGOMMbes6aeXSbT9QXee/fH3GnijLY7wB+qTPv9" + + "ohubrSpnv3yen3CEBW6Q2YK+NlCskma42Py8YMV2idmYjtJi1ckvHFWt5wADBQL/" + + "fkB5Q5xSGgspMaTZmtmX3zG7ZDeZ0avP8e8mRL8UszCTpqs6vMZrXwyQLZPbtMYv" + + "PQpuRGEeKj0ysimwYRA5rrLQjnRER3nyuuEUUgc4j+aeRxPf9WVsJ/a1FCHtaAP1" + + "iE8EGBECAA8FAkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCfd66H7DL7kFGd" + + "IoS+NIp8JO+noxAAn25si4QAF7og8+4T5YQUuhIhx/NesAIAAA=="); + + byte[] sec10 = Base64.decode( + "lQHhBEKqia0RBACc3hkufmscRSC4UvPZqMDsHm4+d/GXIr+3iNMSSEySJu8yk+k0" + + "Xs11C/K+n+v1rnn2jGGknv+1lDY6w75TIcTE6o6HGKeIDxsAm8P3MhoGU1GNPamA" + + "eTDeNybtrN/g6C65fCY9uI11hsUboYgQZ8ND22PB0VtvdOgq9D85qNUzxwCg1BbJ" + + "ycAKd4VqEvQ2Zglp3dCSrFMD/Ambq1kZqYa69sp3b9BPKuAgUgUPoytOArEej3Bk" + + "easAgAxNhWJy4GxigES3vk50rVi7w8XBuqbD1mQCzldF0HX0/A7PxLBv6od5uqqF" + + "HFxIyxg/KBZLd9ZOrsSaoUWH58jZq98X/sFtJtRi5VuJagMxCIJD4mLgtMv7Unlb" + + "/GrsA/9DEnObA/fNTgK70T+ZmPIS5tSt+bio30Aw4YGpPCGqpnm1u73b5kqX3U3B" + + "P+vGDvFuqZYpqQA8byAueH0MbaDHI4CFugvShXvgysJxN7ov7/8qsZZUMfK1t2Nr" + + "SAsPuKRbcY4gNKXIElKeXbyaET7vX7uAEKuxEwdYGFp/lNTkHP4DAwLssmOjVC+d" + + "mWB783Lpzjb9evKzsxisTdx8/jHpUSS+r//6/Guyx3aA/zUw5bbftItW57mhuNNb" + + "JTu7WrQgdGVzdCBrZXkgKHRlc3QpIDx0ZXN0QHRlc3QudGVzdD6IZAQTEQIAJAUC" + + "QqqJrQIbAwUJACTqAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDjDROQZRqIzDzL" + + "AJ0cYPwKeoSReY14LqJtAjnkX7URHACgsRZWfpbalrSyDnq3TtZeGPUqGX+wAgAA" + + "nQEUBEKqia4QAwClpIsfJaSTezDE16G+MRRdU7xpZdFA9WwxjjDG3rOmnl0m0/UF" + + "3nv3x9xp4oy2O8Afqkz7/aIbm60qZ798np9whAVukNmCvjZQrJJmuNj8vGDFdonZ" + + "mI7SYtXJLxxVrecAAwUC/35AeUOcUhoLKTGk2ZrZl98xu2Q3mdGrz/HvJkS/FLMw" + + "k6arOrzGa18MkC2T27TGLz0KbkRhHio9MrIpsGEQOa6y0I50REd58rrhFFIHOI/m" + + "nkcT3/VlbCf2tRQh7WgD9f4DAwLssmOjVC+dmWDXVLRopzxbBGOvodp/LZoSDb56" + + "gNJjDMJ1aXqWW9qTAg1CFjBq73J3oFpVzInXZ8+Q8inxv7bnWiHbiE8EGBECAA8F" + + "AkKqia4CGwwFCQAk6gAACgkQ4w0TkGUaiMzdqgCgl2jw5hfk/JsyjulQqe1Nps1q" + + "Lx0AoMdnFMZmTMLHn8scUW2j9XO312tmsAIAAA=="); + + public char[] sec10pass = "test".toCharArray(); + + public byte[] subKeyBindingKey = Base64.decode( + "mQGiBDWagYwRBAD7UcH4TAIp7tmUoHBNxVxCVz2ZrNo79M6fV63riOiH2uDxfIpr" + + "IrL0cM4ehEKoqlhngjDhX60eJrOw1nC5BpYZRnDnyDYT4wTWRguxObzGq9pqA1dM" + + "oPTJhkFZVIBgFY99/ULRqaUYIhFGgBtnwS70J8/L/PGVc3DmWRLMkTDjSQCg/5Nh" + + "MCjMK++MdYMcMl/ziaKRT6EEAOtw6PnU9afdohbpx9CK4UvCCEagfbnUtkSCQKSk" + + "6cUp6VsqyzY0pai/BwJ3h4apFMMMpVrtBAtchVgqo4xTr0Sve2j0k+ase6FSImiB" + + "g+AR7hvTUTcBjwtIExBc8TuCTqmn4GG8F7UMdl5Z0AZYj/FfAQYaRVZYP/pRVFNx" + + "Lw65BAC/Fi3qgiGCJFvXnHIckTfcAmZnKSEXWY9NJ4YQb4+/nH7Vsw0wR/ZObUHR" + + "bWgTc9Vw1uZIMe0XVj6Yk1dhGRehUnrm3mE7UJxu7pgkBCbFECFSlSSqP4MEJwZV" + + "09YP/msu50kjoxyoTpt+16uX/8B4at24GF1aTHBxwDLd8X0QWrQsTWVycmlsbCBM" + + "eW5jaCBDTEVBUiBzeXN0ZW0gREggPGNsZWFyQG1sLmNvbT6JAEsEEBECAAsFAjWa" + + "gYwECwMBAgAKCRDyAGjiP47/XanfAKCs6BPURWVQlGh635VgL+pdkUVNUwCdFcNa" + + "1isw+eAcopXPMj6ACOapepu5Ag0ENZqBlBAIAPZCV7cIfwgXcqK61qlC8wXo+VMR" + + "OU+28W65Szgg2gGnVqMU6Y9AVfPQB8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf" + + "3HZSTz09jdvOmeFXklnN/biudE/F/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2g" + + "pXI61Brwv0YAWCvl9Ij9WE5J280gtJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPA" + + "Q/ClWxiNjrtVjLhdONM0/XwXV0OjHRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQD" + + "GcgHKXrKlQzZlp+r0ApQmwJG0wg9ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH" + + "/RYtVo+HROZ6jrNjrATEwQm1fUQrk6n5+2dniN881lF0CNkB4NkHw1Xxz4Ejnu/0" + + "iLg8fkOAsmanOsKpOkRtqUnVpsVL5mLJpFEyCY5jbcfj+KY9/25bs0ga7kLHNZia" + + "zbCxJdF+W179z3nudQxRaXG/0XISIH7ziZbSVni69sKc1osk1+OoOMbSuZ86z535" + + "Pln4fXclkFE927HxfbWoO+60hkOLKh7x+8fC82b3x9vCETujEaxrscO2xS7/MYXP" + + "8t1ffriTDmhuIuQS2q4fLgeWdqrODrMhrD8Dq7e558gzp30ZCqpiS7EmKGczL7B8" + + "gXxbBCVSTxYMJheXt2xMXsuJAD8DBRg1moGU8gBo4j+O/10RAgWdAKCPhaFIXuC8" + + "/cdiNMxTDw9ug3De5QCfYXmDzRSFUu/nrCi8yz/l09wsnxo="); + + public byte[] subKeyBindingCheckSum = Base64.decode("3HU+"); + + // + // PGP8 with SHA1 checksum. + // + public byte[] rewrapKey = Base64.decode( + "lQOWBEUPOQgBCADdjPTtl8oOwqJFA5WU8p7oDK5KRWfmXeXUZr+ZJipemY5RSvAM" + + "rxqsM47LKYbmXOJznXCQ8+PPa+VxXAsI1CXFHIFqrXSwvB/DUmb4Ec9EuvNd18Zl" + + "hJAybzmV2KMkaUp9oG/DUvxZJqkpUddNfwqZu0KKKZWF5gwW5Oy05VCpaJxQVXFS" + + "whdbRfwEENJiNx4RB3OlWhIjY2p+TgZfgQjiGB9i15R+37sV7TqzBUZF4WWcnIRQ" + + "DnpUfxHgxQ0wO/h/aooyRHSpIx5i4oNpMYq9FNIyakEx/Bomdbs5hW9dFxhrE8Es" + + "UViAYITgTsyROxmgGatGG09dcmVDJVYF4i7JAAYpAAf/VnVyUDs8HrxYTOIt4rYY" + + "jIHToBsV0IiLpA8fEA7k078L1MwSwERVVe6oHVTjeR4A9OxE52Vroh2eOLnF3ftf" + + "6QThVVZr+gr5qeG3yvQ36N7PXNEVOlkyBzGmFQNe4oCA+NR2iqnAIspnekVmwJV6" + + "xVvPCjWw/A7ZArDARpfthspwNcJAp4SWfoa2eKzvUTznTyqFu2PSS5fwQZUgOB0P" + + "Y2FNaKeqV8vEZu4SUWwLOqXBQIZXiaLvdKNgwFvUe3kSHdCNsrVzW7SYxFwaEog2" + + "o6YLKPVPqjlGX1cMOponGp+7n9nDYkQjtEsGSSMQkQRDAcBdSVJmLO07kFOQSOhL" + + "WQQA49BcgTZyhyH6TnDBMBHsGCYj43FnBigypGT9FrQHoWybfX47yZaZFROAaaMa" + + "U6man50YcYZPwzDzXHrK2MoGALY+DzB3mGeXVB45D/KYtlMHPLgntV9T5b14Scbc" + + "w1ES2OUtsSIUs0zelkoXqjLuKnSIYK3mMb67Au7AEp6LXM8EAPj2NypvC86VEnn+" + + "FH0QHvUwBpmDw0EZe25xQs0brvAG00uIbiZnTH66qsIfRhXV/gbKK9J5DTGIqQ15" + + "DuPpz7lcxg/n2+SmjQLNfXCnG8hmtBjhTe+udXAUrmIcfafXyu68SAtebgm1ga56" + + "zUfqsgN3FFuMUffLl3myjyGsg5DnA/oCFWL4WCNClOgL6A5VkNIUait8QtSdCACT" + + "Y7jdSOguSNXfln0QT5lTv+q1AjU7zjRl/LsFNmIJ5g2qdDyK937FOXM44FEEjZty" + + "/4P2dzYpThUI4QUohIj8Qi9f2pZQueC5ztH6rpqANv9geZKcciAeAbZ8Md0K2TEU" + + "RD3Lh+RSBzILtBtUZXN0IEtleSA8dGVzdEBleGFtcGxlLmNvbT6JATYEEwECACAF" + + "AkUPOQgCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDYpknHeQaskD9NB/9W" + + "EbFuLaqZAl3yjLU5+vb75BdvcfL1lUs44LZVwobNp3/0XbZdY76xVPNZURtU4u3L" + + "sJfGlaF+EqZDE0Mqc+vs5SIb0OnCzNJ00KaUFraUtkByRV32T5ECHK0gMBjCs5RT" + + "I0vVv+Qmzl4+X1Y2bJ2mlpBejHIrOzrBD5NTJimTAzyfnNfipmbqL8p/cxXKKzS+" + + "OM++ZFNACj6lRM1W9GioXnivBRC88gFSQ4/GXc8yjcrMlKA27JxV+SZ9kRWwKH2f" + + "6o6mojUQxnHr+ZFKUpo6ocvTgBDlC57d8IpwJeZ2TvqD6EdA8rZ0YriVjxGMDrX1" + + "8esfw+iLchfEwXtBIRwS"); + + char[] rewrapPass = "voltage123".toCharArray(); + + byte[] pubWithX509 = Base64.decode( + "mQENBERabjABCACtmfyo6Nph9MQjv4nmCWjZrRYnhXbivomAdIwYkLZUj1bjqE+j"+ + "uaLzjZV8xSI59odZvrmOiqlzOc4txitQ1OX7nRgbOJ7qku0dvwjtIn46+HQ+cAFn"+ + "2mTi81RyXEpO2uiZXfsNTxUtMi+ZuFLufiMc2kdk27GZYWEuasdAPOaPJnA+wW6i"+ + "ZHlt0NfXIGNz864gRwhD07fmBIr1dMFfATWxCbgMd/rH7Z/j4rvceHD2n9yrhPze"+ + "YN7W4Nuhsr2w/Ft5Cm9xO7vXT/cpto45uxn8f7jERep6bnUwNOhH8G+6xLQgTLD0"+ + "qFBGVSIneK3lobs6+xn6VaGN8W0tH3UOaxA1ABEBAAG0D0NOPXFhLWRlZXBzaWdo"+ + "dIkFDgQQZAIFAQUCRFpuMAUDCWdU0gMF/3gCGwPELGQBAQQwggTkMIIDzKADAgEC"+ + "AhBVUMV/M6rIiE+IzmnPheQWMA0GCSqGSIb3DQEBBQUAMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDAeFw0wNjA1MDQyMTEyMTZaFw0xMTA1MDQyMTIwMDJaMG4xEzARBgoJkiaJk/Is"+ + "ZAEZFgNjb20xEjAQBgoJkiaJk/IsZAEZFgJxYTEVMBMGCgmSJomT8ixkARkWBXRt"+ + "czAxMRUwEwYKCZImiZPyLGQBGRYFV2ViZmUxFTATBgNVBAMTDHFhLWRlZXBzaWdo"+ + "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK2Z/Kjo2mH0xCO/ieYJ"+ + "aNmtFieFduK+iYB0jBiQtlSPVuOoT6O5ovONlXzFIjn2h1m+uY6KqXM5zi3GK1DU"+ + "5fudGBs4nuqS7R2/CO0ifjr4dD5wAWfaZOLzVHJcSk7a6Jld+w1PFS0yL5m4Uu5+"+ + "IxzaR2TbsZlhYS5qx0A85o8mcD7BbqJkeW3Q19cgY3PzriBHCEPTt+YEivV0wV8B"+ + "NbEJuAx3+sftn+Piu9x4cPaf3KuE/N5g3tbg26GyvbD8W3kKb3E7u9dP9ym2jjm7"+ + "Gfx/uMRF6npudTA06Efwb7rEtCBMsPSoUEZVIid4reWhuzr7GfpVoY3xbS0fdQ5r"+ + "EDUCAwEAAaOCAXwwggF4MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G"+ + "A1UdDgQWBBSmFTRv5y65DHtTYae48zl0ExNWZzCCASUGA1UdHwSCARwwggEYMIIB"+ + "FKCCARCgggEMhoHFbGRhcDovLy9DTj1xYS1kZWVwc2lnaHQsQ049cWEtd3VtYW4x"+ + "LWRjLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNl"+ + "cyxDTj1Db25maWd1cmF0aW9uLERDPVdlYmZlLERDPXRtczAxLERDPXFhLERDPWNv"+ + "bT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM"+ + "RGlzdHJpYnV0aW9uUG9pbnSGQmh0dHA6Ly9xYS13dW1hbjEtZGMud2ViZmUudG1z"+ + "MDEucWEuY29tL0NlcnRFbnJvbGwvcWEtZGVlcHNpZ2h0LmNybDAQBgkrBgEEAYI3"+ + "FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAfuZCW3XlB7Eok35zQbvYt9rhAndT"+ + "DNw3wPNI4ZzD1nXoYWnwhNNvWRpsOt4ExOSNdaHErfgDXAMyyg66Sro0TkAx8eAj"+ + "fPQsyRAh0nm0glzFmJN6TdOZbj7hqGZjc4opQ6nZo8h/ULnaEwMIUW4gcSkZt0ww"+ + "CuErl5NUrN3DpkREeCG/fVvQZ8ays3ibQ5ZCZnYBkLYq/i0r3NLW34WfYhjDY48J"+ + "oQWtvFSAxvRfz2NGmqnrCHPQZxqlfdta97kDa4VQ0zSeBaC70gZkLmD1GJMxWoXW"+ + "6tmEcgPY5SghInUf+L2u52V55MjyAFzVp7kTK2KY+p7qw35vzckrWkwu8AAAAAAA"+ + "AQE="); + + private static byte[] secWithPersonalCertificate = Base64.decode( + "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" + + "cGdwbWltZQULBwgJAgIZAQUbAQAAAAUeAQAAAAIVAgAKCRDlTa3BE84gWVKW" + + "BACcoCFKvph9r9QiHT1Z3N4wZH36Uxqu/059EFALnBkEdVudX/p6S9mynGRk" + + "EfhmWFC1O6dMpnt+ZBEed/4XyFWVSLPwirML+6dxfXogdUsdFF1NCRHc3QGc" + + "txnNUT/zcZ9IRIQjUhp6RkIvJPHcyfTXKSbLviI+PxzHU2Padq8pV7ABZ7kA" + + "jQRIfg8tAQQAutJR/aRnfZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr" + + "5dg50wq3I4HOamRxUwHpdPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO" + + "8LUJ2VTbfPxoLFp539SQ0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0Ft" + + "JycAEQEAAbABj4kEzQQYAQIENwUCSMYtnAUJAeEzgMLFFAAAAAAAFwNleDUw" + + "OWNlcnRpZmljYXRlQHBncC5jb20wggNhMIICyqADAgECAgkA1AoCoRKJCgsw" + + "DQYJKoZIhvcNAQEFBQAwgakxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj" + + "aCBSZXB1YmxpYzESMBAGA1UEChQJQSYmTCBzb2Z0MSAwHgYDVQQLExdJbnRl" + + "cm5hbCBEZXZlbG9wbWVudCBDQTEqMCgGA1UEAxQhQSYmTCBzb2Z0IEludGVy" + + "bmFsIERldmVsb3BtZW50IENBMR8wHQYJKoZIhvcNAQkBFhBrYWRsZWNAYWxz" + + "b2Z0LmN6MB4XDTA4MDcxNjE1MDkzM1oXDTA5MDcxNjE1MDkzM1owaTELMAkG" + + "A1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMRIwEAYDVQQKFAlB" + + "JiZMIHNvZnQxFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5IeW5l" + + "ay1JbnRyYW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAutJR/aRn" + + "fZYwlVv+KlUDYjG8YQUfHpTxpnmVu7W6N0tNg/Xr5dg50wq3I4HOamRxUwHp" + + "dPkXyNF1szpDSRZmlM+VmiIvJDBnyH5YVlxT6+zO8LUJ2VTbfPxoLFp539SQ" + + "0oJOm7IGMAGO7c0n/QV0N3hKUfWgCyJ+sENDa0FtJycCAwEAAaOBzzCBzDAJ" + + "BgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD" + + "ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNaw7A6r10PtYZzAvr9CrSKeRYJgwHwYD" + + "VR0jBBgwFoAUmqSRM8rN3+T1+tkGiqef8S5suYgwGgYDVR0RBBMwEYEPaHlu" + + "ZWtAYWxzb2Z0LmN6MCgGA1UdHwQhMB8wHaAboBmGF2h0dHA6Ly9wZXRyazIv" + + "Y2EvY2EuY3JsMAsGA1UdDwQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQCUdOWd" + + "7mBLWj1/GSiYgfwgdTrgk/VZOJvMKBiiFyy1iFEzldz6Xx+mAexnFJKfZXZb" + + "EMEGWHfWPmgJzAtuTT0Jz6tUwDmeLH3MP4m8uOZtmyUJ2aq41kciV3rGxF0G" + + "BVlZ/bWTaOzHdm6cjylt6xxLt6MJzpPBA/9ZfybSBh1DaAUbDgAAAJ0gBBkB" + + "AgAGBQJIxi2bAAoJEAdYkEWLb2R2fJED/RK+JErZ98uGo3Z81cHkdP3rk8is" + + "DUL/PR3odBPFH2SIA5wrzklteLK/ZXmBUzcvxqHEgI1F7goXbsBgeTuGgZdx" + + "pINErxkNpcMl9FTldWKGiapKrhkZ+G8knDizF/Y7Lg6uGd2nKVxzutLXdHJZ" + + "pU89Q5nzq6aJFAZo5TBIcchQAAoJEOVNrcETziBZXvQD/1mvFqBfWqwXxoj3" + + "8fHUuFrE2pcp32y3ciO2i+uNVEkNDoaVVNw5eHQaXXWpllI/Pe6LnBl4vkyc" + + "n3pjONa4PKrePkEsCUhRbIySqXIHuNwZumDOlKzZHDpCUw72LaC6S6zwuoEf" + + "ucOcxTeGIUViANWXyTIKkHfo7HfigixJIL8nsAFn"); + + private static final byte[] umlautKeySig = Base64.decode( + "mI0ETdvOgQEEALoI2a39TRk1HReEB6DP9Bu3ShZUce+/Oeg9RIL9aUFuCsNdhu02" + + "REEHjO29Jz8daPgrnJDfFepNLD6iKKru2m9P30qnhsHMIAshO2Ozfh6wKwuHRqR3" + + "L4gBDu7cCB6SLwPoD8AYG0yQSM+Do10Td87RlStxCgxpMK6R3TsRkxcFABEBAAG0" + + "OlVNTEFVVFNUQVJUOsOEw6TDlsO2w5zDvMOfOlVNTEFURU5ERSA8YXNkbGFrc2Rs" + + "QGFrc2RqLmNvbT6IuAQTAQIAIgUCTdvOgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC" + + "HgECF4AACgkQP8kDwm8AOFiArAP/ZXrlZJB1jFEjyBb04ckpE6F/aJuSYIXf0Yx5" + + "T2eS+lA69vYuqKRC1qNROBrAn/WGNOQBFNEgGoy3F3gV5NgpIphnyIEZdZWGY2rv" + + "yjunKWlioZjWc/xbSbvpvJ3Q8RyfDXBOkDEB6uF1ksimw2eJSOUTkF9AQfS5f4rT" + + "5gs013G4jQRN286BAQQApVbjd8UhsQLB4TpeKn9+dDXAfikGgxDOb19XisjRiWxA" + + "+bKFxu5tRt6fxXl6BGSGT7DhoVbNkcJGVQFYcbR31UGKCVYcWSL3yfz+PiVuf1UB" + + "Rp44cXxxqxrLqKp1rk3dGvV4Ayy8lkk3ncDGPez6lIKvj3832yVtAzUOX1QOg9EA" + + "EQEAAYifBBgBAgAJBQJN286BAhsMAAoJED/JA8JvADhYQ80D/R3TX0FBMHs/xqEh" + + "tiS86XP/8pW6eMm2eaAYINxoDY3jmDMv2HFQ+YgrYXgqGr6eVGqDMNPj4W8VBoOt" + + "iYW7+SWY76AAl+gmWIMm2jbN8bZXFk4jmIxpycHCrtoXX8rUk/0+se8NvbmAdMGK" + + "POOoD7oxdRmJSU5hSspOCHrCwCa3"); + + public void test1() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + Iterator sIt = pubKey.getSignatures(); + while (sIt.hasNext()) + { + ((PGPSignature)sIt.next()).getSignatureType(); + } + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + // + // exact match + // + rIt = pubRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = pubRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on partial match 1"); + } + + // + // partial match 0 expected + // + rIt = pubRings.getKeyRings("XXX", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of public keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = pubRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of public keyrings on case-insensitive partial match"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + pk.getSignatures(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + + // + // exact match + // + rIt = secretRings.getKeyRings("test (Test key) <test@ubicall.com>"); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on exact match"); + } + + // + // partial match 1 expected + // + rIt = secretRings.getKeyRings("test", true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on partial match 1"); + } + + // + // exact match 0 expected + // + rIt = secretRings.getKeyRings("test", false); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 0) + { + fail("wrong number of secret keyrings on partial match 0"); + } + + // + // case-insensitive partial match + // + rIt = secretRings.getKeyRings("TEST@ubicall.com", true, true); + count = 0; + while (rIt.hasNext()) + { + count++; + rIt.next(); + } + + if (count != 1) + { + fail("wrong number of secret keyrings on case-insensitive partial match"); + } + } + + public void test2() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes, new BcKeyFingerprintCalculator()); + + keyCount++; + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + if (pk.getKeyID() == -1413891222336124627L) + { + int sCount = 0; + Iterator sIt = pk.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + while (sIt.hasNext()) + { + int type = ((PGPSignature)sIt.next()).getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING) + { + fail("failed to return correct signature type"); + } + sCount++; + } + + if (sCount != 1) + { + fail("failed to find binding signature"); + } + } + + pk.getSignatures(); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass1)); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec2pass2)); + } + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 2) + { + fail("wrong number of secret keyrings"); + } + } + + public void test3() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPPublicKey pubK = (PGPPublicKey)it.next(); + + pubK.getSignatures(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test4() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec3pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test5() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 1) + { + fail("wrong number of public keyrings"); + } + + if (noIDEA()) + { + return; + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec5pass1)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + private boolean noIDEA() + { + return true; + } + + public void test6() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub6); + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.getKeyID() == 0x5ce086b5b5a18ff4L) + { + int count = 0; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test6."); + } + } + } + } + + byte[] encRing = pubRings.getEncoded(); + } + + public void test7() + throws Exception + { + PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(pub7, new BcKeyFingerprintCalculator()); + Iterator it = pgpPub.getPublicKeys(); + PGPPublicKey masterKey = null; + + while (it.hasNext()) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + + if (k.isMasterKey()) + { + masterKey = k; + continue; + } + + int count = 0; + PGPSignature sig = null; + Iterator sIt = k.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION); + + while (sIt.hasNext()) + { + sig = (PGPSignature)sIt.next(); + count++; + } + + if (count != 1) + { + fail("wrong number of revocations in test7."); + } + + sig.init(new BcPGPContentVerifierBuilderProvider(), masterKey); + + if (!sig.verifyCertification(k)) + { + fail("failed to verify revocation certification"); + } + } + } + + public void test8() + throws Exception + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub8); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + fail("wrong number of public keys"); + } + } + + if (count != 2) + { + fail("wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec8); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec8pass)); + } + + if (keyCount != 2) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test9() + throws Exception + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec9); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator()); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + PGPPrivateKey pKey = k.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(sec9pass)); + if (keyCount == 1 && pKey != null) + { + fail("primary secret key found, null expected"); + } + } + + if (keyCount != 3) + { + fail("wrong number of secret keys"); + } + } + + if (count != 1) + { + fail("wrong number of secret keyrings"); + } + } + + public void test10() + throws Exception + { + PGPSecretKeyRing secretRing = new PGPSecretKeyRing(sec10, new BcKeyFingerprintCalculator()); + Iterator secretKeys = secretRing.getSecretKeys(); + + while (secretKeys.hasNext()) + { + PGPPublicKey pubKey = ((PGPSecretKey)secretKeys.next()).getPublicKey(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on secret key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on secret key ring"); + } + } + + PGPPublicKeyRing publicRing = new PGPPublicKeyRing(pub10, new BcKeyFingerprintCalculator()); + Iterator publicKeys = publicRing.getPublicKeys(); + + while (publicKeys.hasNext()) + { + PGPPublicKey pubKey = (PGPPublicKey)publicKeys.next(); + + if (pubKey.getValidDays() != 28) + { + fail("days wrong on public key ring"); + } + + if (pubKey.getValidSeconds() != 28 * 24 * 60 * 60) + { + fail("seconds wrong on public key ring"); + } + } + } + + public void generateTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void insertMasterTest() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + + rsaKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing1 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair2, + "test", PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + PGPSecretKeyRing secRing2 = keyRingGen.generateSecretKeyRing(); + PGPPublicKeyRing pubRing2 = keyRingGen.generatePublicKeyRing(); + + try + { + PGPPublicKeyRing.insertPublicKey(pubRing1, pubRing2.getPublicKey()); + fail("adding second master key (public) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in public test"); + } + } + + try + { + PGPSecretKeyRing.insertSecretKey(secRing1, secRing2.getSecretKey()); + fail("adding second master key (secret) should throw an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("cannot add a master key to a ring that already has one")) + { + fail("wrong message in secret test"); + } + } + } + + public void generateSha1Test() + throws Exception + { + char[] passPhrase = "hello".toCharArray(); + KeyPairGenerator dsaKpg = KeyPairGenerator.getInstance("DSA", "SC"); + + dsaKpg.initialize(512); + + // + // this takes a while as the key generator has to generate some DSA params + // before it generates the key. + // + KeyPair dsaKp = dsaKpg.generateKeyPair(); + + KeyPairGenerator elgKpg = KeyPairGenerator.getInstance("ELGAMAL", "SC"); + BigInteger g = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16); + BigInteger p = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + elgKpg.initialize(512); + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPair elgKp = elgKpg.generateKeyPair(); + PGPKeyPair dsaKeyPair = new PGPKeyPair(PGPPublicKey.DSA, dsaKp, new Date()); + PGPKeyPair elgKeyPair = new PGPKeyPair(PGPPublicKey.ELGAMAL_ENCRYPT, elgKp, new Date()); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, dsaKeyPair, + "test", PGPEncryptedData.AES_256, passPhrase, true, null, null, new SecureRandom(), "SC"); + + keyRingGen.addSubKey(elgKeyPair); + + PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing(); + + keyRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPPublicKey vKey = null; + PGPPublicKey sKey = null; + + Iterator it = pubRing.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + if (pk.isMasterKey()) + { + vKey = pk; + } + else + { + sKey = pk; + } + } + + Iterator sIt = sKey.getSignatures(); + while (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + + if (sig.getKeyID() == vKey.getKeyID() + && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), vKey); + + if (!sig.verifyCertification(vKey, sKey)) + { + fail("failed to verify sub-key signature."); + } + } + } + } + + private void test11() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(subKeyBindingKey, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + while (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + if (key.getValidSeconds() != 0) + { + fail("expiration time non-zero"); + } + } + } + + private void rewrapTest() + throws Exception + { + SecureRandom rand = new SecureRandom(); + + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv = (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + } + } + } + + private void testPublicKeyRingWithX509() + throws Exception + { + checkPublicKeyRingWithX509(pubWithX509); + + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(pubWithX509, new BcKeyFingerprintCalculator()); + + checkPublicKeyRingWithX509(pubRing.getEncoded()); + } + + private void testSecretKeyRingWithPersonalCertificate() + throws Exception + { + checkSecretKeyRingWithPersonalCertificate(secWithPersonalCertificate); + PGPSecretKeyRingCollection secRing = new PGPSecretKeyRingCollection(secWithPersonalCertificate); + checkSecretKeyRingWithPersonalCertificate(secRing.getEncoded()); + } + + private void testUmlaut() + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(umlautKeySig, new BcKeyFingerprintCalculator()); + + PGPPublicKey pub = pubRing.getPublicKey(); + String userID = (String)pub.getUserIDs().next(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID test"); + } + } + } + + // + // this is quicker because we are using pregenerated parameters. + // + KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA", "SC"); + KeyPair rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair1 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + rsaKp = rsaKpg.generateKeyPair(); + PGPKeyPair rsaKeyPair2 = new PGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKp, new Date()); + char[] passPhrase = "passwd".toCharArray(); + + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, rsaKeyPair1, + userID, PGPEncryptedData.AES_256, passPhrase, null, null, new SecureRandom(), "SC"); + + PGPPublicKeyRing pubRing1 = keyRingGen.generatePublicKeyRing(); + + pub = pubRing1.getPublicKey(); + + for (Iterator it = pub.getSignatures(); it.hasNext();) + { + PGPSignature sig = (PGPSignature)it.next(); + + if (sig.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION) + { + sig.init(new BcPGPContentVerifierBuilderProvider(), pub); + + if (!sig.verifyCertification(userID, pub)) + { + fail("failed UTF8 userID creation test"); + } + } + } + } + + private void checkSecretKeyRingWithPersonalCertificate(byte[] keyRing) + throws Exception + { + PGPSecretKeyRingCollection secCol = new PGPSecretKeyRingCollection(keyRing); + + + int count = 0; + + for (Iterator rIt = secCol.getKeyRings(); rIt.hasNext();) + { + PGPSecretKeyRing ring = (PGPSecretKeyRing)rIt.next(); + + for (Iterator it = ring.getExtraPublicKeys(); it.hasNext();) + { + it.next(); + count++; + } + } + + if (count != 1) + { + fail("personal certificate data subkey not found - count = " + count); + } + } + + private void checkPublicKeyRingWithX509(byte[] keyRing) + throws Exception + { + PGPPublicKeyRing pubRing = new PGPPublicKeyRing(keyRing, new BcKeyFingerprintCalculator()); + Iterator it = pubRing.getPublicKeys(); + + if (it.hasNext()) + { + PGPPublicKey key = (PGPPublicKey)it.next(); + + Iterator sIt = key.getSignatures(); + + if (sIt.hasNext()) + { + PGPSignature sig = (PGPSignature)sIt.next(); + if (sig.getKeyAlgorithm() != 100) + { + fail("experimental signature not found"); + } + if (!areEqual(sig.getSignature(), Hex.decode("000101"))) + { + fail("experimental encoding check failed"); + } + } + else + { + fail("no signature found"); + } + } + else + { + fail("no key found"); + } + } + + public void performTest() + throws Exception + { + try + { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + // test7(); + test8(); + test9(); + test10(); + test11(); + generateTest(); + generateSha1Test(); + rewrapTest(); + testPublicKeyRingWithX509(); + testSecretKeyRingWithPersonalCertificate(); + insertMasterTest(); + testUmlaut(); + } + catch (PGPException e) + { + if (e.getUnderlyingException() != null) + { + Exception ex = e.getUnderlyingException(); + fail("exception: " + ex, ex); + } + else + { + fail("exception: " + e, e); + } + } + } + + public String getName() + { + return "BcPGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new BcPGPKeyRingTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java new file mode 100644 index 000000000..b6952107c --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPDSAElGamalTest.java @@ -0,0 +1,451 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.Iterator; + +import javax.crypto.Cipher; + +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ElGamalParameterSpec; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPCompressedDataGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; + +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTestResult; +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class PGPDSAElGamalTest implements Test +{ + + byte[] testPubKeyRing = + Base64.decode( + "mQGiBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzLQ3RXJpYyBILiBFY2hpZG5hICh0ZXN0IGtleSBv" + + "bmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3JnPohZBBMRAgAZBQJAEfI2BAsH" + + "AwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgnkDdnAKC/CfLWikSBdbngY6OK" + + "5UN3+o7q1ACcDRqjT3yjBU3WmRUNlxBg3tSuljmwAgAAuQENBEAR8jgQBAC2" + + "kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVjei/3yVfT/fuCVtGHOmYLEBqH" + + "bn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya43RtcubqMc7eKw4k0JnnoYgB" + + "ocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhFBYfaBmGU75cQgwADBQP/XxR2" + + "qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSqAi0zeAMdrRsBN7kyzYVVpWwN" + + "5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxkbipnwh2RR4xCXFDhJrJFQUm+" + + "4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXsNi1tRbTmRhqIRgQYEQIABgUC" + + "QBHyOAAKCRAOtk6iUOgnkBStAJoCZBVM61B1LG2xip294MZecMtCwQCbBbsk" + + "JVCXP0/Szm05GB+WN+MOCT2wAgAA"); + + byte[] testPrivKeyRing = + Base64.decode( + "lQHhBEAR8jYRBADNifuSopd20JOQ5x30ljIaY0M6927+vo09NeNxS3KqItba" + + "nz9o5e2aqdT0W1xgdHYZmdElOHTTsugZxdXTEhghyxoo3KhVcNnTABQyrrvX" + + "qouvmP2fEDEw0Vpyk+90BpyY9YlgeX/dEA8OfooRLCJde/iDTl7r9FT+mts8" + + "g3azjwCgx+pOLD9LPBF5E4FhUOdXISJ0f4EEAKXSOi9nZzajpdhe8W2ZL9gc" + + "BpzZi6AcrRZBHOEMqd69gtUxA4eD8xycUQ42yH89imEcwLz8XdJ98uHUxGJi" + + "qp6hq4oakmw8GQfiL7yQIFgaM0dOAI9Afe3m84cEYZsoAFYpB4/s9pVMpPRH" + + "NsVspU0qd3NHnSZ0QXs8L8DXGO1uBACjDUj+8GsfDCIP2QF3JC+nPUNa0Y5t" + + "wKPKl+T8hX/0FBD7fnNeC6c9j5Ir/Fp/QtdaDAOoBKiyNLh1JaB1NY6US5zc" + + "qFks2seZPjXEiE6OIDXYra494mjNKGUobA4hqT2peKWXt/uBcuL1mjKOy8Qf" + + "JxgEd0MOcGJO+1PFFZWGzP4DAwLeUcsVxIC2s2Bb9ab2XD860TQ2BI2rMD/r" + + "7/psx9WQ+Vz/aFAT3rXkEJ97nFeqEACgKmUCAEk9939EwLQ3RXJpYyBILiBF" + + "Y2hpZG5hICh0ZXN0IGtleSBvbmx5KSA8ZXJpY0Bib3VuY3ljYXN0bGUub3Jn" + + "PohZBBMRAgAZBQJAEfI2BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRAOtk6iUOgn" + + "kDdnAJ9Ala3OcwEV1DbK906CheYWo4zIQwCfUqUOLMp/zj6QAk02bbJAhV1r" + + "sAewAgAAnQFYBEAR8jgQBAC2kr57iuOaV7Ga1xcU14MNbKcA0PVembRCjcVj" + + "ei/3yVfT/fuCVtGHOmYLEBqHbn5aaJ0P/6vMbLCHKuN61NZlts+LEctfwoya" + + "43RtcubqMc7eKw4k0JnnoYgBocLXOtloCb7jfubOsnfORvrUkK0+Ne6anRhF" + + "BYfaBmGU75cQgwADBQP/XxR2qGHiwn+0YiMioRDRiIAxp6UiC/JQIri2AKSq" + + "Ai0zeAMdrRsBN7kyzYVVpWwN5u13gPdQ2HnJ7d4wLWAuizUdKIQxBG8VoCxk" + + "bipnwh2RR4xCXFDhJrJFQUm+4nKx9JvAmZTBIlI5Wsi5qxst/9p5MgP3flXs" + + "Ni1tRbTmRhr+AwMC3lHLFcSAtrNg/EiWFLAnKNXH27zjwuhje8u2r+9iMTYs" + + "GjbRxaxRY0GKRhttCwqe2BC0lHhzifdlEcc9yjIjuKfepG2fnnSIRgQYEQIA" + + "BgUCQBHyOAAKCRAOtk6iUOgnkBStAJ9HFejVtVJ/A9LM/mDPe0ExhEXt/QCg" + + "m/KM7hJ/JrfnLQl7IaZsdg1F6vCwAgAA"); + + byte[] encMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP/dgCkMtPB6mIgjFvNiotjaoh4sAXf4vFNkSeehQ2c" + + "r+IMt9CgIYodJI3FoJXxOuTcwesqTp5hRzgUBJS0adLDJwcNubFMy0M2tp5o" + + "KTWpXulIiqyO6f5jI/oEDHPzFoYgBmR4x72l/YpMy8UoYGtNxNvR7LVOfqJv" + + "uDY/71KMtPQEAIadOWpf1P5Td+61Zqn2VH2UV7H8eI6hGa6Lsy4sb9iZNE7f" + + "c+spGJlgkiOt8TrQoq3iOK9UN9nHZLiCSIEGCzsEn3uNuorD++Qs065ij+Oy" + + "36TKeuJ+38CfT7u47dEshHCPqWhBKEYrxZWHUJU/izw2Q1Yxd2XRxN+nafTL" + + "X1fQ0lABQUASa18s0BkkEERIdcKQXVLEswWcGqWNv1ZghC7xO2VDBX4HrPjp" + + "drjL63p2UHzJ7/4gPWGGtnqq1Xita/1mrImn7pzLThDWiT55vjw6Hw=="); + + byte[] signedAndEncMessage = + Base64.decode( + "hQEOAynbo4lhNjcHEAP+K20MVhzdX57hf/cU8TH0prP0VePr9mmeBedzqqMn" + + "fp2p8Zb68zmcMlI/WiL5XMNLYRmCgEcXyWbKdP/XV9m9LDBe1CMAGrkCeGBy" + + "je69IQQ5LS9vDPyEMF4iAAv/EqACjqHkizdY/a/FRx/t2ioXYdEC2jA6kS9C" + + "McpsNz16DE8EAIk3uKn4bGo/+15TXkyFYzW5Cf71SfRoHNmU2zAI93zhjN+T" + + "B7mGJwWXzsMkIO6FkMU5TCSrwZS3DBWCIaJ6SYoaawE/C/2j9D7bX1Jv8kum" + + "4cq+eZM7z6JYs6xend+WAwittpUxbEiyC2AJb3fBSXPAbLqWd6J6xbZZ7GDK" + + "r2Ca0pwBxwGhbMDyi2zpHLzw95H7Ah2wMcGU6kMLB+hzBSZ6mSTGFehqFQE3" + + "2BnAj7MtnbghiefogacJ891jj8Y2ggJeKDuRz8j2iICaTOy+Y2rXnnJwfYzm" + + "BMWcd2h1C5+UeBJ9CrrLniCCI8s5u8z36Rno3sfhBnXdRmWSxExXtocbg1Ht" + + "dyiThf6TK3W29Yy/T6x45Ws5zOasaJdsFKM="); + char[] pass = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + + private boolean notEqual( + byte[] b1, + byte[] b2) + { + if (b1.length != b2.length) + { + return true; + } + + for (int i = 0; i != b2.length; i++) + { + if (b1[i] != b2[i]) + { + return true; + } + } + + return false; + } + + public TestResult perform() + { + try + { + String file = null; + KeyFactory fact = KeyFactory.getInstance("DSA", "SC"); + PGPPublicKey pubKey = null; + PrivateKey privKey = null; + + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPObjectFactory pgpFact = new PGPObjectFactory(testPubKeyRing); + + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)pgpFact.nextObject(); + + pubKey = pgpPub.getPublicKey(); + + // + // Read the private key + // + PGPSecretKeyRing sKey = new PGPSecretKeyRing(testPrivKeyRing); + PGPPrivateKey pgpPrivKey = sKey.getSecretKey().extractPrivateKey(pass, "SC"); + + // + // signature generation + // + String data = "hello world!"; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(PGPPublicKey.DSA, PGPUtil.SHA1, "SC"); + + sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); + + PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator( + PGPCompressedData.ZIP); + + BCPGOutputStream bcOut = new BCPGOutputStream(cGen.open(bOut)); + + sGen.generateOnePassVersion(false).encode(bcOut); + + PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); + OutputStream lOut = lGen.open(bcOut, PGPLiteralData.BINARY, "_CONSOLE", data.getBytes().length, new Date()); + int ch; + + while ((ch = testIn.read()) >= 0) + { + lOut.write(ch); + sGen.update((byte)ch); + } + + sGen.generate().encode(bcOut); + + lGen.close(); + + cGen.close(); + + // + // verify generated signature + // + pgpFact = new PGPObjectFactory(bOut.toByteArray()); + + PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + PGPOnePassSignature ops = p1.get(0); + + PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); + + InputStream dIn = p2.getInputStream(); + + ops.initVerify(pubKey, "SC"); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + return new SimpleTestResult(false, getName() + ": Failed generated signature check"); + } + + // + // test encryption + // + + // + // find a key sutiable for encryption + // + long pgpKeyID = 0; + PublicKey pKey = null; + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pgpKey = (PGPPublicKey)it.next(); + + if (pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT + || pgpKey.getAlgorithm() == PGPPublicKey.ELGAMAL_GENERAL) + { + pKey = pgpKey.getKey("SC"); + pgpKeyID = pgpKey.getKeyID(); + + // + // verify the key + // + + } + } + + Cipher c = Cipher.getInstance("ElGamal/None/PKCS1Padding", "SC"); + + c.init(Cipher.ENCRYPT_MODE, pKey); + + byte[] in = "hello world".getBytes(); + + byte[] out = c.doFinal(in); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + c.init(Cipher.DECRYPT_MODE, pgpPrivKey.getKey()); + + out = c.doFinal(out); + + if (notEqual(in, out)) + { + return new SimpleTestResult(false, getName() + ": decryption failed."); + } + + // + // encrypted message + // + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + PGPObjectFactory pgpF = new PGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + InputStream inLd = ld.getDataStream(); + + while ((ch = inLd.read()) >= 0) + { + bOut.write(ch); + } + + if (notEqual(bOut.toByteArray(), text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in decrypted packet"); + } + + // + // signed and encrypted message + // + pgpF = new PGPObjectFactory(signedAndEncMessage); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + pgpFact = new PGPObjectFactory(clear); + + c1 = (PGPCompressedData)pgpFact.nextObject(); + + pgpFact = new PGPObjectFactory(c1.getDataStream()); + + p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); + + ops = p1.get(0); + + ld = (PGPLiteralData)pgpFact.nextObject(); + + bOut = new ByteArrayOutputStream(); + + if (!ld.getFileName().equals("test.txt")) + { + throw new RuntimeException("wrong filename in packet"); + } + + inLd = ld.getDataStream(); + + // + // note: we use the DSA public key here. + // + ops.initVerify(pgpPub.getPublicKey(), "SC"); + + while ((ch = inLd.read()) >= 0) + { + ops.update((byte)ch); + bOut.write(ch); + } + + p3 = (PGPSignatureList)pgpFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + return new SimpleTestResult(false, getName() + ": Failed signature check"); + } + + if (notEqual(bOut.toByteArray(), text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in decrypted packet"); + } + + // + // encrypt + // + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(SymmetricKeyAlgorithmTags.TRIPLE_DES, new SecureRandom(), "SC"); + PGPPublicKey puK = sKey.getSecretKey(pgpKeyID).getPublicKey(); + + cPk.addMethod(puK); + + OutputStream cOut = cPk.open(cbOut, bOut.toByteArray().length); + + cOut.write(text); + + cOut.close(); + + pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + encList = (PGPEncryptedDataList)pgpF.nextObject(); + + encP = (PGPPublicKeyEncryptedData)encList.get(0); + + pgpPrivKey = sKey.getSecretKey(pgpKeyID).extractPrivateKey(pass, "SC"); + + clear = encP.getDataStream(pgpPrivKey, "SC"); + + bOut.reset(); + + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + out = bOut.toByteArray(); + + if (notEqual(out, text)) + { + return new SimpleTestResult(false, getName() + ": wrong plain text in generated packet"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + e.printStackTrace(); + if (e instanceof PGPException) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public String getName() + { + return "PGPDSAElGamalTest"; + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + Test test = new PGPDSAElGamalTest(); + TestResult result = test.perform(); + + System.out.println(result.toString()); + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDHTest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDHTest.java new file mode 100644 index 000000000..997b73fca --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDHTest.java @@ -0,0 +1,313 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPECDHTest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB" + + "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l" + + "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV" + + "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi" + + "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr" + + "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy" + + "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI" + + "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS" + + "dmwmbysD192L3z4A/ReXEa0gtv8OFWjuALD1ovEK8TpDORLUb6IuUb5jUIzY"); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKs/gcDAo11YYCae/K2" + + "1uKGJ/uU4b4QHYnPIsAdYpuo5HIdoAOL/WwduRa8C6vSFrtMJLDqPK3BUpMz3CXN" + + "GyMhjuaHKP5MPbBZkIfgUGZO5qvU9+i0UFRlc3QgRUNEU0EtRUNESCAoS2V5IGFu" + + "ZCBzdWJrZXkgYXJlIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhLmVjZGhAZXhh" + + "bXBsZS5jb20+iHoEExMIACIFAlG+BsACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B" + + "AheAAAoJEPfAOVaMWj1Tk7YBAOLz007oltog5sLreMwxayIBpAesEI+QNkuJuA2/" + + "vcMuAP99qi4fhhkmz5w41AE9Wc/VtobU2lZHmmk3clQsVyuFyg=="); + + byte[] testMessage = + Base64.decode( + "hH4Dp5+FdoujIBwSAgMErx4BSvgXY3irwthgxU8zPoAoR+8rhmxdpwbw6ZJAO2GX" + + "azWJ85JNcobHKDeGeUq6wkTFu+g6yG99gIX8J5xJAjBRhyCRcaFgwbdDV4orWTe3" + + "iewiT8qs4BQ23e0c8t+thdKoK4thMsCJy7wSKqY0sJTSVAELroNbCOi2lcO15YmW" + + "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" + + "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg=="); + + private void generate() + throws Exception + { + // + // Generate a master key + // + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(256); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // Generate an encryption key + // + keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(256); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, + new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), + new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + keyRingGen.addSubKey(ecdhKeyPair); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + // TODO: add check of KdfParameters + doBasicKeyRingCheck(pubRing); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + private void testDecrypt(PGPSecretKeyRing secretKeyRing) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(testMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + PGPSecretKey secretKey = secretKeyRing.getSecretKey(); // secretKeyRing.getSecretKey(encP.getKeyID()); +// +// PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey()extractPrivateKey(null); +// +// clear = encP.getDataStream(pgpPrivKey, "SC"); +// +// bOut.reset(); +// +// while ((ch = clear.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// out = bOut.toByteArray(); +// +// if (!areEqual(out, text)) +// { +// fail("wrong plain text in generated packet"); +// } + } + + private void encryptDecryptTest() + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(256); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + ByteArrayOutputStream ldOut = new ByteArrayOutputStream(); + OutputStream pOut = lData.open(ldOut, PGPLiteralDataGenerator.UTF8, PGPLiteralData.CONSOLE, text.length, new Date()); + + pOut.write(text); + + pOut.close(); + + byte[] data = ldOut.toByteArray(); + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("SC").setSecureRandom(new SecureRandom())); + + cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(ecdhKeyPair.getPublicKey()).setProvider("SC")); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), data.length); + + cOut.write(data); + + cOut.close(); + + PGPObjectFactory pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(ecdhKeyPair.getPrivateKey())); + + pgpF = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpF.nextObject(); + + clear = ld.getInputStream(); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int ch; + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + byte[] out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + doBasicKeyRingCheck(pubKeyRing); + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + testDecrypt(secretKeyRing); + + encryptDecryptTest(); + + generate(); + } + + private void doBasicKeyRingCheck(PGPPublicKeyRing pubKeyRing) + throws PGPException, SignatureException + { + for (Iterator it = pubKeyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + if (pubKey.isMasterKey()) + { + if (pubKey.isEncryptionKey()) + { + fail("master key showed as encryption key!"); + } + } + else + { + if (!pubKey.isEncryptionKey()) + { + fail("sub key not encryption key!"); + } + + for (Iterator sigIt = pubKeyRing.getPublicKey().getSignatures(); sigIt.hasNext();) + { + PGPSignature certification = (PGPSignature)sigIt.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("subkey certification does not verify"); + } + } + } + } + } + + public String getName() + { + return "PGPECDHTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDHTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDSATest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDSATest.java new file mode 100644 index 000000000..a879d835a --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPECDSATest.java @@ -0,0 +1,159 @@ +package org.spongycastle.openpgp.test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Security; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; + +public class PGPECDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8FetDVFQ0RTQSAoS2V5" + + "IGlzIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhQGV4YW1wbGUuY29tPoh6BBMT" + + "CAAiBQJRvgeoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDqO46kgPLi" + + "vN1hAP4n0UApR36ziS5D8KUt7wEpBujQE4G3+efATJ+DMmY/SgEA+wbdDynFf/V8" + + "pQs0+FtCYQ9schzIur+peRvol7OrNnc="); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8Fe/gcDAqTWSUiFpEno" + + "1n8izmLaWTy8GYw5/lK4R2t6D347YGgTtIiXfoNPOcosmU+3OibyTm2hc/WyG4fL" + + "a0nxFtj02j0Bt/Fw0N4VCKJwKL/QJT+0NUVDRFNBIChLZXkgaXMgMjU2IGJpdHMg" + + "bG9uZykgPHRlc3QuZWNkc2FAZXhhbXBsZS5jb20+iHoEExMIACIFAlG+B6gCGwMG" + + "CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOo7jqSA8uK83WEA/ifRQClHfrOJ" + + "LkPwpS3vASkG6NATgbf558BMn4MyZj9KAQD7Bt0PKcV/9XylCzT4W0JhD2xyHMi6" + + "v6l5G+iXs6s2dw=="); + + private void generateAndSign() + throws Exception + { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(256); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // try a signature + // + PGPSignatureGenerator signGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PGPPublicKey.ECDSA, HashAlgorithmTags.SHA256).setProvider("SC")); + + signGen.init(PGPSignature.BINARY_DOCUMENT, ecdsaKeyPair.getPrivateKey()); + + signGen.update("hello world!".getBytes()); + + PGPSignature sig = signGen.generate(); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), ecdsaKeyPair.getPublicKey()); + + sig.update("hello world!".getBytes()); + + if (!sig.verify()) + { + fail("signature failed to verify!"); + } + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + for (Iterator it = pubKeyRing.getPublicKey().getSignatures(); it.hasNext();) + { + PGPSignature certification = (PGPSignature)it.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("self certification does not verify"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + + generateAndSign(); + } + + public String getName() + { + return "PGPECDSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java new file mode 100644 index 000000000..4510f3864 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.1/org/spongycastle/openpgp/test/PGPKeyRingTest.java @@ -0,0 +1,968 @@ +package org.spongycastle.openpgp.test; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTestResult; +import org.spongycastle.util.test.Test; +import org.spongycastle.util.test.TestResult; + +public class PGPKeyRingTest + implements Test +{ + byte[] pub1 = Base64.decode( + "mQGiBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWrQidGVzdCAoVGVzdCBrZXkpIDx0ZXN0QHViaWNh" + + "bGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB4TOABgsJCAcDAgMVAgMDFgIB" + + "Ah4BAheAAAoJEJh8Njfhe8KmGDcAoJWr8xgPr75y/Cp1kKn12oCCOb8zAJ4p" + + "xSvk4K6tB2jYbdeSrmoWBZLdMLACAAC5AQ0EQDzfARAEAJeUAPvUzJJbKcc5" + + "5Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj47UPAD/tQxwz8VAwJySx82ggN" + + "LxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j2BVqZAaX3q79a3eMiql1T0oE" + + "AGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOHAAQNBACD0mVMlAUgd7REYy/1" + + "mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWaHz6CN1XptdwpDeSYEOFZ0PSu" + + "qH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85efMBA9jUv/DeBOzRWMFG6sC6y" + + "k8NGG7Swea7EHKeQI40G3jgO/+xANtMyTIhPBBgRAgAPBQJAPN8BAhsMBQkB" + + "4TOAAAoJEJh8Njfhe8KmG7kAn00mTPGJCWqmskmzgdzeky5fWd7rAKCNCp3u" + + "ZJhfg0htdgAfIy8ppm05vLACAAA="); + + byte[] sec1 = Base64.decode( + "lQHhBEA83v0RBADzKVLVCnpWQxX0LCsevw/3OLs0H7MOcLBQ4wMO9sYmzGYn" + + "xpVj+4e4PiCP7QBayWyy4lugL6Lnw7tESvq3A4v3fefcxaCTkJrryiKn4+Cg" + + "y5rIBbrSKNtCEhVi7xjtdnDjP5kFKgHYjVOeIKn4Cz/yzPG3qz75kDknldLf" + + "yHxp2wCgwW1vAE5EnZU4/UmY7l8kTNkMltMEAJP4/uY4zcRwLI9Q2raPqAOJ" + + "TYLd7h+3k/BxI0gIw96niQ3KmUZDlobbWBI+VHM6H99vcttKU3BgevNf8M9G" + + "x/AbtW3SS4De64wNSU3189XDG8vXf0vuyW/K6Pcrb8exJWY0E1zZQ1WXT0gZ" + + "W0kH3g5ro//Tusuil9q2lVLF2ovJA/0W+57bPzi318dWeNs0tTq6Njbc/GTG" + + "FUAVJ8Ss5v2u6h7gyJ1DB334ExF/UdqZGldp0ugkEXaSwBa2R7d3HBgaYcoP" + + "Ck1TrovZzEY8gm7JNVy7GW6mdOZuDOHTxyADEEP2JPxh6eRcZbzhGuJuYIif" + + "IIeLOTI5Dc4XKeV32a+bWv4CAwJ5KgazImo+sGBfMhDiBcBTqyDGhKHNgHic" + + "0Pky9FeRvfXTc2AO+jGmFPjcs8BnTWuDD0/jkQnRZpp1TrQidGVzdCAoVGVz" + + "dCBrZXkpIDx0ZXN0QHViaWNhbGwuY29tPohkBBMRAgAkBQJAPN79AhsDBQkB" + + "4TOABgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEJh8Njfhe8KmGDcAn3XeXDMg" + + "BZgrZzFWU2IKtA/5LG2TAJ0Vf/jjyq0jZNZfGfoqGTvD2MAl0rACAACdAVgE" + + "QDzfARAEAJeUAPvUzJJbKcc55Iyb13+Gfb8xBWE3HinQzhGr1v6A1aIZbRj4" + + "7UPAD/tQxwz8VAwJySx82ggNLxCk4jW9YtTL3uZqfczsJngV25GoIN10f4/j" + + "2BVqZAaX3q79a3eMiql1T0oEAGmD7tO1LkTvWfm3VvA0+t8/6ZeRLEiIqAOH" + + "AAQNBACD0mVMlAUgd7REYy/1mL99Zlu9XU0uKyUex99sJNrcx1aj8rIiZtWa" + + "Hz6CN1XptdwpDeSYEOFZ0PSuqH9ByM3OfjU/ya0//xdvhwYXupn6P1Kep85e" + + "fMBA9jUv/DeBOzRWMFG6sC6yk8NGG7Swea7EHKeQI40G3jgO/+xANtMyTP4C" + + "AwJ5KgazImo+sGBl2C7CFuI+5KM4ZhbtVie7l+OiTpr5JW2z5VgnV3EX9p04" + + "LcGKfQvD65+ELwli6yh8B2zGcipqTaYk3QoYNIhPBBgRAgAPBQJAPN8BAhsM" + + "BQkB4TOAAAoJEJh8Njfhe8KmG7kAniuRkaFFv1pdCBN8JJXpcorHmyouAJ9L" + + "xxmusffR6OI7WgD3XZ0AL8zUC7ACAAA="); + + char[] pass1 = "qwertzuiop".toCharArray(); + + byte[] pub2 = Base64.decode( + "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"); + + byte[] sec2 = Base64.decode( + "lQHpBEBtfW8RBADfWjTxFedIbGBNVgh064D/OCf6ul7x4PGsCl+BkAyheYkr" + + "mVUsChmBKoeXaY+Fb85wwusXzyM/6JFK58Rg+vEb3Z19pue8Ixxq7cRtCtOA" + + "tOP1eKXLNtTRWJutvLkQmeOa19UZ6ziIq23aWuWKSq+KKMWek2GUnGycnx5M" + + "W0pn1QCg/39r9RKhY9cdKYqRcqsr9b2B/AsD/Ru24Q15Jmrsl9zZ6EC47J49" + + "iNW5sLQx1qf/mgfVWQTmU2j6gq4ND1OuK7+0OP/1yMOUpkjjcqxFgTnDAAoM" + + "hHDTzCv/aZzIzmMvgLsYU3aIMfbz+ojpuASMCMh+te01cEMjiPWwDtdWWOdS" + + "OSyX9ylzhO3PiNDks8R83onsacYpA/9WhTcg4bvkjaj66I7wGZkm3BmTxNSb" + + "pE4b5HZDh31rRYhY9tmrryCfFnU4BS2Enjj5KQe9zFv7pUBCBW2oFo8i8Osn" + + "O6fa1wVN4fBHC6wqWmmpnkFerNPkiC9V75KUFIfeWHmT3r2DVSO3dfdHDERA" + + "jFIAioMLjhaX6DnODF5KQv4JAwIJH6A/rzqmMGAG4e+b8Whdvp8jaTGVT4CG" + + "M1b65rbiDyAuf5KTFymQBOIi9towgFzG9NXAZC07nEYSukN56tUTUDNVsAGH" + + "tCZTYWkgUHVsbGFiaG90bGEgPHBzYWlAbXlqYXZhd29ybGQuY29tPrADA///" + + "iQBXBBARAgAXBQJAbX1vBwsJCAcDAgoCGQEFGwMAAAAACgkQpdB/9FP325iZ" + + "8ACgwrFXmDajAh+bLbDu9Iu85BSm+84AnieMHrEmi3nbes8BBb2K0+gYZ6SK" + + "sAFnnQJqBEBtfW8QCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoB" + + "p1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3b" + + "zpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa" + + "8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPw" + + "pVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obE" + + "AxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7" + + "AAICB/0Xv+ckcNrInekFVNNVKraDlYpRcyDHEYpO85qGJVFxs7TfDSkoCQfz" + + "kI5qRV97nqwxilZGXfpp2M2go7FpyKjDvgPVHmydj+G/+QFc8GTOjhQyRsA0" + + "hVpnQhycun6uSRdwNVtEKJtl/wV8JjXNdup3rzRS0SBFu/UPzAUGp8y3uQuy" + + "5/0WcjDq8rgNTTrPlkODblEcaazBoPJjozA0YKsoZsd/0j9Pswy6EYX+KfxR" + + "TyG/y5EQ6Ox46qSUbf+ewjWEP8x1QLt/NeIxE7hG9qEuh5c65oOQxgAt0mTm" + + "eY24IvVg+lsUa20jdewrtSs/8WF3w0Po2ujAKqs3qR524r64/gkDAmmp39NN" + + "U2pqYHokufIOab2VpD7iQo8UjHZNwR6dpjyky9dVfIe4MA0H+t0ju8UDdWoe" + + "IkRu8guWsI83mjGPbIq8lmsZOXPCA8hPuBmL0iaj8TnuotmsBjIBsAGHiQBM" + + "BBgRAgAMBQJAbX1vBRsMAAAAAAoJEKXQf/RT99uYL/QAnivXK/K7ip85eUOC" + + "ZW4Ky8cVajreAJ9EisIHYm/83VyCmarQvwsYczsb8rABZ5UDqARAbX2jAQgA" + + "ydq8tipHt99oM9tur6VBm8sIr/e4DRCXKeA3JNqwNbRYLX+vLLZ3HvKk44KJ" + + "yOc9h+Dcs/lORgAagyPEfn7HMzrF85H7z8TArXM3cDn5f89IEOViND10RUqu" + + "+9zuIw6n7UrVUx/hSDxhqHbY63h4sY/QdEJeK/m+B0E2wCX+5ecEm4NhCYus" + + "SeKqr/pTEkcofFmhL/mnXcKrs18oHUIkK4ldoIRNbZ2j5wOk3qvSW2QX+v4R" + + "L7YvuPHKgdy9DhiismgMyUA3rGkhlDx01pNg/+czH+kNLeyFTNtT5Rg9Cut4" + + "kswXWkP5hY/kxMpplOj5T+o+MMUZxp0ieE/G+HfWywARAQABCWEWL2cKQKcm" + + "XFTNsWgRoOcOkKyJ/osERh2PzNWvOF6/ir1BMRsg0qhd+hEcoWHaT+7Vt12i" + + "5Y2Ogm2HFrVrS5/DlV/rw0mkALp/3cR6jLOPyhmq7QGwhG27Iy++pLIksXQa" + + "RTboa7ZasEWw8zTqa4w17M5Ebm8dtB9Mwl/kqU9cnIYnFXj38BWeia3iFBNG" + + "PD00hqwhPUCTUAcH9qQPSqKqnFJVPe0KQWpq78zhCh1zPUIa27CE86xRBf45" + + "XbJwN+LmjCuQEnSNlloXJSPTRjEpla+gWAZz90fb0uVIR1dMMRFxsuaO6aCF" + + "QMN2Mu1wR/xzTzNCiQf8cVzq7YkkJD8ChJvu/4BtWp3BlU9dehAz43mbMhaw" + + "Qx3NmhKR/2dv1cJy/5VmRuljuzC+MRtuIjJ+ChoTa9ubNjsT6BF5McRAnVzf" + + "raZK+KVWCGA8VEZwe/K6ouYLsBr6+ekCKIkGZdM29927m9HjdFwEFjnzQlWO" + + "NZCeYgDcK22v7CzobKjdo2wdC7XIOUVCzMWMl+ch1guO/Y4KVuslfeQG5X1i" + + "PJqV+bwJriCx5/j3eE/aezK/vtZU6cchifmvefKvaNL34tY0Myz2bOx44tl8" + + "qNcGZbkYF7xrNCutzI63xa2ruN1p3hNxicZV1FJSOje6+ITXkU5Jmufto7IJ" + + "t/4Q2dQefBQ1x/d0EdX31yK6+1z9dF/k3HpcSMb5cAWa2u2g4duAmREHc3Jz" + + "lHCsNgyzt5mkb6kS43B6og8Mm2SOx78dBIOA8ANzi5B6Sqk3/uN5eQFLY+sQ" + + "qGxXzimyfbMjyq9DdqXThx4vlp3h/GC39KxL5MPeB0oe6P3fSP3C2ZGjsn3+" + + "XcYk0Ti1cBwBOFOZ59WYuc61B0wlkiU/WGeaebABh7QuU2FuZGh5YSBQdWxs" + + "YWJob3RsYSA8cHNhbmRoeWFAbXlqYXZhd29ybGQuY29tPrADA///iQEtBBAB" + + "AgAXBQJAbX2jBwsJCAcDAgoCGQEFGwMAAAAACgkQx87DL9gOvoeVUwgAkQXY" + + "iF0CxhKbDnuabAssnOEwJrutgCROCJRQvIwTe3fe6hQaWn2Yowt8OQtNFiR8" + + "GfAY6EYxyFLKzZbAI/qtq5fHmN3eRSyNWe6d6e17hqZZL7kf2sVkyGTChHj7" + + "Jiuo7vWkdqT2MJN6BW5tS9CRH7MeD839STv+4mAAO9auGvSvicP6UEQikAyC" + + "y/ihoJxLQlspfbSNpi0vrUjCPT7NtWwfP0qF64i9LYkjzLqihnu+UareqOPh" + + "XcWnyFKrjmg4ezQkweNU2pdvCLbcW24FhT92ivHgpLyWTswXcqjhFjVlRr0+" + + "2sIz7v1k0budCsJ7PjzOoH0hJxCvsJQMlZR/e7ABZ50DqARAbX2kAQgAm5j+" + + "/LO2M4pKm/VUPkYuj3eefHkzjM6nKbvRZX1Oqyf+6CJTxQskUWKAtkzzKafP" + + "dS5Wg0CMqeXov+EFod4bPEYccszncKd1U8NRwacbEpCvvvB84Yl2YwdWpDpk" + + "ryyyLI4PbCHkeuwx9Dc2z7t4XDB6FyAJTMAkia7nzYa/kbeUO3c2snDb/dU7" + + "uyCsyKtTZyTyhTgtl/f9L03Bgh95y3mOUz0PimJ0Sg4ANczF4d04BpWkjLNV" + + "Ji489ifWodPlHm1hag5drYekYpWJ+3g0uxs5AwayV9BcOkPKb1uU3EoYQw+n" + + "n0Kn314Nvx2M1tKYunuVNLEm0PhA/+B8PTq8BQARAQABCXo6bD6qi3s4U8Pp" + + "Uf9l3DyGuwiVPGuyb2P+sEmRFysi2AvxMe9CkF+CLCVYfZ32H3Fcr6XQ8+K8" + + "ZGH6bJwijtV4QRnWDZIuhUQDS7dsbGqTh4Aw81Fm0Bz9fpufViM9RPVEysxs" + + "CZRID+9jDrACthVsbq/xKomkKdBfNTK7XzGeZ/CBr9F4EPlnBWClURi9txc0" + + "pz9YP5ZRy4XTFgx+jCbHgKWUIz4yNaWQqpSgkHEDrGZwstXeRaaPftcfQN+s" + + "EO7OGl/Hd9XepGLez4vKSbT35CnqTwMzCK1IwUDUzyB4BYEFZ+p9TI18HQDW" + + "hA0Wmf6E8pjS16m/SDXoiRY43u1jUVZFNFzz25uLFWitfRNHCLl+VfgnetZQ" + + "jMFr36HGVQ65fogs3avkgvpgPwDc0z+VMj6ujTyXXgnCP/FdhzgkRFJqgmdJ" + + "yOlC+wFmZJEs0MX7L/VXEXdpR27XIGYm24CC7BTFKSdlmR1qqenXHmCCg4Wp" + + "00fV8+aAsnesgwPvxhCbZQVp4v4jqhVuB/rvsQu9t0rZnKdDnWeom/F3StYo" + + "A025l1rrt0wRP8YS4XlslwzZBqgdhN4urnzLH0/F3X/MfjP79Efj7Zk07vOH" + + "o/TPjz8lXroPTscOyXWHwtQqcMhnVsj9jvrzhZZSdUuvnT30DR7b8xcHyvAo" + + "WG2cnF/pNSQX11RlyyAOlw9TOEiDJ4aLbFdkUt+qZdRKeC8mEC2xsQ87HqFR" + + "pWKWABWaoUO0nxBEmvNOy97PkIeGVFNHDLlIeL++Ry03+JvuNNg4qAnwacbJ" + + "TwQzWP4vJqre7Gl/9D0tVlD4Yy6Xz3qyosxdoFpeMSKHhgKVt1bk0SQP7eXA" + + "C1c+eDc4gN/ZWpl+QLqdk2T9vr4wRAaK5LABh4kBIgQYAQIADAUCQG19pAUb" + + "DAAAAAAKCRDHzsMv2A6+h5C6B/0QWPJb1L9a/CHU7Of3zfHvzJxnk4S5fia5" + + "WBf+Ld7Y12CH+cVI/3PTXY63imIB3zLa21TNPC5cVFUJZI7hdwyJOW1R/QZa" + + "vGl5sosIjORqwq8vs1oqpKWc3tRue+wt/JjCWxw8G6jQiwCzM6PltqtOAL5e" + + "bJ4jZwzRmxs/RI4UYKO6EteipJA+i7OEZx0xGdL8mTl6HQx7vrJP9pM4/CtO" + + "5CmjPK70e2gyoiOxx9RsSc+9H59YJCrldogCCIlNuW7cvkt8K/uYdN9GAbFG" + + "RdanignRjpqSu3vTn8r/VO63+meZfKvmpI6i2b3o/UZ8Xh9lJu1vrRoNpnuP" + + "Nifs+ljmsAFn"); + + + char[] sec2pass1 = "sandhya".toCharArray(); + char[] sec2pass2 = "psai".toCharArray(); + + byte[] pub3 = Base64.decode( + "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" + + "n3FaE6UZE9GMDmCIAjhI+0X9aH6CsAIAAw=="); + + byte[] sec3 = Base64.decode( + "lQHhBEB9BH0RBACtYQtE7tna6hgGyGLpq+ds3r2cLC0ISn5dNw7tm9vwiNVF" + + "JA2N37RRrifw4PvgelRSvLaX3M3ZBqC9s1Metg3v4FSlIRtSLWCNpHSvNw7i" + + "X8C2Xy9Hdlbh6Y/50o+iscojLRE14upfR1bIkcCZQGSyvGV52V2wBImUUZjV" + + "s2ZngwCg7mu852vK7+euz4WaL7ERVYtq9CMEAJ5swrljerDpz/RQ4Lhp6KER" + + "KyuI0PUttO57xINGshEINgYlZdGaZHRueHe7uKfI19mb0T4N3NJWaZ0wF+Cn" + + "rixsq0VrTUfiwfZeGluNG73aTCeY45fVXMGTTSYXzS8T0LW100Xn/0g9HRyA" + + "xUpuWo8IazxkMqHJis2uwriYKpAfA/9anvj5BS9p5pfPjp9dGM7GTMIYl5f2" + + "fcP57f+AW1TVR6IZiMJAvAdeWuLtwLnJiFpGlnFz273pfl+sAuqm1yNceImR" + + "2SDDP4+vtyycWy8nZhgEuhZx3W3cWMQz5WyNJSY1JJHh9TCQkCoN8E7XpVP4" + + "zEPboB2GzD93mfD8JLHP+/4DAwIvYrn+YqRaaGAu19XUj895g/GROyP8WEaU" + + "Bd/JNqWc4kE/0guetGnPzq7G3bLVwiKfFd4X7BrgHAo3mrQtVGVzdCBLZXkg" + + "KG5vIGNvbW1lbnQpIDx0ZXN0QGJvdW5jeWNhc3RsZS5vcmc+iFkEExECABkF" + + "AkB9BH0ECwcDAgMVAgMDFgIBAh4BAheAAAoJEKnMV8vjZQOpSRQAoKZy6YS1" + + "irF5/Q3JlWiwbkN6dEuLAJ9lldRLOlXsuQ5JW1+SLEc6K9ho4rACAADRwXPB" + + "cQEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3Jl" + + "YXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZ" + + "EhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sA" + + "QwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy" + + "MjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAFAAUAwEiAAIRAQMRAf/EABoA" + + "AQACAwEAAAAAAAAAAAAAAAAEBQIDBgf/xAAoEAABAgUDBAEFAAAAAAAAAAAB" + + "AgMABBEhMQUSQQYTIiNhFFGBkcH/xAAXAQEAAwAAAAAAAAAAAAAAAAAEAgMF" + + "/8QAJBEAAQQAAwkAAAAAAAAAAAAAAQACERIEIfATMTJBUZGx0fH/2gAMAwEA" + + "AhEDEQA/APMuotJlJVxstqaPo22NlAUp+YsNO0qSUtBcMu6n6EtOHcfPAHHF" + + "I16++oajQtTA3DapK02HFR8UpE9pTbQWtKm2WG2rlxVyQTcfGbn7Qm0OIjL7" + + "7Wrs2NNm9lzTmmSxQ0PX4opSprk5tmESF6syggzGwOLG6gXgHFbZhBixk8Xl" + + "IDcOQLRKt+rX+3qC5ZLTQblpQlvwvxn9CMpZturVGkJHapQJphRH8hCLXbzr" + + "qpYsCx1zC5rtpJNuYQhASc0UAQv/2YhcBBMRAgAcBQJAfQV+AhsDBAsHAwID" + + "FQIDAxYCAQIeAQIXgAAKCRCpzFfL42UDqfa2AJ9hjtEeDTbTEAuuSbzhYFxN" + + "/qc0FACgsmzysdbBpuN65yK01tbEaeIMtqCwAgAAnQEUBEB9BH4QAwCqRuWO" + + "rxm5v/8WWBtPEUZnYuu3WYxefRKn6ON5ka5Ymp4AfKpKqKqn982tjlG13GVE" + + "P2ow11L4yQKGNm+VyVEapm7SpMD0SHg9vsJygAWrfMJnBYWSgE19W6o3ckUR" + + "sWsAAwYDAKDCSiSxC3Z5/POaCehw5iHs/vmVk5Wn831LSZEEwkSohNDbIEry" + + "ia/9tFXSt11AP4stJtZ0MH87/6JBbiIRD4JXcx+zfzchL7Teczr/06QMphSn" + + "sAENupbhLupfpR+BbP4DAwIvYrn+YqRaaGBjvFK1fbxCt7ZM4I2W/3BC0lCX" + + "m/NypKNspGflec8u96uUlA0fNCnxm6f9nbB0jpvoKi0g4iqAf+P2iEYEGBEC" + + "AAYFAkB9BH4ACgkQqcxXy+NlA6mtMgCgvccZA/Sg7BXVpxli47SYhxSHoM4A" + + "oNCOMplSnYTuh5ikKeBWtz36gC1psAIAAA=="); + + char[] sec3pass1 = "123456".toCharArray(); + + // + // GPG comment packets. + // + byte[] sec4 = Base64.decode( + "lQG7BD0PbK8RBAC0cW4Y2MZXmAmqYp5Txyw0kSQsFvwZKHNMFRv996IsN57URVF5" + + "BGMVPRBi9dNucWbjiSYpiYN13wE9IuLZsvVaQojV4XWGRDc+Rxz9ElsXnsYQ3mZU" + + "7H1bNQEofstChk4z+dlvPBN4GFahrIzn/CeVUn6Ut7dVdYbiTqviANqNXwCglfVA" + + "2OEePvqFnGxs1jhJyPSOnTED/RwRvsLH/k43mk6UEvOyN1RIpBXN+Ieqs7h1gFrQ" + + "kB+WMgeP5ZUsotTffVDSUS9UMxRQggVUW1Xml0geGwQsNfkr/ztWMs/T4xp1v5j+" + + "QyJx6OqNlkGdqOsoqkzJx0SQ1zBxdinFyyC4H95SDAb/RQOu5LQmxFG7quexztMs" + + "infEA/9cVc9+qCo92yRAaXRqKNVVQIQuPxeUsGMyVeJQvJBD4An8KTMCdjpF10Cp" + + "qA3t+n1S0zKr5WRUtvS6y60MOONO+EJWVWBNkx8HJDaIMNkfoqQoz3Krn7w6FE/v" + + "/5uwMd6jY3N3yJZn5nDZT9Yzv9Nx3j+BrY+henRlSU0c6xDc9QAAnjJYg0Z83VJG" + + "6HrBcgc4+4K6lHulCqH9JiM6RFNBX2ZhY3RvcjoAAK9hV206agp99GI6x5qE9+pU" + + "vs6O+Ich/SYjOkRTQV9mYWN0b3I6AACvYAfGn2FGrpBYbjnpTuFOHJMS/T5xg/0m" + + "IzpEU0FfZmFjdG9yOgAAr0dAQz6XxMwxWIn8xIZR/v2iN2L9C6O0EkZvbyBCYXIg" + + "PGJhekBxdXV4PohXBBMRAgAXBQI9D2yvBQsHCgMEAxUDAgMWAgECF4AACgkQUGLI" + + "YCIktfoGogCfZiXMJUKrScqozv5tMwzTTk2AaT8AniM5iRr0Du/Y08SL/NMhtF6H" + + "hJ89nO4EPQ9ssRADAI6Ggxj6ZBfoavuXd/ye99osW8HsNlbqhXObu5mCMNySX2wa" + + "HoWyRUEaUkI9eQw+MlHzIwzA32E7y2mU3OQBKdgLcBg4jxtcWVEg8ESKF9MpFXxl" + + "pExxWrr4DFBfCRcsTwAFEQL9G3OvwJuEZXgx2JSS41D3pG4/qiHYICVa0u3p/14i" + + "cq0kXajIk5ZJ6frCIAHIzuQ3n7jjzr05yR8s/qCrNbBA+nlkVNa/samk+jCzxxxa" + + "cR/Dbh2wkvTFuDFFETwQYLuZAADcDck4YGQAmHivVT2NNDCf/aTz0+CJWl+xRc2l" + + "Qw7D/SQjOkVMR19mYWN0b3I6AACbBnv9m5/bb/pjYAm2PtDp0CysQ9X9JCM6RUxH" + + "X2ZhY3RvcjoAAJsFyHnSmaWguTFf6lJ/j39LtUNtmf0kIzpFTEdfZmFjdG9yOgAA" + + "mwfwMD3LxmWtuCWBE9BptWMNH07Z/SQjOkVMR19mYWN0b3I6AACbBdhBrbSiM4UN" + + "y7khDW2Sk0e4v9mIRgQYEQIABgUCPQ9ssQAKCRBQYshgIiS1+jCMAJ9txwHnb1Kl" + + "6i/fSoDs8SkdM7w48wCdFvPEV0sSxE73073YhBgPZtMWbBo="); + + // + // PGP freeware version 7 + // + byte[] pub5 = Base64.decode( + "mQENBEBrBE4BCACjXVcNIFDQSofaIyZnALb2CRg+WY9uUqgHEEAOlPe03Cs5STM5" + + "HDlNmrh4TdFceJ46rxk1mQOjULES1YfHay8lCIzrD7FX4oj0r4DC14Fs1vXaSar2" + + "1szIpttOw3obL4A1e0p6N4jjsoG7N/pA0fEL0lSw92SoBrMbAheXRg4qNTZvdjOR" + + "grcuOuwgJRvPLtRXlhyLBoyhkd5mmrIDGv8QHJ/UjpeIcRXY9kn9oGXnEYcRbMaU" + + "VwXB4pLzWqz3ZejFI3lOxRWjm760puPOnGYlzSVBxlt2LgzUgSj1Mn+lIpWmAzsa" + + "xEiU4xUwEomQns72yYRZ6D3euNCibcte4SeXABEBAAG0KXBhbGFzaCBrYXNvZGhh" + + "biA8cGthc29kaGFuQHRpYWEtY3JlZi5vcmc+iQEuBBABAgAYBQJAawROCAsBAwkI" + + "BwIKAhkBBRsDAAAAAAoJEOfelumuiOrYqPEH+wYrdP5Tq5j+E5yN1pyCg1rwbSOt" + + "Dka0y0p7Oq/VIGLk692IWPItLEunnBXQtGBcWqklrvogvlhxtf16FgoyScfLJx1e" + + "1cJa+QQnVuH+VOESN6iS9Gp9lUfVOHv74mEMXw0l2Djfy/lnrkAMBatggyGnF9xF" + + "VXOLk1J2WVFm9KUE23o6qdB7RGkf31pN2eA7SWmkdJSkUH7o/QSFBI+UTRZ/IY5P" + + "ZIJpsdiIOqd9YMG/4RoSZuPqNRR6x7BSs8nQVR9bYs4PPlp4GfdRnOcRonoTeJCZ" + + "83RnsraWJnJTg34gRLBcqumhTuFKc8nuCNK98D6zkQESdcHLLTquCOaF5L+5AQ0E" + + "QGsETwEIAOVwNCTaDZvW4dowPbET1bI5UeYY8rAGLYsWSUfgaFv2srMiApyBVltf" + + "i6OLcPjcUCHDBjCv4pwx/C4qcHWb8av4xQIpqQXOpO9NxYE1eZnel/QB7DtH12ZO" + + "nrDNmHtaXlulcKNGe1i1utlFhgzfFx6rWkRL0ENmkTkaQmPY4gTGymJTUhBbsSRq" + + "2ivWqQA1TPwBuda73UgslIAHRd/SUaxjXoLpMbGOTeqzcKGjr5XMPTs7/YgBpWPP" + + "UxMlEQIiU3ia1bxpEhx05k97ceK6TSH2oCPQA7gumjxOSjKT+jEm+8jACVzymEmc" + + "XRy4D5Ztqkw/Z16pvNcu1DI5m6xHwr8AEQEAAYkBIgQYAQIADAUCQGsETwUbDAAA" + + "AAAKCRDn3pbprojq2EynB/4/cEOtKbI5UisUd3vkTzvWOcqWUqGqi5wjjioNtIM5" + + "pur2nFvhQE7SZ+PbAa87HRJU/4WcWMcoLkHD48JrQwHCHOLHSV5muYowb78X4Yh9" + + "epYtSJ0uUahcn4Gp48p4BkhgsPYXkxEImSYzAOWStv21/7WEMqItMYl89BV6Upm8" + + "HyTJx5MPTDbMR7X51hRg3OeQs6po3WTCWRzFIMyGm1rd/VK1L5ZDFPqO3S6YUJ0z" + + "cxecYruvfK0Wp7q834wE8Zkl/PQ3NhfEPL1ZiLr/L00Ty+77/FZqt8SHRCICzOfP" + + "OawcVGI+xHVXW6lijMpB5VaVIH8i2KdBMHXHtduIkPr9"); + + byte[] sec5 = Base64.decode( + "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"); + + + + char[] sec5pass1 = "12345678".toCharArray(); + + private boolean notEqual( + byte[] b1, + byte[] b2) + { + if (b1.length != b2.length) + { + return true; + } + + for (int i = 0; i != b2.length; i++) + { + if (b1[i] != b2[i]) + { + return true; + } + } + + return false; + } + + public TestResult test1() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub1); + + int count = 0; + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec1); + + rIt = secretRings.getKeyRings(); + count = 0; + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test2() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub2); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + PGPPublicKey pk = (PGPPublicKey)it.next(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + + keyCount++; + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec2); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + PGPPublicKey pk = k.getPublicKey(); + + byte[] pkBytes = pk.getEncoded(); + + PGPPublicKeyRing pkR = new PGPPublicKeyRing(pkBytes); + + if (k.getKeyID() == -4049084404703773049L + || k.getKeyID() == -1413891222336124627L) + { + k.extractPrivateKey(sec2pass1, "SC"); + } + else if (k.getKeyID() == -6498553574938125416L + || k.getKeyID() == 59034765524361024L) + { + k.extractPrivateKey(sec2pass2, "SC"); + } + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + return new SimpleTestResult(false, getName() + ": exception - "+ e); + } + } + + public TestResult test3() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub3); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec3); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test4() + { + try + { + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec4); + + Iterator rIt = secretRings.getKeyRings(); + int count = 0; + + byte[] encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec3pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult test5() + { + try + { + PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(pub5); + + int count = 0; + + byte[] encRing = pubRings.getEncoded(); + + pubRings = new PGPPublicKeyRingCollection(encRing); + + Iterator rIt = pubRings.getKeyRings(); + + while (rIt.hasNext()) + { + PGPPublicKeyRing pgpPub = (PGPPublicKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpPub.getEncoded(); + + pgpPub = new PGPPublicKeyRing(bytes); + + Iterator it = pgpPub.getPublicKeys(); + while (it.hasNext()) + { + keyCount++; + + it.next(); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of public keyrings"); + } + + PGPSecretKeyRingCollection secretRings = new PGPSecretKeyRingCollection(sec5); + + rIt = secretRings.getKeyRings(); + count = 0; + + encRing = secretRings.getEncoded(); + + secretRings = new PGPSecretKeyRingCollection(encRing); + + while (rIt.hasNext()) + { + PGPSecretKeyRing pgpSec = (PGPSecretKeyRing)rIt.next(); + + count++; + + int keyCount = 0; + + byte[] bytes = pgpSec.getEncoded(); + + pgpSec = new PGPSecretKeyRing(bytes); + + Iterator it = pgpSec.getSecretKeys(); + while (it.hasNext()) + { + keyCount++; + + PGPSecretKey k = (PGPSecretKey)it.next(); + + k.extractPrivateKey(sec5pass1, "SC"); + } + + if (keyCount != 2) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keys"); + } + } + + if (count != 1) + { + return new SimpleTestResult(false, getName() + ": wrong number of secret keyrings"); + } + + return new SimpleTestResult(true, getName() + ": Okay"); + } + catch (Exception e) + { + if (e instanceof PGPException) + { + if (((PGPException)e).getUnderlyingException() != null) + { + ((PGPException)e).getUnderlyingException().printStackTrace(); + } + } + return new SimpleTestResult(false, getName() + ": exception - " + e.toString()); + } + } + + public TestResult perform() + { + TestResult res = test1(); + if (!res.isSuccessful()) + { + return res; + } + + res = test2(); + if (!res.isSuccessful()) + { + return res; + } + + res = test3(); + if (!res.isSuccessful()) + { + return res; + } + + res = test4(); + if (!res.isSuccessful()) + { + return res; + } + + res = test5(); + if (!res.isSuccessful()) + { + return res; + } + + return res; + } + + public String getName() + { + return "PGPKeyRingTest"; + } + + public static void main( + String[] args) + { + Test test = new PGPKeyRingTest(); + TestResult result = test.perform(); + + System.out.println(result.toString()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.3/org/spongycastle/openpgp/test/AllTests.java b/libraries/spongycastle/pg/src/test/jdk1.3/org/spongycastle/openpgp/test/AllTests.java new file mode 100644 index 000000000..e7469c0f1 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.3/org/spongycastle/openpgp/test/AllTests.java @@ -0,0 +1,45 @@ +package org.spongycastle.openpgp.test; + +import java.security.Security; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testPGP() + { + Security.addProvider(new BouncyCastleProvider()); + + org.spongycastle.util.test.Test[] tests = RegressionTest.tests; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("OpenPGP Tests"); + + suite.addTestSuite(AllTests.class); + suite.addTestSuite(DSA2Test.class); + + return suite; + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDHTest.java b/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDHTest.java new file mode 100644 index 000000000..99a4053ef --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDHTest.java @@ -0,0 +1,313 @@ +package org.spongycastle.openpgp.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataGenerator; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPLiteralDataGenerator; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; +import org.spongycastle.util.test.UncloseableOutputStream; + +public class PGPECDHTest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB" + + "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l" + + "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV" + + "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi" + + "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr" + + "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy" + + "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI" + + "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS" + + "dmwmbysD192L3z4A/ReXEa0gtv8OFWjuALD1ovEK8TpDORLUb6IuUb5jUIzY"); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" + + "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKs/gcDAo11YYCae/K2" + + "1uKGJ/uU4b4QHYnPIsAdYpuo5HIdoAOL/WwduRa8C6vSFrtMJLDqPK3BUpMz3CXN" + + "GyMhjuaHKP5MPbBZkIfgUGZO5qvU9+i0UFRlc3QgRUNEU0EtRUNESCAoS2V5IGFu" + + "ZCBzdWJrZXkgYXJlIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhLmVjZGhAZXhh" + + "bXBsZS5jb20+iHoEExMIACIFAlG+BsACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B" + + "AheAAAoJEPfAOVaMWj1Tk7YBAOLz007oltog5sLreMwxayIBpAesEI+QNkuJuA2/" + + "vcMuAP99qi4fhhkmz5w41AE9Wc/VtobU2lZHmmk3clQsVyuFyg=="); + + byte[] testMessage = + Base64.decode( + "hH4Dp5+FdoujIBwSAgMErx4BSvgXY3irwthgxU8zPoAoR+8rhmxdpwbw6ZJAO2GX" + + "azWJ85JNcobHKDeGeUq6wkTFu+g6yG99gIX8J5xJAjBRhyCRcaFgwbdDV4orWTe3" + + "iewiT8qs4BQ23e0c8t+thdKoK4thMsCJy7wSKqY0sJTSVAELroNbCOi2lcO15YmW" + + "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" + + "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg=="); + + private void generate() + throws Exception + { + // + // Generate a master key + // + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(new ECNamedCurveGenParameterSpec("P-256")); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // Generate an encryption key + // + keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(new ECNamedCurveGenParameterSpec("P-256")); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, + new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), + new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + keyRingGen.addSubKey(ecdhKeyPair); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + // TODO: add check of KdfParameters + doBasicKeyRingCheck(pubRing); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + private void testDecrypt(PGPSecretKeyRing secretKeyRing) + throws Exception + { + PGPObjectFactory pgpF = new PGPObjectFactory(testMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + PGPSecretKey secretKey = secretKeyRing.getSecretKey(); // secretKeyRing.getSecretKey(encP.getKeyID()); +// +// PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey()extractPrivateKey(null); +// +// clear = encP.getDataStream(pgpPrivKey, "SC"); +// +// bOut.reset(); +// +// while ((ch = clear.read()) >= 0) +// { +// bOut.write(ch); +// } +// +// out = bOut.toByteArray(); +// +// if (!areEqual(out, text)) +// { +// fail("wrong plain text in generated packet"); +// } + } + + private void encryptDecryptTest() + throws Exception + { + byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' }; + + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH", "SC"); + + keyGen.initialize(new ECNamedCurveGenParameterSpec("P-256")); + + KeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date()); + + PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); + ByteArrayOutputStream ldOut = new ByteArrayOutputStream(); + OutputStream pOut = lData.open(ldOut, PGPLiteralDataGenerator.UTF8, PGPLiteralData.CONSOLE, text.length, new Date()); + + pOut.write(text); + + pOut.close(); + + byte[] data = ldOut.toByteArray(); + + ByteArrayOutputStream cbOut = new ByteArrayOutputStream(); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("SC").setSecureRandom(new SecureRandom())); + + cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(ecdhKeyPair.getPublicKey()).setProvider("SC")); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), data.length); + + cOut.write(data); + + cOut.close(); + + PGPObjectFactory pgpF = new PGPObjectFactory(cbOut.toByteArray()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("SC").build(ecdhKeyPair.getPrivateKey())); + + pgpF = new PGPObjectFactory(clear); + + PGPLiteralData ld = (PGPLiteralData)pgpF.nextObject(); + + clear = ld.getInputStream(); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int ch; + while ((ch = clear.read()) >= 0) + { + bOut.write(ch); + } + + byte[] out = bOut.toByteArray(); + + if (!areEqual(out, text)) + { + fail("wrong plain text in generated packet"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + doBasicKeyRingCheck(pubKeyRing); + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + testDecrypt(secretKeyRing); + + encryptDecryptTest(); + + generate(); + } + + private void doBasicKeyRingCheck(PGPPublicKeyRing pubKeyRing) + throws PGPException, SignatureException + { + for (Iterator it = pubKeyRing.getPublicKeys(); it.hasNext();) + { + PGPPublicKey pubKey = (PGPPublicKey)it.next(); + + if (pubKey.isMasterKey()) + { + if (pubKey.isEncryptionKey()) + { + fail("master key showed as encryption key!"); + } + } + else + { + if (!pubKey.isEncryptionKey()) + { + fail("sub key not encryption key!"); + } + + for (Iterator sigIt = pubKeyRing.getPublicKey().getSignatures(); sigIt.hasNext();) + { + PGPSignature certification = (PGPSignature)sigIt.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("subkey certification does not verify"); + } + } + } + } + } + + public String getName() + { + return "PGPECDHTest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDHTest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDSATest.java b/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDSATest.java new file mode 100644 index 000000000..94c82756d --- /dev/null +++ b/libraries/spongycastle/pg/src/test/jdk1.4/org/spongycastle/openpgp/test/PGPECDSATest.java @@ -0,0 +1,159 @@ +package org.spongycastle.openpgp.test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Security; +import org.spongycastle.jce.spec.ECNamedCurveGenParameterSpec; +import java.util.Date; +import java.util.Iterator; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPKeyPair; +import org.spongycastle.openpgp.PGPKeyRingGenerator; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.util.Arrays; +import org.spongycastle.util.encoders.Base64; +import org.spongycastle.util.test.SimpleTest; + +public class PGPECDSATest + extends SimpleTest +{ + byte[] testPubKey = + Base64.decode( + "mFIEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8FetDVFQ0RTQSAoS2V5" + + "IGlzIDI1NiBiaXRzIGxvbmcpIDx0ZXN0LmVjZHNhQGV4YW1wbGUuY29tPoh6BBMT" + + "CAAiBQJRvgeoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDqO46kgPLi" + + "vN1hAP4n0UApR36ziS5D8KUt7wEpBujQE4G3+efATJ+DMmY/SgEA+wbdDynFf/V8" + + "pQs0+FtCYQ9schzIur+peRvol7OrNnc="); + + byte[] testPrivKey = + Base64.decode( + "lKUEUb4HqBMIKoZIzj0DAQcCAwSQynmjwsGJHYJakAEVYxrm3tt/1h8g9Uksx32J" + + "zG/ZH4RwaD0PbjzEe5EVBmCwSErRZxt/5AxXa0TEHWjya8Fe/gcDAqTWSUiFpEno" + + "1n8izmLaWTy8GYw5/lK4R2t6D347YGgTtIiXfoNPOcosmU+3OibyTm2hc/WyG4fL" + + "a0nxFtj02j0Bt/Fw0N4VCKJwKL/QJT+0NUVDRFNBIChLZXkgaXMgMjU2IGJpdHMg" + + "bG9uZykgPHRlc3QuZWNkc2FAZXhhbXBsZS5jb20+iHoEExMIACIFAlG+B6gCGwMG" + + "CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEOo7jqSA8uK83WEA/ifRQClHfrOJ" + + "LkPwpS3vASkG6NATgbf558BMn4MyZj9KAQD7Bt0PKcV/9XylCzT4W0JhD2xyHMi6" + + "v6l5G+iXs6s2dw=="); + + private void generateAndSign() + throws Exception + { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "SC"); + + keyGen.initialize(new ECNamedCurveGenParameterSpec("P-256")); + + KeyPair kpSign = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDSA, kpSign, new Date()); + + // + // try a signature + // + PGPSignatureGenerator signGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(PGPPublicKey.ECDSA, HashAlgorithmTags.SHA256).setProvider("SC")); + + signGen.init(PGPSignature.BINARY_DOCUMENT, ecdsaKeyPair.getPrivateKey()); + + signGen.update("hello world!".getBytes()); + + PGPSignature sig = signGen.generate(); + + sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), ecdsaKeyPair.getPublicKey()); + + sig.update("hello world!".getBytes()); + + if (!sig.verify()) + { + fail("signature failed to verify!"); + } + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, new JcaPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("SC").build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new JcaKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + } + + public void performTest() + throws Exception + { + PGPUtil.setDefaultProvider("SC"); + + // + // Read the public key + // + PGPPublicKeyRing pubKeyRing = new PGPPublicKeyRing(testPubKey, new JcaKeyFingerprintCalculator()); + + for (Iterator it = pubKeyRing.getPublicKey().getSignatures(); it.hasNext();) + { + PGPSignature certification = (PGPSignature)it.next(); + + certification.init(new JcaPGPContentVerifierBuilderProvider().setProvider("SC"), pubKeyRing.getPublicKey()); + + if (!certification.verifyCertification((String)pubKeyRing.getPublicKey().getUserIDs().next(), pubKeyRing.getPublicKey())) + { + fail("self certification does not verify"); + } + } + + // + // Read the private key + // + PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); + + + generateAndSign(); + } + + public String getName() + { + return "PGPECDSATest"; + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PGPECDSATest()); + } +} diff --git a/libraries/spongycastle/pg/src/test/resources/org/spongycastle/openpgp/test/bigpub.asc b/libraries/spongycastle/pg/src/test/resources/org/spongycastle/openpgp/test/bigpub.asc new file mode 100644 index 000000000..a3b0766c4 --- /dev/null +++ b/libraries/spongycastle/pg/src/test/resources/org/spongycastle/openpgp/test/bigpub.asc @@ -0,0 +1,15124 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.1.0 + +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 +gSCaHIULIYhkBBIRCAAMBQJRn3jMBYMHhh+AAAoJEICU1Dqrdkd9+3AA/1aE7e5fTpcnRxzz +T/vqRn8ZP7f9GI0UvA2gBAgfuzoRAQCP5kYQJoHJov66QdUlS/m0eRswwTZxMerp3jtuTj4Z +zohqBDARAgAqBQJC+KU9Ix0ATWlzdGFrZSB3aGlsZSBkb2luZyBsb2NhbCBzaWduLi4uAAoJ +EBVNeqxcW1sJ4vcAn3ERVGMi6UpBkZ44TlZJHvJOoVyrAJsFvfGdOvNSArCrd7wEHoxmMDTH +TIh1BDARAgA1BQJDAKDvLh0Abm8gbG9uZ2VyIGZlZWwgaXQncyBhcHByb3ByaWF0ZSB0byBz +aWduIHRoaXMACgkQvZIxoLH+UIuZKgCgvA/Be0txtOPZj+XORIjL2+ty7NQAoIQFv2pJySqb +bUq8+B8P0h0WYClZiHUEMBECADUFAkMAoPcuHQBubyBsb25nZXIgZmVlbCBpdCdzIGFwcHJv +cHJpYXRlIHRvIHNpZ24gdGhpcwAKCRDJwzyyhrFKOLZVAJ9LjQ+sFuDIk2ekajKJdGa6IrZ5 +yQCgjxXCS0IoX4fTr1lFYSGgWDHAaGOIdQQwEQIANQUCQwCg/S4dAG5vIGxvbmdlciBmZWVs +IGl0J3MgYXBwcm9wcmlhdGUgdG8gc2lnbiB0aGlzAAoJEF8I49t3ADyqCPEAoM/UfFPGDqx8 +fwgX4E6bcFHqWe3/AKDnMab2YjUkZyWQ5SorQxPEBXmWuYiMBDARAgBMBQJGgTMRRR0ASSBk +aWRuJ3QgcmVhbGx5IGRvIGEgZ29vZCBjaGVjayBhYm91dCB0aGUgYXV0aGVudGljaXR5IG9m +IHRoaXMga2V5LgAKCRD8sLtcXx+/cMwWAJ9fIufE249FIu+9hXdGEYtNqHgPiwCeLTir5fID +sEA3DspaFO2bfrTg0YCIkwQwEQIAUwUCRo9FTkwdAEkgZG9uJ3QgcmVtZW1iZXIgd2h5IEkg +c2lnbmVkIGl0IG9yaWdpbmFsbHksIHNvIEknbSBmaXhpbmcgdGhhdCBlcnJvciBub3cuAAoJ +EFWOCYKC0iQcTIEAnR3e3pOl8M8BKC2ZoqjO/64JLEW1AJ9akHVwIFv+4R72Ti6h4aOtPC2v +TYiVAwUQQbrTnCnYReUHfp2tAQEWTQP/eTYr2U/tgLzbwXuFJcZB1j/CLL33eMiv8YYpsoka +nueGZFxHaR1W72Q08VkNIs/lMh5xxkWgaimD4wP14w3mCBrZTesGAKLHFWDMDXvrBWA5NfTG +PYr4Yb/2Ek3EaF8RmEVo9bFGHyjPD/cm99V2/UcfRNiRyRxjen5/aFFH8giIlQMFEEHCKbNg +WuXggDDyuQEBaawEAK6FlkYemS3hLE6rrWdX+z2DlPuBSp1P/aqjvtXwhhHBpaQyeze5fB2w +YdjpfCXhSIAWcoLeW+Jso2JKxzbYEpuqqkkHSlnOc0WYMX6chGqYqQtIOfC89eUOiKtCi3K8 +6J5HAbTpPOivmwlYbluw0cbVop0qWZBxk5EJDwko+UcqiJUDBRBBw47bmGpB7xiMt8kBARkJ +A/925B3/BrFuLPHnUTDfk7pitLheJ8tY6AribcClGWGD44gcNBob3hkPWi32Fx2TlnAVP/FZ +N7V4aJuPfAGAJXRjaA+Jsj0tfS5G6qeSGGn0FkcIq6pQjatS+b0+vDgnw4Y0b0qiiS5WE2Ev +5csq9ZQlpA2GlLN8MVcPSd/xlG5igIiVAwUQQcOO25hqQe8YjLfJAQEZCQP/duQd/waxbizx +51Ew35O6YrS4XifLWOgK4m3ApRlhg+OIHDQaG94ZD1ot9hcdk5ZwFT/xWTe1eGibj3wBgCV0 +Y2gPibI9LX0uRuqnkhhp9BZHCLuqQI27Uum9Lrw4J8OWJG9aspkuRgNxL+XLOuWEJaQNhpSz +fDFXD0nf8ZRuYoCIlQMFEEHDpQytR66U54OFHAECOecD/21kJd1ZG7QXEx93DWjgCd4rKVKs +B+z2ujl31IaJFl213/Qgfjmb2W7nYmap5asnpDQ6mLIB/W64QlD4n6s5t0YPn/HJyHRwPVjL +2q/qeCYzjmGgY7n64nEbXsxln/kJy3vTMuKDm1/UKjHebM33UB/inygGWwsbro0+WfdBChym +iJUDBRBB2ZjOQpKaQa8SEEkBAYaDBACreaa3+jC6FjlhcmLEFaBCEM3B+ZjlaEPT4wothQfA +YzLmCbTBk/3+VXFYRWBOle8Wqz5KBhAmgf8e6ynskMrOLA888qLlf77/pvyOrkMDk1ly5Dje +i/kUP/krXAFzZ7djF0PwWFHjwA5mMBTTtKprkY08PoylGValN+w4k2miWYiVAwUQQkCMUnqh +VJhZG8KbAQKwHwP9FNVENlgzchctTHkPe/0MoYZD2HgZKhNph3oAwGd6GF+/Dh5gfq+11MnC +p6YtO8aca5Sux4rsP6SP8lPkVyNTpyUznuHASmwQaFE5H3n52c4nt4uVtq9sfHy1rZD+ag85 +ovy2XW2d89nb3HfLVUwSlbxjpriuDa8Wpg/5dRn9TZqIlQMFEEJ9k+Bq9pILoc7PqAECxnAE +AKNnpyX5sH8DvZm7ffRAGRW6dV81z6M7xToiYHNt/L3VGjniYkCdF5gb/VMUwrAGKEA4T69E +yAHtUqiq0FRVl4waW3z0Vb4aDdFyeAL2FHyzbh28EvT18UN4PBZjKptwntZ67NXADFPBUDI4 +q3OTzKhLztjBqYsaP7OZibHcoWTOiJUDBRBDnVByBm1WaVwuSEcBASmXA/9Wnq1gDBDv162L +oaCnZVG1bqYjxhwwyxTLRe4gszEpUWyzzCceCKMeoyf3PPjdf2W+X5rV07ZPCEDv4zizJlEx +yDNk4C8Gmnzw4yBvuCP1TlZk4TCJRqP38WN8e7Yu/FAF5J1QLMZ3nCF3ys30koz7drHdboFG +TUBZV7qA2f0HSoiVAwUQQ51Q68ELehdvEF5pAQH4VwP/cD0m0O/3l0onFPcjOQoGs/Hf/C5w +YqlRxUieZkiJHkKuREi2Ju1YH2mVhOUGFZha8c/JSbWhefiBtqGKU5JcYV+VyiAJIXuk6rrX +lZ89geDZ7a0KKJjdzc0MFjvSxa7BVaboq9tnLA+JYY0x/o46bJNAmB0IUtqb5Fh+8wiXd06I +lQMFEETXnkz6J44/ecVPuQECZlYEAMcgK+HHDzejAyIrbqZI2ffcijMpu2sUX6ySZzPeUHc9 +4Nbfm6uZxp331/FuePWpvqKm+nDRDx1imkiw11+J4QWX0i8lML7q84dJDkiAo8KGZCL6Tmid +34M/GadicmfdZkfEkToYrRD0Vm/hCst1libElZv4xjwOGF0zWjHWI9beiJUDBTBC+j+XavaS +C6HOz6gBAmcaBACxnzvtzYjl5nsAHGaLJMujvPPIOo23ABv4nDw3nR+ZZk9s0AgOzPpJPBHg +4OtVKY79xckSzo4qLGObqJfx/ps6EdTcQGncGjIej5iGZoyU1LGR/xtR7p9qCSnBcw1dqj4l +sEg/4r4csYe+RlHdvsMfmley20jdfnT4XOdaTqudNYicBBABAgAGBQJFvWfvAAoJEA6e4gkg +AtcViJ8EAKIdSy/gXl4RAd9/juZeoXl5OJSRorrx2HfGTfv9+oPEO9zZwt+zaxzOyKYLRcAt +NWY6XEtkdjA720DjKBS/X+n5AAVVh1bRAdBg29WG5Zq6GTbC2GT440uOdKHCPkIQpFvjINWA +zESUtNFOTiUKCgcquJ6zoE7UoFrKMdmyvLBtiJwEEAECAAYFAlAub44ACgkQQuY/n9Pkx1QH +PwP/ZitWBBfzbbbq/+JBOJEtXXk3Nhl+BjZRTRCdYKoH6tvaqZhOQghHW7PdH5u3QmoadmvT +nitLv5EwAeN2FymMdakeybi3WN9FBrXG8LeEK/QHnPXohlLfIfsSodr5lRD+hMnQJyZrXBtp +SYKS11Sp9CxX85sxe3xwqGqVuSABE72IogQwEQIAYgUCQj/yWVsdAEkgZm9yZ290IHRoYXQg +SSBhY3R1YWxseSBoYXZlIG5vIHdheSBvZiB2ZXJpZnlpbmcgdGhhdCB0aGF0IGtleSByZWFs +bHkgYmVsb25ncyB0byBwZ3AuY29tAAoJEDAZDowfKNiuYzcAnj1zpRXdPcNLXmqP1mpCJS6u +Z78dAKCTQsnx4l5r0EqHuEsozpW2YE4lZ4kBFQMFEEHAt7lvdFJej0q5cgECvIcH/RS7pLEb +P1AL3GyqMP6BshusjzEwWJfrQ06NSnPODLcRwbI+bOfimCnxX401D78PYqE5OEaz2xHfkMqS +gdyDCKGCfjv21AQrmifntTY3JeTx8kW9TCirOIUHqtKRp5CVofT1Stou32YJbO/dgeYMUAAQ +Yq0vacx52xQ5Y94qk1TLqIS26LYTn7r6ItSkS9Sz4+KTMJsgfCwUBnAKj0DnRcIh1lhqkL+Z +USbPF1W+r6tDy6tfObOhKWZVdtKeTmJ8iPoR2ptheEMl15tY9KPtHweUYWwdBW9YtTLvIbhD +QtLta/Ia7a+4ZDLVG6jYHiKi2aOdO3UCpVN9QUfktPK4imqJARUDBRBBwU8G3OqEx4IG8kIB +Atw5B/4y0mHWUi+i/J3EDR36OQLsXNTPU3eOYeraC/cZbMFkO0H3KaJiapN5GU08/H1ETqTA +xxFSS8uWTVHvCmf1+/GzyORn60TeumEwe3bb7XZCxybCOccU8xLwkRZNfHM8W1HT6gq3UZlD +qwoChxDNPdFcdgFMSkFe+3k1UMJz/mGcWHcjuM0nWZ62qfo+yU+WrR/qUKOp4nyH8FFe2zQJ +u8uUCjn82KDwuQIZXwj95QYDitz1obQ48PrAz6y5Jh3KWzLKGEsplHuYmuCgSFThGHyZkDiP +FLmK7abNJ7vuInX5SZInq0fndeAISjpQMGzhNXh2+nqqXY/x7+vVnZhBByk6iQEVAwUQQcFs +RBPNLh6xMvPYAQKBWggAoeO/T2fHXVELv+va+3XYvmxMRHVaNbyJErBS3SVqFK8SgaAOFMCA +sJejIT7XguxGXchu98TsNQs9X9SkpiQ8LmjCzuQaQqEJpjFZjYpMtEyRsSzpPjaJPQTWPTry +zLpIFCHooQiWcW6r/CJXaZuBFwk+xLRjFXpE/CNPhzMTR2uNLhuHwOFEZWnuFsp2BpFHwy5M +5rx0UkfJZ3XlAcvaptMz/Ja0ZGtsRFMU/Y7o+6sQtYQu7H/m2KUe76tkZBEu2FuXaywRtXad +XuaFonQqVG11ZMvnnz4cD8lpGjsjmA8HtuThBxh233GFAzV7JjViycFBzvQU/OK2r7/qvLqj +c4kBFQMFEEHBc89veFaBNbRS4QECw3QH/jEPajp6mwPb2eU3zKlr026/aSgapm3i7XnepUts +fhZIjOF0iSyKsyp+l50RwvWcplFeXTbhNsemNywQxf5LlmrzK6ffzaJMgvMd0DAAx56kqKpi +JD3pCcAO545nm7WdcEhknkyKal3WuEa7xYs2bPAi3GtNgWjOgj1InjiPPsOM+tD7juCZkVj2 +pi928Y6j5iNax78YMtIDmLzk6gDe05ZqoeJ71CJjMDr2S6g/lVPDOklJNgz9+eUF6N929kE3 +rv5E9O9Wdkr/NgwagrERdM3ONPwClh0HHM3LdnfI+OCYLt5pkftjtiYQtr2p2AJm55dIpZHu +AXqnCiDdC0S4DUyJARUDBRBBwn29kTGfx9W42B8BAhT8B/9YGHbJEpN2MxypVuJC+IU27zEW +1NU5/Z+gxT38YYJJMPcefQuecMiDAzgmgTp6WtiLt/py3Tm9mcSQQU3acPavjfSFu4XDsq83 +alGKBxhurLacGscvid1tV9F0sblyWAP6TwZwW41/4obAFLiPgq/Mgm4UVP5egzdYkH6UcNTr +977ov0xp0FCxe+lQIAMHSYrrMYsE8rNTZ6FYb8yQCU7vCltZ2r9ZjR/gF4/X4xG1pLbzS/0O +XQSsEtsbKe9RTihTJaL5A27rYnTwpRmhJIxeQHdy0mYeJzZgTlyxaV/NcTvdwonMWr9XJWXl +95wIWZMweEnxAg82cd2VaAnrX/5xiQEVAwUQQcKhX9c4IS+hYWXbAQElQQf+MXaDWIwSF5hu +GTiQgQpTmZO9+cgQy59bwc8w3K5qRyOKSxAvSx7qCATbm6pU5HLWf3sxSXv6ybd+EmX0zQkO +s6wUsl5nHCJIizfuEM7biaWTh6GaiWGz/SBla9aReE04DWPFzVwXYYQiyc+cq8S2zEU15OQb +PPDM912JlEmiiXVbAemLDrMYMosVlm3YzUkJIx8JVxQSOrU8MucReuFFY+v0UMDwNnmvWAOv +M2phAmCsV+TzHp18BoAo8A5B9mZXpy9NiHG9gv6uJGTnp4dVUrKrPXRwnskw7svWqk5HryjI +JL1q5/H424CsLK7yInTYdIA21Rgq9Q1lTXfdjWrDvokBFQMFEEHWz6B3BZ4y3VODogECmmsH +/ivhRcEoM3k5vpocawfqbrgd7gR1jFhpWRlaH/zHi9HeTGHJCkJ2UYzTYiM9ifdZDWNPxzDc +FwwsK5ct+/0PCHwJKfNAY7zcLJNQX0JhhD3I0qEdoO5Jnqe3LtLCT57SNrtQ7fNr+YHC6cOw +UL7vJccsQ0DrPKiUZk13QyPFV8u4bhxiRZC7H0K4PQzvcFrKbXLik29bnUpymOiBAsgiXGCx +YCVpfQ1dbG+gt09TRrL42LYx2yePpxNw9W9T5Mg7W86p4NLJejNM+GNPWt0ytAiYY4TbGLFQ +nAg92kkRyY9X3qSY7br2vk/UuasHKDePYGUMKC23lmEfv6rTnawPvHeJARUDBRBB2ZDhvSgA +c/+ueEgBAozQB/9+iIw6XJNA7PjSgz4yn7SaJKVy2V098gQk7k6GH+i7fsJarZj9Kn/IjDLZ +4q4szDdFyA9qybCa/D9rPd/YPbENVjWa25cvbCT5EP40gpuDLZ+5B99x6DPL9e2icCC6sdG0 +sPGE0QS9jWgw3E02MQElVLe/eoiN5o+aj0Wtw8NeGGnSYnkKpNe9JHlniKYTVN380MWQw/R0 +mnTLgu8ktYrWzPM4wmdURbwRdcTQZZrejaSCStWLp0cenbqfVPNqzAH9p8gpXvG75RHItZok +IUxMg39+nNG2SizFWqTg2Y3yXN7u2v6PrOKudqGSDMl1GyrIP6qsYTBOwcVdF4iU+cm+iQEV +AwUQQdmVlv1NDm0sZIiqAQLFaggApsuSWc9ESq+YfSsA1SHSbMygQuWc23+3wjaI56CzYft1 +4NOSkPBbt8zXYgMaEtWfHEA5DB/I1VMOpp19RwU31GwCYq4grXEIHWoauLLm2lcDlrMivchz ++HjEbjC+lY0HlenMqCRjWTmGXLRXBGWE1DmbVSJIw3ZPG3AqK5CP1IDbYZognajR9xwddhzz +1STWvDy7DIbNq0LotTmQ7J7s5qQrwynu5gv+Hw1PMKZcIMmeynCp7U8CxxedFj3O4cTlbBy+ +DZDCSHvKcW7bWUD4SRuTjpZ1GVhVy8BXEcRP0kLU4EI8GjTcaHKDYHa5S5sqsem7h6TUpHup +b20tGcESUYkBFQMFEEHZl24Lz0fPIO3gMQECOooH+QGRUyZtjWnlmUDBiLcYYSCAIRu2+LkX +3kh4e0n3wxfsl2uhVuOXnc9N1oOmHo3Sb9LGc4Dwqk8r5ZZnTOwoEO4d2Yn9ZO/uaP2uv1gY +G23B2Hrf/bKNDMsm8F5l+emC0Fl2iP+FgdnhljmIMmC1vuOppuy08cP8iTVliYEmrLj/Ut5k +9ObVJeyJOTs880qU42qb/VDHncSQynhKEH9fvdV+oDgTrCUVq1ugX5ESChMmxh1OgMOBQGh6 +OUOCfL/bbamnglImzeosy/BwgQLkMUNz5lEBhshMd46JbtK8yUj1LLR+Ik9KliY+ADVpXDuC +a3+tHjQjDS5k8NWw5J0aH8mJARUDBRBB3jUEhqM6D+NlefwBAgM0CACMGn4Yr2/MOXvUQFUs +LWFBIaprk/JJHMOOzkF7hzTt0ugD1ALu4qLidznHOCW9S6bXbrkRL5PBQJiWf21RV/wYNTfv +4R0RAChFvfFZjLIiQ8VI3LsIctbfa/0+U9jy6BPe3fPvUHnNPAKB1yeReW64ifv+FlBoeKlT +8QZuIu9O8SZAKkQSvopTMy80LPpFEbU4rhbWtkKGdFr+uFJHlxdM/DWNQOGWNwYkSsUuxnNb +Ks8Ni8kivRP8/KZdxQ4cD5TVWsyCG9hyImRg0Ftqa3sblUf/5g/pgKGz0NfLgsdwtle4mFLy +849qN5mjLIimzEvUf3Hq2AvE74wJ+Go1D/ANiQEVAwUQQigpBHgtxUjvujiBAQGD6Af/VAKz +NK2AgT6Nmi/kLhAfjBUvJICx+xb7U0GIb8JauyWgT4FF+M9H+TzAOrLPurx93TMHT5heFaMR +pb18c98bFmerA5kzjbyI/xu5D2njtF//uRmqoXlUJ55F2QTtl7AQJ/fJUTuSpGc9Ovr6dKku +0p+DqK6NI6O2b4hARhSS2EEVPeJkzl1RNS9Nrg9VYDJIG+OQLE+S7cRul4gL/Hfe3tdhWUfK +XYVEFjEfB43MpgKQlGpL0FRKo67nYjXeBL8fxhIXnYV+XhHfC3+ttIgti9/ZVc0DkETPCMU/ +B+Ptv+NTSwJ58OeSrY//3ZQ7j0hsKwRGBFhRD9CF7Yov5ndhB4kBFQMFEEIus1+GozoP42V5 +/AEC++EIAIijVEQGvRCavHVInFNj3FBUm04sjruSSPkMkAWc6g87eLZWEY8RqMbDvpcLxU/k +XGB+i/YIp7U6EzxuTHdlm7sO9QRc8+DCxXdeLsKOOgTFkuUHSwDW1bPPxjAu8gA5fR+S0jIr +6pGqIAARkRksDEVYI3vj9AgNrzQi5WYkD6tkOTdcJhQwp6HnsGtP3ZKIrjkzJjcOEEb95u22 +q8h6juMw+x17807de2vvX2U+Xn2UT35Z1gS/dfQzdZdiJneI/IM0lTrHCX4vNFGL4XZBDoRM +0pTduAFOIigaYzlnOXZzXtFd+XaZKK8wtOc6cWe1jQ03y6vsmwF+P45TAeccjsKJARUDBRBC +eQq2pjV5yboKovEBAolvCACDbV54atVnFf7+waci5Uj7auONGYAlyxX0fABWjEIqy4QoLp+T +csI4ricafs8x7kY0nGHtsNfRzOi44KYJm3oXuAnpZSB9z2SdxYcBRc4n8z/8BIXUDkuLeECn +UHHVgWe8YuLpHFZj4iZ9ZlL6wnZy/ahxcAwin/jtNPKBhkui+ArvHlmG671dGOqU+uYiB91S +FcDDpjbNAHU6P/00GtBIVllby7D4k546R5nNtSEoeA16aYnO8z9o97qLG1RGL2q1JzpQSf/Q +Nfn5eBDkTnsi3nPVPq9wOukzwLQzpohXsFGIHNFocKxSe884ARCjqc1nIXR08wlYXpvBtOfu +jRraiQEVAwUQQojhz7l4UXTQhVDxAQH5Iwf/YTpWiH0KNcsrP+LwHuS2XUu0Qsk+2ZRBRe46 +UOYTbLiUjrCkW43gDgDljkx7d2KOkn1bRkjnADEkm3CCxWchfx8BOELw4clLarOPiUl/uC7v +rrGmfTlw7mRSJO4HfD52GgJlS7OokiJ3VWpU6xTbNZ7vbHqQTirIa92sQ1itTo3ydPiQ4Z5R +pqGu/crQgQdsU99OjNnrGTeSz22eEXHxF6E13jOuLEbXJADydw7tvjN7VBfUcbe6nQ6/fNZt +sDZgCtpTThIIulUmEMQX2fs5CcWI8XJss7KeD7VR721IzlzmsnXIdejU1J+TiVZmY1Eqqs+p +nHg+wYMYmcu7Ka5XMYkBFQMFEELFXW4qxWeLJ+FZoAEClScIALfV1icg71USgTWHVuOnksBR +vyrZCeWPxFkhlLY6JY6eA0fTUcLzjr0TThm8EYFv98eRIeHX935oIOvjWATA6+nWryCjSxJU +CsTXf6EtWiEClnayxPSQyK9ck8s+XZb1crmo342GSBd3ZaCLXAMQhEKksaSQV+yUlgC3krkd +vUVs5bVHLmkgj0MXUrddPUhHC7C6QKMC6eHYs/yhHkdY4ZadKuf81AAUfBb8R/afh3HNDlKt +nwVUnRTTLII4pNba451Bd2RXkUSOaWpT0s/rTdhO5RPGZfX58Dd8/obKsJPk8mUyfuQbrfGZ +yKtxNvm+TedGW9z6iN6a0MaoIy1cUYCJARUDBRBDM02xGXKT+HtJ9VQBAq/8B/942Yjl/R/f +qxLCfcL3RhvuvGqOmCctBFBGcPB6TD+3azTBtObxbrFEAf83dphDm8vweKUHH5vkr+QsVef+ +XgB+IbPIRacR+r7+L7f9aWkY4h/i/iAM8V51QYedcrjbLGShLyk3w9htilItuqg+B56+dWuq +3JNcAhZFg+1f76wmL716crUFJKsT+HI6ovm4BR5nIPKQDX06zrIBFeXis5Ib0TfnY7kiZ8cH +yPCS93O/yfN63hbJa5UqQCzNhglTcxo9uo0DCo8atPDu9S6mMRI2qJmHXZNjFwaADVHWWnVa +QbEcGiANJOUge6Qs10OvhExNczf4o4zTKZBtGuBmYMWtiQEVAwUQQ/H6pHzUaXo2X2mDAQES +sgf9FYwYYVr0UbaVEvDD/OfCsXbDNIySOHyQ6bHSXM8MDIJKpyp/85qq5SwFbLtxFdnbpom1 +AYA+/k58jAVw9Qr3L6uPV5Kf8ReVmp5kZffN44wz8tY/RmlsGmBtA48UZALM9NJGsLNayf0k +DE+VKwLZOPqaPnLuQh4WjtdhlTmwxNgaB2fnmLYn6fpMpgp9Gxrm70eXqfT2hXB7bLhlMWD8 +NwtRSEiyPmu71Uk8kFny5kiZwrBHd8T3k2shT3tuCy2aUaUPgckb+0yQ0RidxiznJQtzGJhf +z9jjxZp6FzBO3TmYefWHviiTWzsGy9WbVDGCju5AhYz7wP+nuciul5i4/IkBFQMFEEPx+wO7 +EOsTGtkMgQECYCUIANHDubya3DpuK3ny3Ubt5P82mIRX7kQubijg2EEIkh3JqiA5ci5nHadg ++6hYGizH/uR7oeYB/c/ci4j1GikNq9wNT4Wn/BbctMtnypoqiejC7usZRAxrSxHcgCMxDJJl +10CTV3EXdLzCCH7MXXyvneiSWzSZgHfww3E+LhS7cFwLowI6jJUk9V1K6040jPJg/SUI8Ky4 +Na53SmQmz5O9+ZN67k4KBZpfE/Ui6dNOYX7I7HpmMUegmpocp9xaEHSGc8rXhLr/QldftVFc +cKYrw1qjy4tPLjRifwFgQrlNHU97Xq16wySgLmiQSW/vOz7XPfibyrlNfghUOLvqT8rQX+KJ +ARUDBRBGGpONp+Eawzal+GkBAnmhB/0c1SAOUINAnIEttP0BI7L876Wae1215Iiqncp/Jugd +XmPfis14lMmYZH6SuWoV4TTzJ54+Iue4dA539CwdUw0gC7iT7KBmVN0ajtA7AcW0FfDPOcIf +hd/pTj8Kwew/LdlF5J2PAQEpFJ2/qK1D0cP7mw5THExf+H0tcYbiCA081xapdVD9jfCRlFUG +A0cNgUM1W6TkFCfz50LuBmtQ1kS5b1fX+OPdPK8DUUnXGefLqYyuqRZzBg2N9THUC6kFxuZ1 +ubWaOArm0TAuw/f7RiiVYysk7z+nfD9LfGiC91GoWot2rM8UZxexQBnDatL2feyWgLgBTNmF +aipSNzc/JkHmiQEVAwUQRsBySgwtq626QKxjAQLB8Qf/QSAzqdjtQs6YJCDGmK0WU82St8Py +b/C3XGZiEKtRa8ju7DYXbNbd0bEECcLKQdTALaQ3dxS3hSg/5XYlKf17KhaD1R3j0BEwg9Es +XwGaW18vQ1/tDjt9yVjDrGZRsDoUItjBlUp+nUVlFVLREpTCH5Vdjr0UV1rJrqw0SOROJznX +XDUPXYTknidha7bw+eDNFdqX1NfcZq+4rvmuOtUQwSCN5Hfy40FaqUxlnWugLQqQj1hSLNDb +6F/So1F6AxNJI1Y2ceu+WRqw0HzS2r6l9ZV6DcqQEeIF3jBV7nenxOktp5NMMHxyKEkLCPG5 +r+H4OBXNUnVH72+cgK//pAOLoIkBFQMFEEeOCvFGa2l4mGdspAECSakH/3rYqvNG6H1jIodG +asjDw6qyGWwOnTJF90Y9N/ght8ilYNBei0v7lXDZaKhn8Y5idY0ZjSc0t0K54Li4EWtr5SvL +R41Dam/4qqiqUg+HaSCl9tSPFeX/yIoXsnXVJ0GfCtrIgusaIi2hegcYltG1PpnwshhDba28 +JY7909W/K47gkPb6NJb+ttva1rMIFiQqjXIezawtaExwao6aRNE4M2QYR0UnLeei2/Zk3VCs +WP23EjV6cIe0D9vFfQ7MKzuG4T2DPEa8fXbT64jv5IPTZ4OicxiUA9ltv2wKc+hIW9dHiSoN +LiRPhMAiLhiwsVf8HkUR1iMzc2WGQBm614V2utaJARsEEAECAAYFAk4CwTsACgkQDTZL/TSJ +p4d/bQf4nx+iBOTwdnbIMvCx3yZ27xYjcE9ovBG1fUMn3qYbnJWbjueAzuAZas3VshuaPkwj +vEarRk5NOVKbJyzu8H6izbH6ppawC9UuPCw9+ot/I8v/dahQS7upESiyQUQdDJbwO3Krj+kH +A4JqMJjpnYd4WfEKwvsrcz1jmm4SO6T07DQznzh9trZbHOTfTczhCtSy7HUJ2o7WJXHP2YDT +4zmsi3Mu3LemCVV6RrB2Cghorn8ZGL89dwVBCM0WtwSvmNbRWIPOoUsVppFvhBaV7xv5Vf+R +fMZLDnkgZrd7fBuMqEFcfATTtr7uU/IYFvZAdKo2URjUkUqHYIPK5bMnOYR1iQEbBBABAgAG +BQJOAsE7AAoJEA02S/00iaeHf20H+J8fogTk8HZ2yDLwsd8mdu8WI3BPaLwRtX1DJ96mG5yV +m47ngM7gGWrN1bIbmj5MI7xGq0ZOTTlSmycs7vB+os2x+qaWsAvVLjwsPfqLfyPL/3WoUEu7 +qREoskFEHQyW8Dtyq4/pBwOCajCY6Z2HeFnxCsL7K3M9Y5puEjuk9Ow0M584fba2Wxzk303M +4QrUsux1CdqO1iVxz9mA0+M5rItzLty3pglVekawdgoIaK5/GXVJMuhmpSGDnikqE9PKA7OF +928mNzjofHG+910CcuytkXzGSw55IGa3e3wbjKhBXHwE07a+7lPyGBb2QHSqNlEY1JFKh2CD +yuWzJzmEdYkBHAQQAQIABgUCQm8txAAKCRBTGFKa9WrpPTNgB/9KKLtEaQvcHc82JNYdKBD1 +EaHIzJGAEKUhn1oKLs0B0qCHfj85Pa+UtcluO23g5di88XSOyeIgDv04/ENnfzKHUnI8fvYi +TnNbFOYnPXxwbbHVIOnqphHKavtguL2SUZkccqMUWIPAfzNoKouArXC0TzfLebu26fYdsgmr +kBm6B3qKLVjbCG7SGrl/ZtjCJmkFOT14je6xJlR/LrR3xCkK28950x13K3quErvqEtlm+vPc +brx/xtYgZ37saAQ8/wGkbpv7/6oKPRyDkVnE/x0vJh2IkqUXPTb9jUF7KrEzpmRe12kzRubi +gZtM/y/VJjbyhV/IQhbl3ADHKDuvR697iQEcBBABAgAGBQJDoSQTAAoJECnZLSEortaNEGYI +AJ24tMN3lgQ3TUIFY8GDn3GrVnBaWJPMhg/kEXBL5gKt56MxEmoy/GKZBtXAGE/bxihmZNFU +naLEm4R3mqSknp3sFURV+OqZPNJWHp7+oxV0KUt7DOt+HJeKKiAarZ+jFgnjZ7OI9KtFTAmZ +J9DsOtPKE2M+qetxJcBD4usd7FsJOIIyLlIzrMo+Y6Gggv6hR6y4mpoGCsfNj/6XtMRTlP8x +Y3oIMKuKvSHMOAPl1tOpZkrVx+yUZB6+uCo42hSbe1eprD0vDoRNGOGs63OJJODwNSBAsJsz +UsVNLSnNFp0lVscgo6TFXK7hc8Sxrpzx1uqx1lLIF+hGqeIs/Mo7b6mJARwEEAECAAYFAkOh +JBMACgkQKdktISiu1o0QZggAnbi0w3eWBDdNQgVjwYOfcatWcFpYk8yGD+QRcEvmAq3nozES +1hNfdTVVZLhkDRkXwa7Z/2meXn222hDnLOCTtfjUYeP46pk80lYenv6jFXQpS3sM634cl4oq +IBqtn6MWCeNns4j0q0VMCZkn0Ow608oTYz6p63ElwEPi6x3sWwk4gjIuUjOsyj5joaCC/qFH +rLiamgYKx82P/pe0xFOU/zFjeggwq4q9Icw4A+XW06lmStXH7JRkHr64KjjaFJt7V6msPS8O +hE0Y4azrc4kk4PA1IECwmzNSxU0tKc0WnSVWxyCjpMVcruFzxLGunPHW6rHWUsgX6Eap4iz8 +yjtvqYkBHAQQAQIABgUCRBfk6wAKCRDkLbL89SJutWoAB/9XqfRzlVrThTvwjDroEpgDZSj5 +oKeHJWNWP77wDCAzwja2PxkK8eUCVbjMeRqgIVHMHzsdN96SIQ/BVVL6SBb7KP3uHQvdDCeH +vOR7cfhJEsdQ7zIs5gKo/9Njqy+fi0Fgv6G/E5+L9s/UZeBqGlPda4KhxK5f8pDpSQYHlpgW +9tcDjgUTcYmGjg/nWcipQtOzFZMP1sh5u96/DRzBZoHH5DcRrCaNiveSBBoRHHI1pUogfvCu +t6Ov7vOekDrXBHwmkqPUbXyFeT2wi38K2oFNrnv5lT0B6UCb49b00iRu/l+kjXWCrw1Nm33Y +I0BxKh2UM7QyvuVraNSdCFSMOepjiQEcBBABAgAGBQJEgK8yAAoJECZJc7D9BKMmecUH/0G0 +ds0Vwed71QkdxG1i3gyBDz7p3HN0obB17ICl1+hCnpB8TQePH3rSrF6rlxMEzN56om40ZvdC +yZ8HU3TUBY1cIBcmJDvSc+fbsLy4uzuCjkLikXMRA51CjziidD0jLPaABUazuXh+8pudLeW7 +E4IORIOdnnvkPYG4Lyd3IeAqu+NEtyOMK0c4BLX/Fohk20YddtZLBieEidfkxQ3mwE6vR0Y6 +x5xXLrxWq0MRgy0U7EBgYc31ybev06lyFLmuGkwS7Ef9XaHBgg+WUm+F+8UiBJ4Iu+k/mRc1 +Ql6bo7m8mxxWbqt5rcXqYG8zXyjo2l/FpNxtMsM804Mmgla5oGSJARwEEAECAAYFAkXDr2oA +CgkQ393l/0mdhI0xSQf/Uc0Yw0vFnqVwNM/eShNLqKJYSGjNGU4DHBgI/MojyN1yMPm82FvE +PNjSMymVDcNTw4Uqn6oDkq+BiNBalBTJQoXlQL6dy+WWLEBWvntUtZH7zcpaPWH/bxilT3p8 +GU2dCUeTcFLNlg1Uy5l9zChjbXXpXiHlx3jlkw384+nNmQg7jLa2w6oG1YNSEVA66Pq6O8k1 +mSMcLk7eZ2DD6iIdVeA1QVoSH+s8f1krIcLp3goDG/CbHPISkGElYZ+nY9gA23BxFpzNQqNg +hJd4fzUfp7UP63DagGo39lRzjo62DjVTnqy1991sCc32WM9Xn84z3wZ7Uw1uLlx4LH8rNoKQ +0okBHAQQAQIABgUCRhKCAAAKCRCDz8ieYyx0v859B/90URm8shWHTHavtDqeWi2dYhvRG8gG +Pxeh/CLzHvsyRI4uriri5yaebuk7zy9Pp01EaBz2mgGRP3pfegU5QiPujJf6EyDI1Kk5qtaL +fdnAdpAIA/YumG1PnNxFyqDsB5cMw0tYh7wLUzH+oKrAMUZ19eYUpcMzSqJk+Do0cJ54ntGN +lBH11i/uiVRbL/KgPJMLHQMiosiv6XzjrrSpturILXw7B9UD9niXqkWyyxbKKeLAXJfRfWQY +E3ZT7XCou+ShGEAj/envYapMl+w4bFBJgDBLVa2Bl4U2Zg4jJtVaUpe0G14Hob+kz5GppN0I +aKCockbDoGOTJcRfjtyVW3VciQEcBBABAgAGBQJGp/oYAAoJEDQuCzwIs/YJTwgH/inm3WwV +KdXmIPPs9xGt5a7RxodYy0v59OxNfVW+JK3IkMkZa672eQZArxIDVv99WQh6TXwFjKY5fpoy +ikDYO1I3wW8qOeu4x9IFVUcvpE7Z021Evjg+4ERI0EoI+fBs0z78A7K6ayI5e6g0xDtWT+Uj +wIiZICnPR8n3XB343PYVtQBK/fXjY6jYHhaVj4C5L4UKFh7+5udv7WFu+OZpwjingQ4vBmAB +KLTjnzN/DZ6T7mxrI/XJg3mrBHUsorKcIBJr/2pWbXJCIUe86FX5hPrf3DQKI83mPRvFkmvx +pQWRuf2wBnr/xwBx4ZvWjtM39+YQFqctydN9xEt9Zj15a6eJARwEEAECAAYFAkdNyDIACgkQ +/L9Sqis+Yjd2FggAjQmlnSV71OJk4y3fxnz6I5jY+ztU8HOME39iSAN3Jd27jHprpz67wm10 +Zd0PiuAGXx1DRRVakIAJnI8kcV2IMHm1eDYAeilpQXUqsPHPT3KmBKx2NZ09IfZ7KUhZAaOR +kSimuSj68tkj0vPKLWCY4S2AIvG7MhLNh01c2zn67/8mBekdOnv6JpQrNj5+egIwPU8nU9A0 +nTMQCgZpreSXM9WjfsuhspFdvyHMBxropRurM7ZWdNSAcwXS6obQ/BgXPfacWzFqxItcxkpG +mz28BTMI+fvlaA4eZq9udn4Us3hHZ6DvL5w7Y2Vx+xKG4VoZPnpgnK8ybq5CAdbpWyvP2IkB +HAQQAQIABgUCSDJsbQAKCRAEAaogRtOX/3wDCACRnLd2kNkZqLDPq0xIj4Uvbn+sFr62axW9 +SBomsCnF8sYxZJhig9LjtBEgm0i17FRMsH6YuriuOVk0QJ6EaAr22OGvzd/Q0BQ/8oMAr3k+ +YQUeK7go5zn3mRArDcOirWe+35YbpWQLip1pN3YZXW4GU5cfIG4sfM+8p1I1aTF51Ap6cFEI +w7J4vrvtF3SgOmxfdTpoNu6+tlVFO5fpx+Ix4qG8UOBWPXP3KgyF0/4d2ob4CxZtZb+Js0g4 +xFupNN2U5L9hBYBnrZ14t3wmJiyk5/YnvINAeZ5wpL+DuK+nhPkq0ZVxdacDE9ctJIAqlSmh +1eHTZ8jQ64BTKRCVlAHriQEcBBABAgAGBQJIvn/VAAoJEOnF3QKdJiZtszIIALEG5F/8JkRQ ++mm64S54D3WqP+ZHR5Ajd6jgvY8JOAE67rzITr8s1xcXW4OC7AeFtmaljtAqot37kjs6lHnJ +/JiRxr9CmYdzv9FpkkzFhhhUTllWiAHY8JWpPKfaQRBmOl1fMQFSJ2np0oADCcxfLz2jYtx9 +8z/VkR7G68SNk+y3DnLMc4Pc+YPUrZtgfOB7iXhxnDmpOFkRPokzbXbiu2e3cQxAc5zga8A/ +AYjNJNN7NjiYeOEx2SKJaiGWOvdifv+0fwp1Yd/wJ50uT9lSU724ZX7Mt8iCoEtKXZrQUKk8 +vcJjPOQo07RJLHiwNphzN36QYvdifUyutQB4jD6MGHiJARwEEAECAAYFAkjL7ngACgkQOnjr +TPFCDo6csQf/YKIs5k1i9KycRDqVZd4XLd3K2AD4EsyGtTGsC+7TA7VRirlJ0Tu15y15OICu +9sRxSnGeLprAmp8h4g707k8F2CeQPgQuv9n1LfwLVyZsBlvEDgIlR6LubeKiRXZmN3dzsybk +AfKrGKXUTOkXiPF9tX3ddGFIdYKlX49TktksAnG6gYDSFwYsLIUNZka9GnOHtcTMq7skPSVT +67TLyB5zC4xO/8QmZLof73sIT5quPSl9H35d7huAfZFb7ymg2ALcLAUu7y43jS8hCm6984Q/ +INT0qYiRM842MDV2cfofJaEhG6GNaYA3PLaJ/zlTCLrrEPwYERl4weK2Xq3xA1I0sokBHAQQ +AQIABgUCSTVKswAKCRBRkI8FP/0GSaZQB/oD0ZrOwJh/e9xecdPEdtsNYRUAYT1ahwFBxNbA +FXLjHIiqGMmHdCvZjlMNbTHc10A8f94uu4FQN16iAb9UHBF8+lO/IqE+LQNnVeyRi5Eb1Q6r +7dVB/Ic9ruudei8AmmRQF5y5RLAFysl20jphTsjewPvMy1sJQniAg4ahzY07VMpAcVJsUehF +E3q9vUScSsSkJI6imgflPQgfQgoQPsuKcbSr1bwjMDj8ewh5mFIR3Ru9yDW3sNXVNG2j6OO3 +CNdL86Dn06xBeT12vA+fnICAi3WxpINvbB0EXyk4wxSRKP3gEX4rIlbSkM+3Mlwl1/mOrmWh +GmJFbHfO3xI0E/1biQEcBBABAgAGBQJJYeQ0AAoJEFyl5OKa21WS0OgIAKLZ5QWGLwz1YN2Z +bBCyWknIdqf+0i8eS4bCjbWGSVFdbFoy+NypnFVTqqhi49BJo2hnQOpImyellJLIn04RDgEe +E6esIt3ZW8tbNSCREB+nlGZ/8wnsqKjLuNdDIJyP3uu2fC1WdaklYnjc+brsYN8HEezRYf1f +JhVxVD3NbgoIbABpeveeb83Ecq5oPGnaOCZ9b/NP0mUNlEKwmOF5YWfltpxw7mcrrDB1IJgS +DkEWHgCUTNoPfaPevBTQv7DqgDvvA0lKqcuU8KOIAzTQd29xllvakBfECT+6AXgiA5UJDwJQ +yiMwGoIJPpcXAmLx8NR05bBWlEUMu3xH6JaAgGaJARwEEAECAAYFAkm7GEwACgkQjBSFwK87 +aXTYtggApjSl0++KDiOdXo+FIPYsu9AnOzT2P1f29RbL4qfgNqIs3a1j9cBzQkN5RYT+zs2P +C6y10u15Q0hL0GT8jfunJaxB65WhYxCDdzypFzekKAwsvYcHKjwx0HktEZ4EnIzWgwqEG1MU +Hfki7CkokUZZy0VqYaNk9wA+UpnMeQM2ZyrNdQ4/eBKDobakiV6KGR5FI+gDdoO3qSOOZgJN +XxTwrh/3OXY336aE1chocQ76Kx0JR+I7A4My+FeuBobYqLVIwR8l4UKT4skh27XzkCbrqI4Q +ihQpHj5P7+X21o+tcwhz0efJkibcPPE/DAyMDVPkZRSvlRB5XUz8sdt1+Uxhw4kBHAQQAQIA +BgUCSb0WCgAKCRBN6N+mun8mrs9sB/9CRV6RBpGap7UU+503HhYbsHda0FXQNdfxlcj7yzH5 +H8sGbYnEaStGO1HwVhYgq8xtEfPMZ5CcA93TWv1RXmIcyUU0TXV2W/+0/9jHmPmOgghLnoj3 +MWku0XgwubfY9J4a86pko+8rH4SsKVZeQ6RkrqeyeYlD2R2MT9KlRqTzRQwB6TGDXCXiGTHE +ok90G2oWYCy8LFFDkuqhTj0vTcLqIWvKxFs2/GjsftRjcTT+ZoKMsLb+CuC0NuwEHlfrBT1p +3709oM8Z0lj4j9t3W73az72dSW4KAdRlgbZJbf8GJIvHjDfW/+8VmDldtfhoZh38FiI0DV00 +kixOf4x6bTemiQEcBBABAgAGBQJKjy2pAAoJEJHK+B1kakmRNIUH/3WvCRHVMtYAkNDdHZcA +xU5UDKDEpUYE94cFKqp84WaMMEbn965oVHyAV+lk1QI8wp5iUOAVkHCTEc8YpxdYyoXEVx2s +dK9G0gnTNlfaDE/4EDNEeuZuP/xgWV22+OnOU8pDpnOeRWUUM0xjSXqzg3mHcmvDOHHnOwdw +1KC0bQRDuy+bVLzrAs4r8rPTDsxSKYw0xtMsDVOjVdHjulfMByxVmkbroX1DlJ4LNCf0HT8a +xvKowfBU9Nu7Pq/aw35Wj1c2e4gKWxUFnY3mPp6P72EL+39rhXGdzYbd0IG7wksw7nDA9CiA +s6EOlrKG0A2ziXeLR6A5mqy+dcKNlGLrwlSJARwEEAECAAYFAkqbN8wACgkQFp6637aC+/tU +Ogf/aVsGaE8PvFVvUOeMzdZzpc08AH7eUObsnWaUJE4AoOAwntmOEoL3nPfmPSYMfk2zdb/l +acvp8oY9rjhTMZzLHZjYlzwCgu7XaduKrRoeVdrebdliC8li8lblHxUNspOA+esGC9y42FC3 +44EQnWunfEJqV3vW3WXYdf4dMFzBjvSgstmKQkgpYfMFSF32JQGtGkkBwftym7qwY8LaQnhU +V8yx4PJoNJUu2KMIXtmpQL+QLIF0F1e2yxPTyUdnuzHaAKd5KuYKAYmcJpndhbLxSYpjHLQ6 +b04DjV0wgIJHkI3OtBoGL9njPfz6JaAZmCs3oP+0OorUxo3qwVtf+0rOsokBHAQQAQIABgUC +Sps42AAKCRBs3DM0v/5/3mTmB/sGJLaQv5UdCat3eW58DMvMUSXpXTru6cCoK+8Ai83Vjl3n +iETXjMFE4uwHHyjbT77udK4kstOgT58uLoMboMEAIgagk1eUFmfTFS+TFeb9lRyEfeP/eRFI +QzaZtXqsy4nEKrSbrBmfyfV6zrkTr5p5qhJsujfO4mEaVQAmCwENdvLwPupD1P/bDE464WMp +HGWwhy0SfgqR5f2ufbS2eO3EAv+PACK4FDkaAClaETbSWDIylIIeTuAqic676hXvz2fO++XD +p+ihmAN6JAj3y/ebSWI2PcE0IWS4YPI/xuITNq+S2oa0d7b69+boWtZ/QbWXCEwwlEDSuf+5 +NGREgp8xiQEcBBABAgAGBQJKmz2uAAoJEAf1UvnxFyrrEFwH/Rs/w1l/tUmBkaZ3WVqAoyPa +iHjqJgdgE8/jVh9cNgZJwmAoHkdrAsDzBH4mj+P2TJY8FqCjOMnY6y+FzOZKRXEb9zjPzTiz +XrwVRLfYXhVZLUZrLrV3RxaFAJt/ozqm0jILzhfPMC1BBf/mpj/F8M97KcaoF2ooBMx89Xbj +3xWVLrXfa7//cSRguGCEV5sF/aMWYcLAq6JAdWYras6PdqgK0H/ZcSATT07h+CoUjOUASGrn +01kfXKdfWz0AuL8afQPj0SYexP8072SQPVcXQwhrow3t5jEDgJ9ai4JX4oo24glLKLuSadNr +/UHExfF9yhTsaY4iu/dN9rHt9sGCbryJARwEEAECAAYFAkqbPhMACgkQHt3DFXKyB7k0rAf9 +GpQU1xyhOSGUM1/fzmqTmAEb6mmDdT9x/9hF7dvREPDvE6VCHHj9Xf0cb8vkP40z3BURLH5r +1YDeGCARpxDDLZcn7r2UlhQkrcGNUpgiNNdKMG6y1Qfsf4LlpdjFomi+ErmY9sveidfGsLit +jGtijuHuXkE+N0NYarwXzqL7/Z1bTKajJ8f2zkDlZYE6YdzYIHCgWZhSTMZXhvK9ei3Ob3Ge +YQ5zY0lgJXAobXB2PFRjEvfNzH4kcAyHkHdAioEOByqEVfKrolt9dNVYLyMGVoIaIXb0RwKS +fVfzTVfJqurnGq5Y1B48Kvsn63OjJfXt7LhD8kYq0SzFqe5ki78j8okBHAQQAQIABgUCSsjr +VAAKCRBsMIg8qaUqeCCBB/4j1+Cg50D3LGmFmDkw8ECjd+ici0NH5zpslfjGmHzcfV9Rum7P +H5lxrfpHrz4/bvKmvIzzraNw1pdkzYRXtMdrboc6a2f7Mkh+/XaPutHhDGudM90uOUR0clBb +TwnSkMD8oI0YPrXCr++PtquVSkaN5KUAQokRtXYOcJVjvu3lHjVqcGKurSckpguzEmFMHHAZ +f51cQAWKN5TV2vvH+IR+K3oIzwEJNz04EhuD0/GRvpiMlKjIQ3aa8FTGhQ3h0TP+UIIjORdQ +dTiKQfppODIjg51vi9RPBWDsiZyrOLuE7xuE+B7rXl91OmarAtCYHdFnT4Gn3s2+M2aB8uIT +UiW5iQEcBBABAgAGBQJK57RnAAoJECFwHpJcEbGwPfIH/1Q/Bb+MiAmzzDVERVLpWQgpzjzR +MwEuLnz4V278qujEWiYwLkO7hXYklfkbgxx0Yr4XH6OUXIOQXas00J/lh3KzFclmCwTweNY3 +rbWxqTF2knd2lCpZ35/IZltCozSuSVLFoDgIBtbaGNp810j8nTJVuPp8/lZV3KAJSro9gW9o +tTF4J86USo7mnsH76OLujlQqye7lGoohvBSsA9uI6uNemjRA/OPhJbKSgv/IGlTTLOJROEvQ +FBFQ2KSjeIHVxGhvZUMXmgg9mzgdZ8m8vSEbokAxARc53e+jBPWq/XwZ1U3z8jpzqx7h1oVu +NVIMeu9+uSHD2Cr/sN3QvoZ5TxeJARwEEAECAAYFAksI7zcACgkQxU2iX9uLln6Zwgf/Upnk +XoicBHJKF56B4hM0gLY9iqiL1Y5amJepn57XA+eXZI55vXdXrGBWY2aSdgNB1Rt47piINEu8 +WZg0cbRuQa/5wHPIXokT2AAmEpNQLy42vz2987sBSh6nawwjOIfa1CmVOExOMWL5tZXDgO30 +XxbK4RN/2sES6TJ6NgCeXKYgtjvLljWB/zQ18wMHhAnF0sH09AfJmYukvJHLy0jFKkmMhdWC +d0557NwUmz1Tm6GzWEPeK4Rm8Lr0XkhgfnjJtw9LaqkQ9B8Bkwusg7GXAmlcEjs41YhZBsvC +ZORliOFH1wihD5EwPzpAG+TP+uKaXr23lmd050YpfLu8pje7ZYkBHAQQAQIABgUCSwkPcwAK +CRDHeSvxFJaGkWfhB/9peXpEKg2fQjDStA29ivmUid0c6zmgfV68X3u9rWvSjWHCsbW4wCkH +khR/xAZR5u1/a7xkzwRVIpnBafiQZmszoq8yDOWWSG92EusxeNijNHfw4CR80eCF1Yg2LuqK +3kvkoVh39YiGvgWArJr4N6OpGOPba8VSLU0Y2VisgSFSR82qEaoHkTTY3rd5z4iCJgnQ+xpT +6njk0U+Uoi41sN099OnL3NPi1keIK9yYsFExjHo6ll/Kre+6rkR4KI8ZxYblChkRzE5Pjx5T +E2kHGkIL1ydba1+puOaZJd0CPgRw2FjsDEwyuoWFiR/iuXqfFR77KlEcf185PFpsmtBjFoB4 +iQEcBBABAgAGBQJLEuB3AAoJEBWI0NIWdnDTAuIH/0NYOg2rHHZAVn32ZXhcRr/9FZfEdG5L +cwBFWSOFHhcL0H3/GrzZzhWXXuO7VYBsoBkzjnkARHMW5HQFQhQY8B6KhN7PpXVZ7GtKYf9L +m5+NLUyxER5rxjU9hrbi4XLRCO32nDu5Iuf9kyH1MjY/iqJ3gh4pIqigyqYZZH1yjnhOrrtg +JyABtfPHxeP9/PKj1BXjzJzdUR3zsZnO2K4ASRTUZalPylHCY4YOTa2kjfVmvrp/94DYsXLo +WErSt1vIB0VZVpgpXsL9XBx0IFEJTdH7MC6voW5tv1N9h3pCtk0HYN8k3CDkpzZhSiXwoGpl +7W3wqm68Y34g+diNLaKBSAKJARwEEAECAAYFAkslHsIACgkQVLP+2sl+x7V2NQf+IfUdmkGn +OmccP4YQm0OtBKYyfVF0wFqWc7xU/1i8NjfJyr3e/Sbb+KE3hgyV+bsqCfKI1ChRrLKtg2V7 +A8dxu1YwWLrzd3Kg52fSNestsoONQGOUVAny3AyOPa8d0FJ2Tuerh+8Ci+PShPMzvkaWPws9 +F0HbTEQIecm5IwxS14CzEEjDDICoNOik60XbDnJH9uvi9bSwBL7dhHX+EioDcmtJMVc+FVY+ +E/8ADih+3VeeaHghNEvETuchGmMqOSAONaxk7iEs9RUDfpM+DHwdKJSvqcOiW8yJHYwlZOBK +4Q1UropwMlwoh2AObMj+1TXBcYyKZlKVUajJ8BblGCb2W4kBHAQQAQIABgUCSywGMQAKCRBm +JAozLe73BCZoB/45u5ZB6Ka3OmFWz8pvvOervykv4or4BfGuU1pWiCJ/8+LTjH+sW7tL/4vR +kzqal4BF9qLJ9XnAlEwLK3kgJ/ZP7ZhDt2Lc8fhwu8GMzaXbRffwyXlC9U2w0CE57XuulLWg +ezxeVdItotg1HXXnuHegXENm5MmMZLQO0GU5Die+yeX2f6/Fc5QQfsv7mpZ6AwhmF3Fkxwm2 +WloNd3ifHx6nA+W1N7ikrtTVk6QSB1niEMneNYgOqykk/l34OBZGepqBROJQWfEbXaYtYAlu +J3xdsY7FBwljT+xc9TmxIn/hZI/0o4DmJTxMtmlBJGpzokMgyPsBVA8YQTLaX9uMIEVGiQEc +BBABAgAGBQJLNpFDAAoJEPgN9wItixQGfAgIAKX9Sis/e/d/lCDp6Y4I+jqKxwmnrIcQgCYr +cdTwC22PcAAPYmKTmoCpvUQ3x2d5JML961uWLC8Vg1TLQ4/16c5gWKGAgyIUBi8A+1vgWb7v +9s/HHsFlt910L2JGndrKb87QM+91sZVeS6ozYXto5cghTw7qXwqZdPbDBj24BROgGWB+tldZ +6fKdz99Tkvio0ekxTD0G+jRdbpHuOizzAb+6K6F0vACrThhCPJ8r7w5pCC/KTKMEW7dGQft6 +1+7xUEKICq9fFVklahAynQTBTFwnagT76BML89YXIZs0nw47RwLvE5+kRAoHW3hAnIvxK9Al +pywohF5VScbUncC8yb+JARwEEAECAAYFAktAjhQACgkQFdq5JRU4Q12JmQf/UB3QT7V8yl7p +9OMCN/v8sUmFZRzunO8mhCe+/5QqSYmcdEO6N0cN3d0qnvKp9D6gbsozlhXXJtxT/lcyLc8O +QwiCcyLrF3Iu/D4MkB/mj4ln3I7qnx3SpbSX2HBGW8062AMGQl53VOK8IyhquMr4Eum/HCsi +rLc+gHKSjV+pqAuRjQC1eYVBa7K/UqFCxiO7nnW2r+Tcpk7E6JXNUUe659Yq9C/PzkuBQccT +/wjJlAN1z7W9rBR+VtEZ6YChanwIxwezWFujebUfwXz+beVCtVRF3+eTr254wu1aL5nt5Gv7 +mNSAsAhPORizjiMm5KlDEvdMBXpIgvrz1jo7OGKsa4kBHAQQAQIABgUCS1ru/wAKCRD623Ps +3UYSIYSzB/4lmuiyzqx4sZCwpFTu+7sXKFfkhv5/8uB3BR5kL0N4n3CIZL6EYnK/G0o1Vtjo +Fu5RtT1L5OuBMZUAgS7AS53vUSZVFvp6iqZsa3wO5cJdxzp8vV29gvvTYI3r0b3uZxa0w0Vo +YvPIe2B5ni+G0rfX6ODvwHdjID8GyweljXWWnH/1zujIOcJUV2BkZiwKViLSUleC42+8fHAx +5W0UzeipUiYeYsHWFPPCIdl6pyPekJ06RbqRirK0ZKjS+z7XKnhJBAnGHrYjNmaNaD81qLFz +RuyFKei0SN/RX7A83lAzCkidSyefjpTynNzLpn10vUv10QmPmUZWEeEXN33rwWcSiQEcBBAB +AgAGBQJLdCZ4AAoJEH49EF04a3+yBggIALIbpMo9DvbyludM7dnWZjFrjmv1LgyFrw6Sb2bX +ek+aGZIAbjQIMz6bQissOLx986q0QcwC3bMZK/UptoFOws3NzOr715+yr7MXeD3ZSSz6GJzQ +AlfN3pnL/KJSNBIfN1S2QNhu5P32f6fbRzV0qlUl+8bbXu79CjYtn1Eub/pc0p6h1P9NH1Z2 +mOSB67pmPx6iPJTB2Wmcn7zTjUQV6yxFpfIQ35HHAgp7NkE0Ud3jxR6ZHT2Og0uMTUGrLl8U +P9nlwqW1JyD6fSdm/md6byC+du20jVU4HaPjHPH6w4hLH0fy3p2lb5K9V90EfpKERpwaNPV4 +IwIfmsBVLYFixq6JARwEEAECAAYFAkupOMoACgkQgo2EZGQSVELN/wf+IwSKGDtUuXL3lPKJ +IXDZRlqYWbL8DrVUwOjpFz9/7WEO3zn+d55OLDfDxDTajXcTuuxXrUsjtum0EgaWznmySebY +6MPcI895hd1wbF4uehGhzL1/kQc82cVzMMtfS8jfWXaPxOWNePSmtkpBln2/P07dieYLldGo +SJJ8OrXEMI11QspvLVv88x+x0ODizWeuYJumGU931cDSPGzfD9h9N0US6FsK4pft9RwgJ0E4 +1bTlhSmXBhH5d3bu3ShNoLK5AnSBfPnhIGONm6knym63KOMc0SXKocY+vQromM/4NXTptB49 +no6vXeIFyxvmfuGs+O9+XlfuKXOxqDW8rydM/YkBHAQQAQIABgUCS7yyRQAKCRA2955DH/tP +YuPuB/4n3fRx/d8eSK4jfCZ6T+HX/ThgJmMDe99DoZmHeMuDiROvDctbcJvR5qqUp7lMsF8c +B19YDznbTMCyKvH5ZsdEpREzp8LFnSzcYtIXvu5gjNwCC/8gMIyArcJxYwG/2UQxuUG/zZD7 +RQ8Pl92jHEb4Cz+fP/mKAtcu2nXPxd3C6NCLdyqhbXJCSuwmMSzbl3K08pvWmBB1wkLyeLDI +3Yr41iFiskB3JGzrvYUi98AVAspd8IeyD/fiteP7MqY300HfOrnN3ApPwFHdVHpuQKWLytvX +bA3AWawmDZlbRvoY4f08mseX+iA3gh8iUWr0CEODMHS4G1jN/cJKzqbdx9+DiQEcBBABAgAG +BQJL3VvfAAoJEMGin57KPrf8xgwH/ixlPhhg5hOjTMBuW6qHScL0z/UPtx4o0Q5f6rP+XIsM +UFNa0895HAYZk2HDJBiSDBv53S3faEhujVVvAzXmyFDhbudZunAOoMkOYMkTVu7oNZQCBIJF +bE7nN/eEfgyCsAQSk+xgr+q0D3OefXfLiRgC44xCSy+nFh0c420ws0GO+jd0a5m8KtxD57RH +i76w2RFu9Hb6R+Mf6WicWahIM2kM1JgYl6IyS7HJ1Yklrkj3U9VTb5E0sI0xQbw+pOzfXaDI +cmBPHub8cgyztN1jgVSvw8EV5WXnvSpOVgca85CarcbgnYZ7BHZVt6kNFaEx6WRkunz6BlFi +3ZFN+piGtJqJARwEEAECAAYFAkviYQcACgkQcTEsuehR0guqvgf9HPY5aNbdIEBZz/r1Nl3p +LJRpCOAFAKFLeyXoxUi6B0YGdm/ozYNLK6CXCD1P16cS/gjJPN3Wp9VqYoGAqvWr6RVYWdNL +t6xEixOv1SbTv0OmUyU0PbRU03Mg06r8+QrTec6JP7i1JgLguhDvAF/9uisjO5fL6LPzCmf9 +UjQcffw9d0eKx+EXGoEHYMqibE3KA/XnL+Z7hBJrC/zAFG7hYb6QimBry7yXBeLLJCK2iE0D +ZRQ5JQAtD5Zrjx8gDW/acnN9Z3sDYr21RUBrNTHtmShaEwU7gUe8L0cK1ZPTcAg7JCDw1iTh +3Ly9fS7fZRk4ymbXuJnbbm138bXBvMxJd4kBHAQQAQIABgUCS/BwCAAKCRAG1xokeFMuksSR +CACdLtYrxe/8BJNmErgmRSC61KE+J+T7/vLddp3ZszUtnV5TUHCZyWgHASo+R+P83cGm5UBb +D0iJIqgsHebE8UWZLBkrp+Ul4QSYzzF4mxBwNRAC1XFzSCuAv+yaJXMwklqI9VufLglUs/q8 +5g8rB5bd1HOqTO720QnWdRlm7bq7Q/Sz+4yUPBG76w3AW3sNTkultMnzMFI0J2jNhkrTEtei +F6mvZBzYkgbbdmI/kt+8b1qH8vTRXZe0dq5nY2sRW4ujvJAlUAHmy2f3amRBfxcBibIrA0Zz +rnTklj9qKd8aC+ptowI49IJ90hdP8MBwAQchb8kujD965rD70/nGAQzaiQEcBBABAgAGBQJM +CopvAAoJEIkIN8ipQjLytEUH/iiyn/N0kpXasuC+sdbPIw2XZd5dXSxWV+BliabcygboGfJw +n3Ada+isjf0WkKOFHA9p1t+6ZQtDlkgpYgh0hVPUrzNqgKYSaxXoCyFa/Vk0TJzkC96m1IS0 +r6fHEDxYGTJhYGNH8TDUBljqQ5gqDwNCUAyevUWSpm7SsTgMrBB7g305XtKd+/3xJbG3NVI+ +bh5i3CUs74Km4ycjNxppvAo0NR35xXYfqX8HH74UNe9En8ObSKhrYjDA/TVeTyDro/EP4VC1 +UpL8KEYTSTqSF6AVqAybTZ78Pc2Lu6HSbjSZcEi9QdXZM5v1K4NHW/6aRdmaN5vnbVVLxzmq +p2/2oj2JARwEEAECAAYFAkwZMRUACgkQ/Cgw27yG79bsAAf+IHWSV6HnvtCDvzuxW+udoS/G +MB/ZFbZn9YdhnLd/WTPoeCySY5wniaCWvhZKtwt/OjPZr0fO8pi+L/k+PzQgpuo6xS7Wl2Fh +Ad1mq+h19AC0a87Huely1LAcBzOXTKk5x3KQqHvQnwmZ4QgvFTZ/ySsRzOZsz8XG2coWQ9b4 +Tyo7RqwA9SK2Apek9BffcI7S84+fJm1bq2/fj2z/dMS0NVnnvVJud32FbRPpCLbGEUBhr9tR +lhbEw80ZzWwfWPoGxouBEPSQp/Wt6URQZb+PMnJqhvaf5l+RMnm5/Qj8TmLwbQ1QgC8EKaxz +2nankbFyUn6f8/mzmPBDdDXEY2zVeokBHAQQAQIABgUCTFYj5gAKCRBrdR/1mboUsnPxB/9g +QNOrbql1d/w4qkZoYF6l7iY4+mW8JA3AwldfdF8fMkYoIqhsfbjsU/CCh/cGxQRJtJC0cOX8 +AftKgiIGfWrjaS5i2a/DFn+xcFUySo7o4r8OAQY+lZpt5OycLwkbo1AyMocoxJfKKWTP9oIP +8rNEls+XBGlufxmACiQ/cvp1v6nqQzgYb6B+NrzH4LHiuG5RMVYYMJgF1RAHg3x8KTOMvAGH +636dp3QUKFl87rYXbBSG+A+RcUtqQeibLaeodJ54oaGbvtJ7jaFryYbEf99TtbzIL+ySFnbH +KeWdplrYnjL+rs6l+4d5LhAttEfgWoqp/Qmv3DyuD2iP1mCZjSYgiQEcBBABAgAGBQJMYN+5 +AAoJEEyJCldSQWdwKhkH/i62XwsfudSSqT9r16tmR4V/5fRcgfpdj0MUhck3kxEYQXl4bagD +Qf/aggeLdYzzI1ODGsV7irL0LcgGUaOwCPJIo04/gzuv24MMw3kpXXXaq6Zkn0LNCRGQuGWq +5+p8yu6WSPpQrHke/gKmlF4UqM4DIFTQDlU+ZqVuFXjuqSXhYZ6lJHsQZrCElEC4aA0Ij3ht +Ly/Q1kY+/1c3YPf5EU4a6DA9wx6B4kENbxHt4AIYNOu5yEbmNgV8n9ArgPrqAmIcDuLV0keL +gmpfhM1xsaQqwqn0gP+g0eaS4Q0uhAHZhfHO1EK4E2/aG3SabT2bOfSZt57r/9BTrks1wO4A +UwiJARwEEAECAAYFAkxybTwACgkQAvwjEeR5oOXbWAgAsirPoDoZ3WdKpfDQzn4UQfy8R14d +sswYLCb1NKzZyYHNy02BHID7sUE1q8m8pkOAYIrPlg22HmHI//pbbRrOnXgSo/Jkv+ChtV8o +8TQU6wjBegwjLnboVfcMF916LqeP8KqtaSkrIAwwEe2hKxZv5epyE5fKtEmLzLrsAr6tyLhw +flnb0g29JZ8qsuWpCSZU27PZEGMIjDdXd/sTUq9Zv5x85uuUJpOHwsYxNq2JXG/IaclJJudC +dljyGhYovpGezSEdLIApTRa7F2BrSZm80viSdzg1DsS8AtBGyN3Xp+WagmpP9e8YLGgJEgFh +95y8FQ9HgGq9RPPCKR19mPG4kokBHAQQAQIABgUCTIIkLgAKCRD3j5/oSuW2EBIIB/94z7po +j7d0o6okzFv7vuzXJ7V42/qBJODgZ4Z/X7nTJ/xYxHnZUqoDZHvFgoGl58L46Qw/MzcxSI4n +QTxjMet6cVW9AA/Yq1ZHKAwiTR9Fd59KBgiPLOgakOCrTi+EPiFuV1QuPgA9O7E/GmvBzgh/ +HMlBoYuLtMu81uDkDU6aKcf2v2CN6YhNqAwVyGXdewsrkjplMVch5YcdevST6bKdIwG96vun +lpJNPWRLk8zX/FPYvJWpASQGspT4Yr2NHMRQZtk1NfFAcbypodqzF90G7swPUax4eQH/ppnm +pBKrCxXcjpCI9zzzaDJ1KWTVMUMXMJ2qdLrMVAtpY8qrqyILiQEcBBABAgAGBQJMijvNAAoJ +ECddKXm5eGlpfy4IAKIR93UJDu9duM/sLELakXL5+829oyzRHdGbptAgzC8ThN8U/24dEYhd +A18z2Cl57j3dzePl/glU0IeWUM08S1gJXYWGm0WCnEUoXiQqtbKDxcGnHfLsaxP4rg25Noq2 +E4VXj/GQPhtVsppGuTZmDsyp0ZbhqSk/8N18Omy+mk+DXRp6Q3kDvB4kTL282jqkCqVxipLf +nAA871OqgRPXA1WDr+hIxmvrBWG3rYcLVN4BwdqhzyFJtOTrJ4oMftJ+2qTauUmoTu6+P9hO +OsOUTwxVV3r25uRrYqyNuL3c2GHCFPOnnRIUesRV+9rnoiWWnzd4pkNhiR+BTpFiTFQBSouJ +ARwEEAECAAYFAkyQ9BgACgkQOKUSo37/2UE/VAf7BH2PmR5DWMO3C3xlUHkeStLm2+gF6iCV +4VIOses9kZqbpRKtUDCbVYP5TbbFLatyZO5H663qu6zUuKli1FdO3EHGScmUnsZD4ijfvltn +qCec/uCff2kitPZROjgeYnSXbqLA67QgzSc/b0NxUBv9OVlLYSVuNN+wmDOuoGe7bGUgWv6j +ydycCCCrBA9pdK0cBLgCS5HW8nzMvToWNC5Y35TKoWv9U62mr7qG15Kbe3wt3+Jx0tdpAPnL +AW6xgtvnjx8Ga8HUb82fLefcSq5WyYzCLsxy5SKFZVofhmOkZaRjvU8ub2zTpDejCXkLgVTr +ZPv4H81WHfG2kiiDAjxDE4kBHAQQAQIABgUCTM3WkwAKCRA9/cacN87JeSh9CACLpAuGyqxY +VEpoGCsWw7JjI2wXgIinyjs/d+WuMg6jkwHlzRCitNPPZTD5xkDxEN/mPYE74RT21neyhTF3 +gsykv3qtBHYI67XiRZbNT7dF+jytdTEEgeZsKqB4ZeTMjo/RafIJDsWaAep0ScQrLwQ7Bz1k +bEnzfVf266Z9fezwa0Pp+1mf+IedfpFdgkN67pUcNYWZ7Oj0nZTTpZv/uCfF6skEeFqXdw2G +9b3Pc4iK1PV/r6R+vLc5jTvqrVqEDFyc2DY7lMRDBpot0LroZP+kIAe+YGcfTqexlaaXSKWc +hNcnOONWfsiFhrfZiXVLoK935BrndVxN3B0p4SDHTAbkiQEcBBABAgAGBQJM2a1dAAoJEMfU +ISciuiZKfY8H/iNPdgfn7qad8j3Mrwl2Q3sBCDFFifxSX07QJZ5hHIj7JYLJR3WbKkRp21VX +YfNtZz4Oy8qq5ouwYu7sc+lN7oW1CNO20bbjAUhXaBirt9TZUh1k8ltjBKFbJoP4uP/CkkvB +KIihDK40rcIoT50LTFY8gxLHtDxTToBKD3NZE8KrHY/Gj4OYBoqw93jmTDab6ZQ1csO0f8Av +zfuIr7RPhGRYKfK9kp4y9mlfnpv6akXhcu/d0rwfZL83Yyv4VOYj1A7uN2uunZyKQGU0KLkL +RmwK2kZIX0KRsB+K1K/7+hUxZMK/YaMK74ty4pBihnHGVv9EeEIoQeEWTnAqp29XxuOJARwE +EAECAAYFAkzysroACgkQOWiovtuGK4DQGQf/b4bRzL1aB2aj53vKpnOwl7N8mCir64DMB0ng +0Azd5GaBTXtwZodVJ9UtgAAFNNU+0cumyHq0MhdQObO6TIwquVqEHdPtnw/jTTvErDjIIZ4U +P0p4vY+yQFH/TOkjQxFCyvBGhWFJL9GRr007rascfwDis9K+XDeKp2S5nmPwJuWQxEPUiqBf +dwl1sQtNOulKh37BuG4W946bmLAC39KTnmwQRq+o7Z6EuHM8+iVGQBiJBkjsOgiyL1C0x6bo +OYEn9m1QMAO12T/wJ7pVR4GqEfOgrCNKZWHIDfqHUJtnsX9TW1CkuD6deu5autVyrG76AuxJ +WyJOv6JMgaDSLZ14y4kBHAQQAQIABgUCTP5IzAAKCRBCsjW0aCN+8LzRB/9iLTUzdNwyDzGv +nOsUwEYBjLmDX2wDZ8j8/+Y7565faN9t9O3+3aP+nO+9pK9zxCzccUjysaWVb3Dxm73khCTL +btzpDdJDsgY+TLroZO3QRaqCzZ0zWlNHaOcfdVi7Uy4i4R4Z4DYW1hptYYMm6DgysKBxMkeW +UldTU1xKGCwap8CeWNZtERCsXNE/fKKOvOLTzJE70oSfp+/19VIgqPeNBzl1NOzrFF/Y/Pwt +MRDyHz1siGxVzw0LwdmetFvqfnllpSbgFRbIEXD1fm95W9qzJnhf2xSestC2VNZjk70LQOdr +3XbCNQZjlDfnLH/FWtQRCEryw/+A39WZFx+kj5WLiQEcBBABAgAGBQJNEB9KAAoJECulqVkQ +h53wG1UH/2aDHv7hx7+rQEXxQabRGQWz+TVn0e2zYMTT2bXdS0EHyrR7eHan3uicWwuwuAtM +kITlX0ALdFG+47SJe5iB1c8sAlRPhNi94pIPzoBJ4GTHSZYtOAoJPgY5eudvtJjF6k+tw3E0 +SEcrBryLLMrqLSTwsJqCDA8dDDJyrF5mkQkWGR9bNhtOcFw/WkeofGcQWuEh9POXTmMsLLk4 +mU445lX92fTm6Oair+S8szqFJ54mBtt8W4Qeru2UtJxNtAGuSO2aeeS+eLh6vjQJQkd/niEf +9ZTOeOpAz4ft1XhPSrF74jTuEgpP4kLsuvZ6/jK6T71ZzZdPFF45ZFxWZbKATUqJARwEEAEC +AAYFAk0ZfsgACgkQM4cFaAKGGJRtCggAx3CMXnoANQmTXYfSq1r0Cen2HxvnmWs6GL3ESeBz +x2MUn5YhaGHJXouq65qg/69kBHr80TPADGnk95bw9WKuo8a/PeR7bD1WZl7X7Cvz3wQg4cqV +SbDncdWlsdHg0mvF5pj93JOmlgaVgvEm1LtTFL8Hf99QfwO4LhmgUUsrbz91+0dQOlttljsh +15wP7tvNyg/WvHT3C9MdXLLkxetolWkX/Cv+Nq/iBlegnaKxYZAo8U924VTbQbf+t1mrpDya +dSr/2rM+joQ4O5qgfSdo1D0uiUgCZLGGBrpzm1SZq2ylH3u1HRa+/OepAdvMUx09j80i10cC +duRjXSf9V36D/YkBHAQQAQIABgUCTSUZYAAKCRBbjy0yOzQXTicoB/9rVG2I/tKdq/aJCR46 +dTum96MvV0GSbjFIy6ThlV5JbvZQNE4nv2tFc3C9lzUo7dl9Q1Z59eqTZg+yyXKpBinY+QI/ +QntbmttcqG/XJFwzt4AnX2WqcPzSCJ/bW1+4gbFhJEctX56HTWvEz/AW38+GXUbnAyNVizV9 +TTuNnqRICr9sCdkB+YU1CCfJNG6161KD2rgLs46isnZez/0DndcNf7hcnPuaRvAOoMkJMiev +HSWYr5Nnq2yAlkPSKhT2AgFPmA/1SnAfs55rfwleSxNU/rkAw4E6yjNG2KD+srCMzDinh/aX +fXkzXTCSGfw5SK1HbHVhPBHWY6aap0SpmXBKiQEcBBABAgAGBQJNJnGBAAoJEJXUidfsGuMh ++IgH/1ydgG/0lhRzowRx8RDub6ZcdRpX3+kK/fP6BkAcBj6C3CB1nZiw/DZy5JWFF9AIG7CU +Pxi+ViGD2bkZLDBH8bTsPmUwYaTjX9tOBmOXn2YjgV1A391TOP0pm6LQKrOqm+c7bhQ017ZM +f2mdbPsCmnYpIWZzue3VETSPCRQ5T7tc3IpVutlRr+rPgroouil4BLvIi2lbZSNDugle1+cw +CoK3sJ3egvG/4uFBKxplIdeyG9RHcvUjolzbBuXhX0zqqY8W/bxVvniQXEQydmuEal/fV3lc +2Lq1HLmZWiVP2RMh1Yf/KuaOW2nI38tfAejmoFtt/lb579IThHaY4j8/nJqJARwEEAECAAYF +Ak1QG5wACgkQo0WcqqOyas4X1gf/eYZ4jvoLaTg8p7cjcK7odfgNhInrAs0AxXhwtxZesjJA +CZzpra+FieAT+Qe++48q6ymxbvbiNTKTEU2mnn7CQoOLWAsJHzTkwk8HGzszExQZ9F6LomjD +oW49CMPPj6XXGeyshP7ugE3ccheRpwYW9+YvNLsV+1QdLzGdXRizzxspt9fTHf9GqKpKYm1A +QRUk7QL4VwAyRi8Be+wEq8etRBuEPM8pzlCOIqR65HZ97U5OBgB+IHv3I/ZrVNNkb1yVGJaG +xnq6n+N6a6ivDv1Pvnr/f624nQZwaATc/6kPLvfi5n8haJ/S9Ak3fJc0P5sctwFddKhGEOp3 +S0Ikzp7OUokBHAQQAQIABgUCTVP09QAKCRDFKoUXoLfYLnc0B/9NLnBeQZJJRDnQUsHsdg1R +h2yqRGFDNAQpfsaPYyZTahHSxMmZC9EKal/sbR6sTyBioi3Ih15t47ZmVcp5OAbUQ46OkMR9 +IpKT85kfbm41yDgPx/LpzT0BjrdXC/ucaymsud9hXBfP9rrecuXE0N2R+o8EgObErcS4wMIi +3+Mt4VoAI08Q2lIpCQBjT1pcxPHMYYpy1X9r01snTQlaRY5KSDoPu6+IFkMZPbJTjDcHOsJw +PazJNuXrERxhWTkrRcPMhxClXECEBTTyoJAohBOx4ZPbznaV0Jy1fWJdVvVqKAfy69OOxqKZ +b6yvEmSfa7J+wVT+gMUs7Q2dTfSpsTvyiQEcBBABAgAGBQJNVCgRAAoJEFVzpOJcXGFhNEoH +/R93iIykbsBW+vdQ9tFNu/MnEol3t6BoLcOD9wHvJvvD+mdoeoMEufDXhL6kua6A/ns29KrK +NlnanHZ0OQP48NislKi47kxL4tOh20M3rTQ37ijuo/+x2kyRrhu0EZcFyxSAR4kCP+BpscvZ +48mWsYCNBZ1RG3e/7wu23DZL3HGrfbMZyjJ5RBxxX8Ds34o29z7PigpjYpQuAiVCKeDneQNw +ykmwxdmeiZXWHSmBQ7WhLm2Xy7qy+ZmTYV9XVkZQV5iVvwdW8vU/HTdu5jjVozgO9FLcRZlb +EvK9HpFZbSUSIaU+P2r+UmPxOuUpflgx1lpdHBchYWAAtd8L6EVI8GqJARwEEAECAAYFAk1X +ND4ACgkQKnj8WGzqmMpxLQgArGIWbBrYpgCQjvXpfDTP4SlShVRC5zRfsw1SCCF2kdBbZKsY +dl/sY3Kgys1ak9LwTSsjGpVJ6SQFytUHpSz/TNW+J8hOpYrRih+7vgqMOn94S7Cwr6pSQd04 +uXp2o17wTj8HTdbdeD71D+M6A9kqn5C2wnCBFTFDwIYwkLlN8lmzMYI+v07Ow/ebLl0+vpwJ +HXPvuHbnkS7GwY7/50xEdfLNAhaKFWAqlkLy4GhYIJ5HaQQJk0WtenW2+/SSg8PPxjaup91J +sRbT3XtNQosCfF4dJtEUCXSsFnCAMSeEb6omA+6rtlTxrqEPveyDSZruLXTy25aU/beF4WYd +W4Z804kBHAQQAQIABgUCTV7yFgAKCRCivuCSi1hE0BUDB/9uydU3aTxqMGim4IO44rOH5yjR +f06Z2n9lXKJ+W6+Kt21rWcLD3h19goTYn9XdoOPHkXgj/pNplhiwRjh6Adcza3ymDN2WOZTi +xojqVXRygjOtA3bNI7X8eHZOnKTXP/7IN44qJzuVIeS6hh5cSWq97kRSuJj+TdT3adF2YVsF +6Q4Sni/a6AXk9Gj0K61TWXZniMl9rH8nqIj3T5ILchR/i3ZzbFLKMaQRfpW4oDj+3+HG6T8b +TAr1ThyAjCziJLxh7qKlWtwdXY1Ml6RmJPh3GCx/x4sBzg9nrkt95XfkorjCgU4kjAGWfylv +agWm7XnNzIh6i2ChqceBmdeLXfaPiQEcBBABAgAGBQJNbTdWAAoJEIHq7RE4XPLs8toH/icv +maO1Bw6TyLm57sBy3JL/DPDd3SDqhOTonFSi0AQIP4Goz3r3iaGuOSQXNIlnDzpCbM4MhGsX +l6iLZ2ssnBS9bUL39ZIDIPK16skv0OZbjlESNdcB5J/rX2c4d8Ao4E8UI92r/jQ1cmeLk86+ +3WsOq9FSqhHjvRaMzu7SscoPUYviNYusgv6n5BTnMb/2o2BfwITY+jYIRBtumoCesWR9atVg +oU46FTjpnDx+Oi4pL24WnZNQygN14DpB1+3ySdZVixqAQoO710nzlMwyHZK4yXwLwF0KBMfj +7NMlTA1Nj5QsbnAQ0Gl7+JPSI65Y7sYTJFhrxKDaO7nRetsl7uiJARwEEAECAAYFAk15BJ0A +CgkQ/wPZeS7wMLeTVwf+OhWAEOfzrO0RP+ZScJpitui5W2WDFhe4JovSO6g3t/BzUkKC4wsI +4akIrek33407hpF3ww52RHkpyjU78qXCXZFmcopefzov277uiGUajqOcUrhMwayrfgFsttfw +616t50dTG/jMtA5CxDrgtwuESbR6zN3nU2leqyFY6jsv5jccSHGRF64nBCkpA+m1NJIu14Cw +HiB4aa2Zey/TvbdodDlQhOGnnybDvPCHJJWuvIR2tECsNPyBLQ3TdG4GtoHaQNnCpMAhE8Is +Oy1E4XPnKpBS8BgMEQTMru2f34QIliqK5YPW3tfXT4JfKscR1aaECGSZUCplrzjzCQCkUYHa +1okBHAQQAQIABgUCTXlB7wAKCRBI29aQMJwwXjusB/9SJ5stvfpfM7+YsfwfuVAXfQ5kXWNI +OGP12mbTaunkjxdQ7gtguWZ4hHRbVAq0KFWICuAWWUiqOUr8hIa8UisrzK4BEjgPprNrqErI +RhojzaK5YbzPJYWbZMI1t7uNnspwpntpr7ix9YqdN511m/UPXcGgmxUGItBlf494BIzm1Ufw +bOGfRJnTKaZQLeCi8PBOVUeaftutojnE2/VnGnX3uMNSzEc1a2VPPDCCL9yDINo1ZaKKFD+5 +Bw387V4WsexWNutTmKi1PIpnrc14Xh9Zy6YROH85wqjFfnQvXOflJbn58mgBB4+YwqRO7dHj +gZorxUQznJMIuU7v1gUDdpZziQEcBBABAgAGBQJNflc4AAoJEOBS8lIZrmJuDXUH/jEFd/Br +61cOo6uNt4IvIEGY2jTqopM1ohATmGJalcHmxcBUi/zKbbpBeZRznmn6CgVSh3RBAMFexiaR +wziKV5wZRlTwV11eXaUSMNm+eIBDJBLVSy9pbmkIKCcasVJ6HR2lmm8PF0K2TuaGKYIGZ+/U ++a6+fBsvQqFyY/mKcLyenaFF318CveYraWuPAfUHE3XawVh/2M8kQlgDFSk+qeC8xRvAmvTV +XwMkx6U9Q0w02uNkFl5/JfuVZN+9CMYRJDCLkCIK5ui0+QMN18wVlbvYoULV3/K5TyAmotPi +PaiETzK8P3PNvDv7W1Qv2dxKzupBnTUL55GeTsoj3B8h+16JARwEEAECAAYFAk2CO9gACgkQ +vap6Rtxc51FTlwf8DkeFVC1zrZTmfaYj2se4kqtcE/YTogi1HynmHeEtUA1xHYIEQaBS2toq +2/gjJvfskFKY9G/dzuWY0JNEVfBaOc3YsrkwkgGx2PNkhTKNrux29ujz3Pkvnzh/ziGwKvin +iARxxAGJ6F+NI6iwVP5HR9FLAvNpb3Bw5nMHm9LttmvorRN3nOyPdgCBOqz8UdTvqwQ9SorZ +NydQ5Fq1bbWlOIXUADYpOzULzHw95m++GFivA6MMW3r6KGlGY6zMV7eAxIzGjZCQP1IYXJbD +lrsDGToOdzyfmPuqVOzIwWGleJHah1FITlbWop0upS3O/EUvnsYxKgTkAkzlOERv75DSaYkB +HAQQAQIABgUCTZOvmQAKCRBEVIv0H3UTFqMAB/9XNpKy7rU23RdGOhbO2XSw6Z3JJiR8ZFvR +voBYyA03JHS9wUYl1dRb4HQGpzdPITOJQzEyuF7iq2w7aPJF530eAHc0LN7k3Wvi+i01uLtR +K136C+Q/P/ofJk8Tts7hQ0JARtdRzwOETdpI0S3QEpeeIt7WRETayeooo2ExFndk6QAyeEty +Wf8/pyIqI/GF5E/F/JOZ3Qh0rAYGfFJ/NXi6LYS1U3NOQ1YxTzaxF9+3iGXY27rCx8jQEFg5 +3ViR9FajvEvyDaOqxZ0C80WQZVUMyRcXr0Thgtg71bSCc1MKpOCuzyuM/MzsJGtpUgoF2gwv +DmsHSRrSuu37ijcpZY4NiQEcBBABAgAGBQJNmNWKAAoJEG50CUA0cgosWKkIAIRj2ahtgri1 +3h8q2FsJ47Nvu6YBGMqi1fTzF4ccPbHmtzCsEgZPHTKSpTw49tfxNftlg9jYu8z/71xAtb0q +/grpQMRSeFV3LxcXTMs+7lCb92OiXBt6yaJsmIlJ6eVRe3k3Hd1FmwRzKiMbQiGw5uO+ntqt +W0GUTIW3sXpO0qJ7fTDxodaqNrXbhLowAd1ftjTJZH25ifrkP9VFzdtq3Pu+rit4TC+JjBHl +QmFLv60HjNWO5CCjC6HcV94dPArsjvi8m79TFzSMJAwL4jtlWaspIYUyMCOLDG3R8Xb0b1ju +CfrMEAw7pgkefBUwcMWZurSXwCnv+/8j/lKZ6z2QWniJARwEEAECAAYFAk2ydj8ACgkQtZRq +HJBc9C8VRQf/R0s1YZkVpGzmqrOYxJtYqHlgURmbWSQyjti+VFGQEjd+wrGwurVYcZoLIa6V +IyHFRVcDgC88q8yxOTPgjuOUiM3nojaLGpKtaf8S8/AIWj8H+ciFQDcYYuLmzt0JNd/y8j4E +PspizPisAUs97i6Euaf1KogfCLBXUbiYM8NGfCi3wvCUVMKskWV+82+WvC7Hfh7HyHzn0HHr +nNAc71labHaXaH+jdfo6O3nwTvlqM/H9Dj9e8oI9I6WavH2ByI6A+wt7xLm/IgOXfaKH6kzk +2JECwmeUcFD7x0DLr4jrGX89SXD48nvjZJiENvqDytMfErfp6WmyDKMO1rDR0MhX3YkBHAQQ +AQIABgUCTbVbAAAKCRAt1TnAal1X6tMuCACiFNnujRa+vL6D718WMi3grpHdfC0zXYbBMrpO +vrm1XvRJ28jhPDH3j/D6KhEPKxIQ28IuRAOFPiohOMV+LpZkGn1YVgC6/Br+yRuT37wTW2XK +s16QZQT4TNJh9NmYXEd0ciC11KuQ0wP7Xm5tTabtHO6Gz0Ofeiwy8gRno+7uOm1k72/3X4i8 +nSF+dxFyCpjPlOfxENGPJBRjuwUiAOjtJ7ZbZ+dSEG+ppA9DL2yrvPzBct8ujH52d77RHJ8Z +bvnJHaeXNWn//Rkw9SR/UYbbPojj6NxjLXmjht+8LR5CVkA/K5wFjJI12pRQtgwxcs5kBDZa +oxA1hrFjwjlwyugUiQEcBBABAgAGBQJN5CfCAAoJEPN7BOnLDI1RxtIIAI27XY2GaF+ulGi6 +IpS9OUWPJ3wjVOXFsve7K7xEmzP22K7NUMp5PAb6F9eROMCAMEHA13aiTec9f2/gqEdXJP8A +yw3H3sqwk/RTMXZTbSib7qbwvazj9RhBmQa4vpD9O6THQLaHjHDUzr2meP5Yv+jhjo/baVqB +QjmNC0q7XFrT/VeWpDCLZEsnzOvOMd1ymD102QyPUA/AIMX21shm1X7Pwx7dM2h0/sfvfikz +Dagn68VnNbdS0BWr+h2AJTxwJ4q5wovvk23KXFN2+WkQoLrbElXaSeSQ5p6I6sL2lP+9ufFt ++RVM1zb9iImGd1Z9UOJYTBXIyLHomBDIvjj/bNCJARwEEAECAAYFAk3qHN8ACgkQgbVCgYHj +5ASyCQf/eD9pz9OJE9nvs/Q2+ynneGu0vu6UxwfPE4rsxJdCFkaau59/2yEET34FEvhX/xkR +haZDIdpkFgJZBAdp5wnyA4BDapTAkKHtUnBlv3ZDwDN0cL4teIfR9aOrw8MDUYa1BLKPj2FC +g7b8mlgkWixVyjss0UTpMnEmNUEeO86sM1Kq6uH5psE1tanHUX5JzDqhYyLYTkqC4Tjt9o2Q +uzdqLE0Ipzdb+M+TUWOZXL3EZl64aY6j7zmaU/fTP7NqDdBoJP3nXwzd7c4XdtfcFKEEWeNM +hHBMK4D6iiGiWf/iIaG+3AX2kusZfPwR7JfyhbdSUXuUzHmKIHDqHSImh9yfe4kBHAQQAQIA +BgUCTgNGhwAKCRCmv+duYCuwhFYVB/9UrCM3kvfx6Zy/qKB+3UfFGdxPEIp0lR2BOhRH1EjB +rZboGj/7Tq84EUdfs0Md6Ey87CE3zC6fOv+CAOT8d0CdEwtf3AtIotufo3SCVYq7CDhl8E/K +rn1NkkknYfeK+IBVnXCA4W0jG/esLfmlPI9VEqZesC22RDPDLH89uh8qpDbZFKHjCHaVV5l5 +L92OBdIKc2ThP2ZSlTwrj4NBD+pjV3mnmiOltdI6Y+0a75mibTOYGK/VB/18JN1pYuvHyiOR +JTT8sK7kKxz0QEkPAlzMh2MR6hgDiHpl4RHaXFCZ3fElKVHT62N4bVxMWcofU1nThoyqBXFm +Xb+6p3RXav9TiQEcBBABAgAGBQJODj+xAAoJELxZ6Kem04bHp1YIAIIo4Cdt+b+l07LrOLvI +g94D+t+c9pA5CxS7WsCwkKd0Q3N7Cp3OiuhJSssYEyQHa0fiqkzP8U9e2zAcNimCNq9+sPVF +QE/GlMUxnXjZ/L6OG5XyAWn30atCFq+4SfjcVbAfIFMr9T4rt/UZgxTCU6Nxtwm16m5Voz/1 +W10Fega9KUTd6p4Jy4YZdDdT9HcK8wEIrDCSoZOu/PkdEtCGBz6el5L9g5rDvkaQllA+UHu4 +BPgkul3DkQHKlrNeEkAruOFz1S4JuWITbraJ3eQzjbGd9A/QVv1YFLjVapBFBhW/9eaklbI8 +C+ftn1TQtCADqT4fUkJ5wz/Rq0UiIGDJqkKJARwEEAECAAYFAk4WU7sACgkQ+2marj4TClEW +tQf+P7zmID5fYtVA49/CSfoy5LcHGzqWuCRSrD7Mygd4L6e/1ovYbZJwYgF26NDlUxHBIzet +8W6lpK66nAmEYP4AKU4swMXwgc8rYCZ3N55TeXhqZ6BOYPmZv+KGtWCsqngMIvvRcRRS0lCX +SgT8tWR9zkgslSjBPYAvED5iYksbh4f/LGntIUnhqWY5P9wGi1a24F9vswiGZx4UfI5wpgxD +EBjHuXeDoXiFyijXA6rcN0COusDS8jLnJHyXXX78y40WPi+OsYAhj6TGTA55iBKcP7psvyOH +LBRKptno1Z4mgnjclkr/TYoQuzWjntVzeZy0uLk5m1Y1Pb2nEzhCS0fc2IkBHAQQAQIABgUC +Th5n+QAKCRA5n7zfcotwdW1PCACRzbvSS7yK6ozUmKeSnBALX1zifea7xgjLkpO/juxGSn+P +hL4OfvVB+wPCuIL6V9/js1xeHzuMIRyGr4i7T5hCASsJPjzikQP6LhzDMNvCUMh0UbMdC59o +ovy+/h4MFzybZBMdEf1SNpebsaNFs+PSOz+d05uSbl2/kZHWVEv+Ft/IBDzXfrH5HlNVbRna +xNGy1WNw3krBFHl7kCmfr8DKPhmY0/pTFU/l4njYpxcNpKfGsXEBcztnhEI8f4EIgRQ6laBO +AGdxt/XB4+ta7boUXLvckAYnkSUGrQFkSkG8zj/N9y90/mAn85eaIVIwX/YrPt763j4Yxfvj +I/YDU0bCiQEcBBABAgAGBQJOJ+xVAAoJEOfAfgBHUfPRU2oH/iS2aX2QyLLFbRSP9qAOSl15 +kTswM7at/evC7tCr93EwuktKBKGDfeU5zLULJiuyyL+Vccp9wvo/JKjPK11VthvtBoVsCkSA +QBTVDv7m5zyReDa5P5PgMtCydEBrMuiDRZ7i8iteMBVuMJaGQBDq3b2+mZ5epUO7ADICqYD+ ++tvyEirDvzBbBDk+J/fXGE6k53ybWDURseOsvJFXnu5waA+O+PNanB34dYzwxPuNTZinOfDk +a5CbZxWOn4l2pg4nOKvXPfE34JkFMvJ1+mzXooWmg9utBYhsEJvp2KDY/H89Gqw73qBRa6Jt +X5a0ny0x1W6SOvpEBe1n2i1nEHpdoDGJARwEEAECAAYFAk4pqZMACgkQkJweG0ia1rz+jQgA +wIwlaLkCmvZLzHk1mkFJwUPl+UkM/NbxrK4xFd2EtPxpYNFbOG46sjHSrhCQLO3ZZ7JcWx+I +DXkJlMMIBahkmyhawLTr4rHycawVZTaMsYb8sPhZkXpQqRRmhnGs6PtereDf8wBiTGpp71Rz +CB7e6QaLHuWn9cz4zF+3tcNIiqohCHw7K544v8dX3I7VQwt95PKWBYlp5CpRj6jYgo6khsBh +9CZf9Dc4+gFqyBqojx7jyxBGygXuYAMdq2umq22hUP3EUO2gi1+ZTtsfoKNRck+hFbQRX5EU +efrMaq72+HXR2wnv5o4mS/+OjKsWWxd3ACNouRKUazOpyhSxVyKIRYkBHAQQAQIABgUCTkHm +vwAKCRCufdOUil+yL0zkB/4wy5mz/7t2OBd0OJdSkLvwn5y/SYiPWtAQxlpoEUbhKgYP+M1V +hoJXZDv4b7/OkjPy2J/UNlOw5Qssi3mY1pHc07Ou/BXQQlh0KTKNJJ/pgh/hn8vTgHHUsQmA +ELtivbWQEzc7pj3RT9zBj8oI65Os84OtJo5Yo5zntt3jQY64aCZXki6y+w14KYWPz2XrSQUW +owPgTa/dM5O7VFR85cI7n6gJZoRS/JzT84o0j3m0Hw3onAZYODKebNqZ6scFnxvZCyYtt/mK +NnFIN0oABty89u0emaTpEel6z58WenerzRkw2hvWGILl8wmQ22e/IUm67WHIRTyPOwt0CQ5u +aCsHiQEcBBABAgAGBQJOSHGbAAoJEH11H55D2pkXKG4H/25iXhaL1rp0EuCOEdFvOKLe6GSO +UD+svxihEJ9oDUmsw1NOseDTJI0qNEtpH86zl7FRA/nRqDCypa/qlp8TaruMFlKIY4rG4kTj +YY8v7X3FVZvdlv/P+nK1DJjjpPLLzFwjR+2f8rjUYgHcCV8JPGyLaYop/mFGjCNQjmJaMOcJ +d36c772fIsgzDP+nDQNuX0CrsxYzMv9Ws4g2xHrojcHCUmoxdJHe0Ze8wsWdWQ7fGbdSzKU1 +GFxzESxuZLUgGCp6TkKJWQBoIgJGIZ4k5yJTUL3iBsx54w6wc1iDQ3jOe7WS/3YdlsUK7HYC +/3JJN/lmdavLIwhdjzNpe4kjOFeJARwEEAECAAYFAk5Me0IACgkQXcufGo9g9ehPhAf/WWjG +wvYK2IVyhwOHrHGgoaMuCCKpilQoeak2RXpYhjm3jIFkD3e66ReDSLzZav8/zk6BUxwRtHKJ +6OeE5U4Rl28H2BjqHwxebvUSMN93z4i1Gs0YFsh5m2lWnjgv3nXLpys14dvr69bYdGLYadq7 +zXwWFqHcUNzodeM4cESeesWZhtet2z+kf+W5UA1tqFA+UFfK9BTO/SlokNGaYcIVXyxnh6wA +qJB5jHYE4LihDrrtsU3arIySCWn7eq7Sqrg8LcUlLm4UvHue4bSqbqiILBEjiEhwfETAx1od +dbxB1dv+ccDv2+BiCVLiAPlHr6T+WWBD3k+0NXI9J0ZNfFIMB4kBHAQQAQIABgUCTmUW7QAK +CRAqT/rubG4mlWOzB/wNoOVP+ae1jIjcPdU2Ex4zlsVfl0Nst2RfR7RVa4lRbPRv0JZL7Z1f +T+Fy+4iLIzzwVhjUq2Q+3kyDrLB5J4mHPrUTxHSIKUjOVHEs6GhNudi1uihBERKcQuFoqXbv +2pqVBn3z7KmunhkbEcsmaRIFToIz1b2jaKbExTH7o95AVFBRStBf9QLMn2tBfmFyZXovktUC +ope+tE3F4V0upQdaR6H/Ce+Zscz/IwmbDmqPizWcHUJgGTYucbxavz7EGxxX0lfOs0+j8aOv +ulAic2yjVsVK9L6M7aNpKSBipZ+LASPXhXNdXlCqTt5k1s1hptPz+HTOe6LM9PlkKS1inW/f +iQEcBBABAgAGBQJOZgneAAoJEMra8zilCRtWIMoIAJcfoV+8iA5l/5ag+SXlpr7k0Jrykh1p +W0BCnj7Voz0Wfz82PlAmovnBQ9QXru7AaxV+Zlcry0E2yv4n31knlyBOCGDb6DANcv5ovNPJ +EtzT9/ViMHCV/1nljZRA55ccHl2RxTJ0uJhMjPI3SW7xizAqHBQx81QP6MG3xxVRMA37E2wP +ruCyrMxT995Xypm8RveWQ4jcGdJLnnqSY3OBxQUzlF6u/g1S5K+5t8cEUUaewy6jinz5LiG6 +WuoONFTVKYGjLTs9qY0ML3HEggJNsaLo17574RHMy5fTfkHlazu+S4jah5W/H1mROk3o5AuL +huOZVGVMDhnHn9FvoXikz2+JARwEEAECAAYFAk5mgzAACgkQE81h9iqsIZBpnwf/dbDcaan2 +t6hDmOj06/DVv0i28sgTlMafThtTobbTUHaV14hXi4wY/ZCzJMzzxDzD2zAQ1kOCc0+/+ZcA +s6RUAwLGRzk/Pph+qx/+fU8d1g0/9tzclGVwM0BsC8qqyHipwJ60MDOlhEQ84cthFUBN3KDt +xu16PNc8p0d8Qyqr900DasHzz/l3OnQJ6dYA2pq6S0fveFjFaN6DDPjGVo+ocibV9X1YpaCu +xBDQmFohYsFOxrn/FccecZmevozGmF6uqNPpspSGKW3TeYW7wcDKMgnWkH4RCuEvT9JzZJ53 +MYylqmpgyo7w/61o7CIc8vzst/+SPhQ7XgaJysWfsWk4aokBHAQQAQIABgUCTnWxsgAKCRAH +JaDFOO6suA/sB/sHg6JEbsjHUPFVO3Rm4Ce07BKylMYPSrK2m0N0QF2bvREPoR47lkOb7Vmg +thU2n/6xiJWUezeKqZvM6LsKBAWDSaBZwEbQiU7ajff3TE/XGsfZeKopGVBkSK3mmOTh2U0h +fbaciFkJ6yS9DOj/KR8ekz8NsNZ/v3r/iJZEsbIHtGJXe7vjiGJSEse21XTihVLSZRWzDHqZ +axsAFgWXGxk/mAsSzkU1IHMbvPNj+GUgv8QyTECM+KiqDSBfAWItC22l4bHgb+kE/XKeUz7E +vw2rqq/BtzRtAiBzCy/IsdSjp+97rgEk0S49nxS5qc7WFKI67/aVU5ZnKBMU6v3JRII1iQEc +BBABAgAGBQJOebKNAAoJELyTxhyFWRfUbvcIAIXhPq9q+kdKRthPCb+tVY2HMsLj9Ghc5f8g +kAlGsiAAvmgPEFRxn7KXL8RWy5lskXoNA9dY/nQGAB28Yw1eGNOq6SlZrB+ORQ5RD3QN3qIE +W5ciC89M2tt5AQL+SdRmLapR2+D6vkij5pob8pmJuQTJ4YG1tPld8pZ42CwXQ2b9BY6fQMTx +o65QfkN2jigcuLZKJWdzBV4wlhwksXsQuMz1h6ynUwj83H61b3wK9SaYngf3r3Z9i+wbG7Ok +PgXzp3HpjseyyIEooT6gE5UmFNY76GMEG5991zfZeZIWKLXpxgzN3ImeqxaBV2pU2hhBK6Wl +qebGZ1H+wFpDLCQnTYCJARwEEAECAAYFAk5+VmEACgkQD8ambWw1lY/Kjgf9GuIu7Vu93Klp +jC8nHHjIdW07TFzoW3571J8S/4DKIXntNhKeKZ9A8oFkZcKamk01tNrpOk/JkOWBgwOsgmhQ +NNU5E0Dikr1xStJTHSFFDUR896B0ukQfFucFF6i1WMA2g8/V9QRpDvL8HSCJMGRztyfcVB/s +LsX7KotkV6oXGRD2wryerx+h6wNHhPmm6dZHSPYyPcp1ibup16sqFBpc5hKsrfI3wW4onj93 +c08qBgPt6hJ8OUbpEXV63byCrsYOln3ssAnFKLjGR0WEcyuXUgkIdDgGEIjZGjqOyP8lBdVu +pRFgQWMPClZPYeITM7PqHA5hL453mCO5KX353KztBYkBHAQQAQIABgUCToYKMwAKCRC5jz/s +VholdUPrCAC5fjuNgjd5Jkl78zMHfU87vQQ2gWBIrFHOWStA0uozX1/1b4QvjaQ8DX2eyGbe ++FGSRQjX1aY3AbceU7A0UJUctPSt42wgg/CsrVTLbzjyEsZxBl+TQm/ldjR2+pLya17gjpOk +tWEUu/sg1/trZEq/zDzRNAEhLfHA23fafsRYeObVldtabjmMf/9BE7trefxUdNDH/6LLZK2o +s96rbbo4s3aWdEPGNLxnNTzTy87ZRLl6OX15pFmk+Cfk3uSU+MKR9aZuOVPQekomL64mk0MT +qHezUZxR4ywXYjMLdeSjPHhknRm/AI/ABWgtGPNdzEZo5DnzZfbDhPtfY0dR7CyaiQEcBBAB +AgAGBQJOhgynAAoJEKMwhjPc7+l7EjwIAJ4zxko0qQjtzbpzwd/8nUbuoaO+L2YYXyntyUZj +eY7fFbTxWJLNc94vnNuGBk1nh4NQhSo0kHAKpchR0bBzRnWoSbm1S/PG9bKFgJ3PxccFD1zM +ZJce60KBye2UI/Ho6cD77S8qzmmPDBINgU0PXScaXJjxX9IC17gW1nM5fo6pCSphYZqiFpMt +l5fnUsj4udQQfgky7in5WSdmkRUCgbk2sze3BvGLoBj6T+TjsFZlISr2eGBC7sAIIi8V7PL/ +CHsaA7bTe0/l7ZzY0ZDC4Wj0tQt0mNc9CGJHlN2bgmzgPd22zRuECiRfpqUweBUWk3ZtGLfV +tpH9P8RkGIIuuP+JARwEEAECAAYFAk6cDs8ACgkQzsWzh+VHGMOBmwgArTZAH4NshpiTUHJM +nOAot7U4Q20u7y+ZGP0jLVW2lPmt0mixlEPikRUXhaOqwJPmUozJIBloTqxomnnzs0fUsddw +8NCFZ/iP0MFntgCBW4QO0matcu4eNzegXOBryvIdjXpJysK9XGKoJ6j86hRYfnPlMCeaHwMn +2DxY01dm42IDLBV400WRFHnJuse/cDDy3ppF1sFv5rRABUw3e6SJsvlh9g7OMy4kMr/1KQwJ +ogfwx0mvVn36Hx5Q9JTgtTzin9CwFOLpB+uTT6FzFz5TUCv8kqWoS38jMf3oxkvzH1psVbOr +DmQrH9TeMETGflxts/M1Xoq3EBgcRQ8D306664kBHAQQAQIABgUCTqIP5wAKCRDcJTETV7OP +0/V3B/9CrwzA4uPVULbf/RxNKCUWsz/WDwNk8jJrIX8knn7u8uIaQ2UK9JH2RO5j9J+xHVlj +uN5BEGI9dEa2LaTUs0yfetome/X4jYNGnu+Uf9DClH2Hwsqvle6Lx003KOkr6YChrfZT+F7E +1zxy/FphxkmgAfr70OnzreD5UpjlMuRFJss6ZUERhhWJuhla1z1XC2UOZMRsqTrhMJbADuQY +gZs9z+lakGga7d31O/2ez8mONwOLAmJ/loYGA4sgTkwQ45Z9peaO/EI4QB1HrIXaJP2FIDbF +orXu21JqH4Ta8QudqjJbAvZOJwXJf5D97095lsRZOlD2VOJAhaGDS67mJBWCiQEcBBABAgAG +BQJOo/QmAAoJEM9PbQftFAEH4EEH/3d0UwnOHy+giuHYBosPUDk4o7sg7Bul45MTqDIt6szR +7/8A7pAKp01pT+HIJPZaP5QzkdaryqSMgER7iUr61TUEJ71h2JGz33u8jQU+SNJRZPJpv0CV +ypEt3ttrtbUnX3TKlOZB7xnZC7qRoTAHnLkfop5aM8+syJSlc2UDbt+Qzsvmih4kwPIlGcDL +Q0gUKM/yf+hxjU5g8bIPlcE+Jug2xPgEE48HbUSsA37WIHn8ZEw66VU6p2ne3FE9BwObL2Gt +GEVwrEtoWAAFxb2k2zoy8l3eg6/IsQWCIzie8fvwxEwA3jlkpKDceDpCKp39TRS7euVJZFKF +lsU/xQUTcAyJARwEEAECAAYFAk6j9GMACgkQOSJ7wLfKUFe71QgAlo46T9MyI8XaOABtVmVt +jzYg3QMJp46F9MiUrP53+6PiNGFureDrb+K0uffATcX/dBQwaPp5aI00P0TBDtpKPPuZqc45 +zvbg5JJc4PCoPCPFiJ9LBM+aQDdGmnFk9dNHS5EqcZkFjauFLYBNBZcKTTBCtQ+EqdaZiByW +CxpGNweASvzaUMGF9eeoWGklvzkugkzZ+LPMYSey5u98S9zTTPQOsLr94eFlDkeELVW0yyPU +79XUp97lGooSFhFBmsyITbudnAj8r1oUDrQmzNHdw8NiPxsedpKaUHCP/InW/0Mxl/P+MuOP +X33qV4AG+hdIUCJEAhFoXFStZHdyliReaIkBHAQQAQIABgUCTqqiQgAKCRC3MQrl8EVprnM/ +B/wJ7iBMdxiL6+V5NKyWR8W592zDKSWN7uRYdgWEipST/LfAJZleqtrrvUdrHewdHdkc0B1B +dxJ6kQgzBzy2JtoqHxCg8Nc5gKbbvevHXsVrai0stizn60Lk4H9n+KMC0bN8pkID2WuSJkZL +psiNZNfC6x0ned/5DAlTwG0hLfVYVDisjPjSqYUPe3QbXU0LOrb89UANUxpPWTMeY62B/Wko +wFQWSvTerNSoKrk/dDsADyQPy9/nQs6myuyGDvXWrKIPEm6PaRYJeyyQWG7ufflw/2g5SyNE +DwvZuJBcWd3Fj6oANLxRnw2/1wYKCBwfachQ1Q5c+oSCHcLah/cUKwZaiQEcBBABAgAGBQJO +uXoUAAoJEL/DIi0V5dqRrLsH/0LTETJJ0z85WlXV/FEJfhXPdcztder2lqBiFZJj6PREvyFT +KXERqhmjZpk0WnZRMPYrlBl5jeVlMHjCi5/H0BsC4h0Papp5r5szxoZ6pkV5y+0hsCFaQSCc +ZVe2KPskVLwjR2ZbpQQu7h1PF9GgLW7e/ewl/grE6lpqz87AUY9gZTW1KfdwihD2pl+FDJAO +hGO+It+4l68fBT3EQg7UHjsb01TPRfDXk/WkEoqLMSxjAMYYVrAD/HgwWyfRO37/eUIJ2DUK +tlpNt/PJR+m4YQSoJu0ozPMpRM6ozK2RKNSf7x94Jb41NE1RIfcqSxPHR066qyAoQ+edBEHA +upd73laJARwEEAECAAYFAk7JE2sACgkQkiPokq9hbfPpKQf9E8Qfh9FSGIVZDscnbEZb+YDN +t22HQZzXGR8LLhlxo1mALJamvoFXodz7YiuiVnX+KkvR0RoTG5iDYb0aPTRoHFwQrUY22QV7 +yR3bIUSbICqa6YtOllquay2F9T3grLpPhRxbCJPxPXk4ZFNThv8kcj5Nao2wmAgEGxJ226Bn +yUfNq3dqelEHYgcKk0tJAr3D2bvoL6hKbaQooRa2yT7ycNfA2cbaEWNepXWxRHKbIIfXB56r +r49qObW5PSMrwngm4WlC20gkkUiG2bh5d4ERh3TjqdxSlNT1A3JJjnpfjMj6ICwoacIwFS5U +hcE12J0ymU4v4Y4yLjMZYl+uXSp3IokBHAQQAQIABgUCTtI+6AAKCRBFdS9sfJMCsU0ZB/9l +wMJcwT9uwy2bacQpWr0c5Y9iqilNBXOvx+P5AiY3WyVjngaCJcq7cPzxkQAisOdaxVDsCJd0 +dxCBj44vyGRLDXORDt6N+bT7vAevvj0tgAuywMEkOWY9OrQd39oOLVii1puX9Xwlv9m2sJmw +PrdO8Yzcd+X/vOOgXlZ9to4AHbjfAJABUE8CM8kuLH7u83JgWTBI5D3KJOfHui0KAyIJxL9F +Ia5+Q4yGAeLv3Zh7fT/9deEtIWaD8UXQeUbDBnq00hca9/9NhvSUj9RiQqI91KG+HZ8bYynr +0sZ7mpo1kGA/9PqvK758FGo8p0AnraLREpoz4DXiRVpJERf/KGV9iQEcBBABAgAGBQJO27ZZ +AAoJEFLWmblcpbYQiwgIAK+uX4rRZ0g4TRy1BLHwzk7tc1O4TFqVJBK00RSfi7iNqJqMi2LH +ZMTH9wLYAnwLFtF3fc1sNI8uJZeqSUcOXN2a+I9TQBB+LCvCrp7cumiOQO66kOv7NC2k1rSF +tb1oHDQXorx8glIFUk6I4Qkn1LKFPSjnCV/8f63IdnYXducvf4kC5Sh9/l8dicc3FxcnFpc2 +pWKtXlnxSufRPbzH4jXwrXmL0HKwRe1s7dKyE/d4RNw29Ml9LPYRjTFjJJZjzg7QYy2ASDmH +WuTM469Y6H93JnFxANmQ1q+AQkAddCF9rHngLtIJXiYtoXOmY8x7A5fWY+CZ3OgGyR0MwXSx +/tGJARwEEAECAAYFAk7mSIcACgkQyuWCweW5CyhqEQf/cQBIMim8ejXPlfIzinN7BatHr+GG +uEfCemx+lR29D4M/fZfUlpYdlXlhRdMemx8kJaHQ7+MHZFI3R51238nDU+t7QRcihFWLBgsL +i3ctReoitte37wMdgD0Dd5DLY2MvsmBPBl14G+Fa3H2jyuF48lMRY8b8vQX/x/HmaCcSWZnq +p0hx+AbAW6VgaT8ZMyaUkV2fq1WYzRuG2OqvMfEdJ4GyaC/leifK0HFTahdGWPQV256EpBhU +PoH7WXRmIFRaNLWUd1qRMjpcvJ/t7H2reAisJIjYQywn4i3He55ZGjXmGEzbJ7KCZMerso0L +SMVVhS2QaUiq8hZmnqMe6pgSpYkBHAQQAQIABgUCTvIMBgAKCRBJMTxQr/2XxDw0B/9UTeJ/ +vi12tpeZcN9l9zW5JdjwQRzoDW7Qfj7s4uEyJDvSrJvohPxuMz768kY1jHaAezjfykPuMj+L +vlPSSBh56TRFD4lPVAzUp4C+bKYpLM2+ZUaiD9TL7ObhToSNJNSY4XE2zJSJs7iqxxxzUQy+ +32Ew2wN2MyHDoi5tzyMqn4ceqiTAUzd8jd01C0IPYS5+EXdLVCxomwZtwP5xls1ntVA32krU +JDZk4o+WWYgLSJ+QIxxHoXlS6bTEJZbT9ie394Kiph1bfq8GD3z5kKVZdgchZzZbsOQrRwDk +gXfdRi3VpWSZ8VdqJlev2RHKnQUhzSmls02Svsdi6hAp/j5riQEcBBABAgAGBQJO+xlEAAoJ +EFE9pukKUb78kp8H/i7EdI2yjTdYgC1Kh08kaG0CHVV2vZM7EezopLLjzjO4lshSibwmOdZo +oFU2JqBxcfUBehBTMUPb1vkk9t7tXl8jSWf9ScMP84GtoNV0PeQ8TAqqFTNRSWHCVt0s5DYE +YH+101zKSARSnDraclcbPvA1pZjd9FyOz/V47pyXcF9fRiXUkWQbtCKJJzLz8Vf6SEKaCscs +heahrQGudA+/Oz8aNf9UKDqWefAeQ6Dd1lggySZO3hmbqmSzQYzG706vl7PFQpH2bcWLdjNL +sTDpIrxt9/j6iMMub8zk6r3TVHmaxeETCsG2TL2KeReMckPGycEvBi0i+7vzBqHGIcoTH7OJ +ARwEEAECAAYFAk8EXlQACgkQPrcz97q/QT+DXQf+NTnGTv7U9cqe/W78+GWoIosTMkGYHZ6l +yv85q+ROJ0cu8AvBS6LQSLnvfT90/sC3I4UJcNDUCEDNV6LC3NQCUr8cMR7F+Zrhpe3gEQpA +1bmTiuydUU871oHJ9DnoL66UHiAs66tcrVss05VQ2UETXE8omDK7vVneVxA/whFCfOn8xxUA +AwSRboNdTYNrmhHDRYuDPUIolXs4AMAzXjnAB1yDhX6xpb0y4tl7hpdxkySLTwAjD6NJ8kun +ftDn5epyoNU0W8ZX0yEC/Ym/Q6cwpohFZvO4yKd0lWYac9x6usfRMKGo1yEOKEWEFvaQsUsG +1pqMdAd+hKUbx+nmlbvEtYkBHAQQAQIABgUCTxyESQAKCRAJJjyM/SZqdhH0B/9KuuE/sd3v +fG6dIHD2CNYDM1lY/tIMQeI12rk8ouE7o9tfzLObosRwBU2U99ma7px5DGU0mqZrQSpPq8bO +e31yqP37es/fTOMvTlnHX4aNtZP9+GgThvJoK8Ua0MAd3g+ChL9QlufcpJowDooWdcnIwHeB +RtGWDV+m6RjI0mlRcv91xv+kUW1FbNPdBV1tjYNIYtlgsWU/ubuczPNcR7qsjS0aW8fLC9KO +YbFESovTa1MwBmbM7yl3z1+yLID8D1maImHcvOOreKuNjdpK/VBi76gv6tVSLbGbqdQlYmJ9 +9mQZmVWdaAKtxYZPVtmW6JNdjY0Pa2zRufWji5PIpTkwiQEcBBABAgAGBQJPH77lAAoJEFtl +UuVAMvN+6RoH/1YDkYYl+Ezb2xVM7uhuIsQNYXhH2qkRfUOjIWr/CbpUspgJL57G09iEPywz +Vn6yweB10Pg8nptlPuUBYsHisrqnbAHGVqWCpEYIjCUbqm/kpJK3KJ46EMyNFjW7/RNHEt3O +0pqVRiPhpWg+Ac9LxjdhGU7mQ/khV6v43lLusuEt4LwSR6o8bKjPpARESM5y8GB+zBa6oLjO +Nq8D+v9NhCMWxje/2UALWr7E5olH92WN4VNgbv9EjU3iCDxr3JvbUuxeKFDwx6goYuTmR3KL +YKPsHu1Zddtrhj/1gCMkx/3Tv4EcfSoGUCyASo/pBDD7sloPA4e+54lJYfqWOYLXYRmJARwE +EAECAAYFAk80fxQACgkQLaxEnyYWtp0YNwf8DHrAK4SJO5yRKvKpvfLU/Ep/0LL/FjvEz7C1 +I3VUzwOdN4YoELTRyadkXtXejSd3cg0gY/uPvhkXSnl/RvDOL7lakkvLig5b8XUTD6tOTMO4 +N3E4uM2xCLtRhj0Hke5E1+UxCw0BVHaKOKdzL8AVvuHTGpeDMp1jpxsvuVwdjeF14h3xcZhX +zCAb6mQDkrtOgApeF9Aod4wVJyOtzpCdxx0M5HzaXV4h0tJ6FFcynMcx4lRhdcDgzIMGhyiL +IpEYPCNxyoYjMFAS+8AAZuyL8/4z52ZlqYVcSgdS1fM1EkI5xpj/evnYIaH983SuyevA0wS3 +BAVf/Ce34ATCeqdraYkBHAQQAQIABgUCT0OBgAAKCRCMvtNAO5mx/7+HB/9DF7apOqUVCoot +NfDza0DupqB+VU319cpzKWN0wM7BcWSvGc+9LCTXt32YV7/vFDNg910VJmXZiGmNlNG0ALT2 +VlOHE3HGgHqhi8cL/udYIgnoOHYpukoOgC00MDhCj3N//66kp7vLLbN08sl8+WXKBlCEvJoa +ugZ1rk2EhzfP1rNnjPWso3tv+OjM9a2Z9bo84kdpHxEWYuVDQ2h8+YHehb7/NCZcJgICL6mc +PY1I4iak7Ch1wOYkaEjRoCcvUOAdWGJUZAz/0QolwRC3g6XQdxI6BA/MLzSJnf4rag2Tc0sQ +fzEd8mEMNd6ezXRy21pIV95cn5pWCQ+WlCII4gF4iQEcBBABAgAGBQJPWK7pAAoJED8u3svJ +65zW9Y8H/R718Tqg/JwipgfOyMLQzs1g7txYa+OG1sE+Q+U9IRkPsj/nP++e2838IJQgBH8F +XOwqqRP7oHXyn0ao00tX3o4mQcSYhxkRjunR2yysEaLd1D6dchHqqaVDfLr03Vx/mNi/k900 +281JurbmJcO7HoKiG3XPNOQTwSkeX9c8VYElGgtayFmltYEqW1t+SAtpYHezF4UU5xALaNRb +gm6Imh1GX9ektSgmKUd6InfBfc1w+QQgMSBpiQ0BXNbjt7ZtgQnUiJRdA9F1QFNUUrWKnEXG +MbJnAnf2RTyPO3Im+knw8v/hvH9NqKnGwV5y7/H3dYsKVwYpka1Ij4Hcq6eaMnWJARwEEAEC +AAYFAk+V6zwACgkQ7vMuMN6SFS15ZggAnwlIf08afvWynA6G5Ic0W3p1V0UhvRc54m+P3mRN +B8AfWtGi0xUjPFAx5QwSw6Vf0h1l6jsPFlBI87RsalgPkFGLpnM5nfHIo7x0LItFSVJr29fX +UZVJubBrx513qIWmZ3LH+ixcKqgjyPH+VgstSiCQ9Wl+ahQXRhOq2yZXUAYn2nsQJ91dkEJH +6v+DDFtXkC9boA6qU4rriWEPJtBavoAasTr91blm75EYtDZNIrvp8tln0Zcx9YKbVBFYKNNq +Arr2oLzKIqWBdUeCOf0Kon93nQgA+/xzAecNftWPQ9xUtqZx4wSww/6H1s4IwiVesX/GuqaF +R8l710lh/7dopokBHAQQAQIABgUCT5karwAKCRDilh6TrBQ4lq6zB/4hsP+uusDUUNqHvvNw +jSCHHIjFj/mGSmAmocUrqvi+9oUMqsAdNHbK5t7LjoJsv2nGEnxzRYgyh7pAoqQZnNUvoyy4 +uuDHWkTfBEkhMDtzuUVm/0EuiXAzynv9lF9zXwZ4Veaa7KsGOO3AZ/OCPE52l0u5KPtQ59zb +mWF5m0jgggyrvZWmcBP3JsnqfbX0iuWsj9pIHfymqrp5OTHxJpctwNHO0TQx07I/WLg3T7zF +z5fIuSQ6GvSB5CZC4TwYxdUouKUIERYHs5o3oLVyTcBAz+sg5D854XgQjk3sbHH2woOs3RhG +mwnYWpZEGfxXD4VRzW6+1b1beyhqQ0QfBmhviQEcBBABAgAGBQJPmR8gAAoJECaAfdB+li5M +Qj4H/2AELXVLnTCbDM9Tf68aBIZJow8KTzMMp/6670BlrbLdFn5yOl3facDJVSpYDQ+qHqQ1 +QO7B7yDA1JIxuf2XIazJ69ATWdaUtRYqJJBglmwN8gEz4yC2WE4GTG0QU+woEEHbE5wOIloL +KIZBzg9Dk/GZ0MXQz1C38aaqIn64wDJm3Yoq5DC3hlv9CabBmh5tLvZoQucvFXl0H3M1Hivc +Q2zf82TklgBFxq72vowlinbNf2oPNIuumsHBqBvEsGyO8RMhOTfQjKwUvruUVBowSUPiVqjr +xe1o9AmNg/OsmiK9NiRKiKdZDWNymAIazBVo/MwWVxQFrdpm66IJWU1KJY+JARwEEAECAAYF +Ak+bwxgACgkQwplX472n69jyFAgAwTNz6jCZt5elrFBr/1IY7kigu1VqX+u0qxmoge32KAJQ +6NH9wzCmKhXq4glvpLw/vUYsT0+gQXzM8bn24cfclBrnqdnpAU3PoJgszPkDQtbvPddukmAf +deb8uV+yLN9rxpN4MQv9BXaSvWPjU4PzofJwGq9+hWg/MKvFgwEOhvU1GmtQpeK7AleSFJng +8IxwIttRWct2EKRPmA/pXtcgphbkKFw5P8WKYXcqLk/3sJatMDB/toh011/7FGYFVqkpFtac +d0x8SJOcesOEaEss0WQ9uAfdjMhaKMmSBsScFwT2E7V7gtv+l6QGwWE3OcNpSKHbYgTp5B/B +hwv/0/WGOYkBHAQQAQIABgUCT6ab0gAKCRCEdLbHZsEP1XGGCACwW1/0o+z3ATUus72fcLQe +WHe/nT/Ef1iBzrSkKnN66UfHYd4NBon+Xbry9dsyIv147Slxx/Qjd+RjwgYP3pv0m84z1K+t +QABrglpbIZmuy5ikJ3giMQjCqYs+Gof0esBeDErtMmXc0Mx6Bw+Uh8CERjvKMjJT/LLmYC6F +RPHq0L0QafLvzP+yeitwl6m/GJTkqUVeylJAT81dGzMPRPnsGdzBDt7vHC4wNNJMbQd8Plwe +QMHJI4hB6iGks1uSPBms8pu6tjhjuVknF3QJniJkboqABh/8hwznb8aKAeA4DhN3SAcN7uq9 +ZNBghvyLul6TbFM3IJW7CE+uuBLUb+fMiQEcBBABAgAGBQJPr8IBAAoJEHZVNcvUwkOPp1MI +AJjGlMtyibkF3SWnUoq8x/KWHS9ffBi3CmUr9o5Ep/0nMzmCMSHH8qw/uDt+jsvHWFSnB++e +Y9F6hsWR/TQw8v2jl7aww160zu3jKq/kLKoRNJ1mSptQ7mY3pLw288hw7mC16v7SefnsZN7b +WWjBmRixYchDAn8WiroexGhpVuL2dYeCMia7VDbMcRwooWGKSae1ieyK/DmsudRE0hiIQgvV +L7/e1SO9yel+239DSAo9kPnHKlp+DQodV746d5hrXoC7zMnUjx6AnUX8rzci5sHvEt10J3eC +PHZDjtW3v0P8okjHWW54IBD4PWBaO9tHGWQDmiOh6dY94ejZ1C+zayKJARwEEAECAAYFAk+v +wkIACgkQC0KrNolcJMMKuwf5AWBvR3rdaHNN4C8iOtn0IcZHinNuYV9PNfLKZCAE+6j4D/Tx +52iwlhP6RU2gRiNJYXDva2InqSMRSkB/Fj7CkkEUiRen0bvuAl5RjcsRDf4AR+KGaOCZsWPA +siV9CFGmS6X1RCK+Z2PWDMnj8KWAnrdqCHUHkZcGwEnC7FjLO/CUQBVRn/r2tZE/TXWFvh3u +WnBJuUXi6daHttIwnIc0g8M1ttNqnVRFEBym2m/K4yAgu2WAhvRdwoT0HZz82rxgYkYM9zZs +Ttb/3xRa4HZgfoK8Grz5aSidzEUDDWVG0O3Y+IS+UovF5R9S6foa53s1FDR5cQivn5+lDZTl +OnL3eokBHAQQAQIABgUCT8KH9QAKCRAq2n4jMZZafVFKB/9raS5RAQ00YVJB3/iwpRF/hvPo +Du2v3AF7cegQctEp/CiSx8tP+tZjn08Mq2pyxGBMUC2RXIlBVhgnCIiBvl1pdccSAPqxdD5B ++Mp7G1CXdIYIlFwtOBGj4BPXUbpf9XqoErZXGUNLwhLJDGOrEayL3nQR4+62pswrlm1onA1+ +MPxzcvsLRyJZcxEhV0PLY6r0DDchyMVTx9vxofC9cSWkapyk3+/B9+ZX4Gfv5dCigrxCbXEo +lSdttiquaX7/HRX2djtW2hWz66X6xrX8oPA9Iw+yH//C2FClVSHxQClSab3eZHAvwGYe0yuJ +uC2c3GMyNtWbW8CHZIuqbAktbvTGiQEcBBABAgAGBQJP6bETAAoJEAkvmNGDnsu4ZfsH/RYc +iFVZzHyOsz9Sb5wlJykxZTwPcXe1Vm1L6CKJHKCm+VBdKSSLGX9thaAtOoVsW4OFS45jshSL +b13kjvD4safqzoJ7iJj7nK3M6wrBGpRk4objyVeQAJZWXAoN1Po73bvV5oilHqSMi/IFlsb5 +G7R7hrg3/cCMvmfp2e1lPH00eh5Dsq7vp2pLtho0/TqQzTm8vQOxgjwsxaQes3rPeRVqghkJ +bVeqXxksdEcAk8Z0hpobTLbTiSB+T3gNgvshDY/JyuZn623Q0OitmeK8wzplGmKnA70LHeoO +FISnQOqmlh/7gINSdQ2dMZFhSDrId4kcFjmNRJNrYPEXVBBoF+iJARwEEAECAAYFAk/vmZwA +CgkQ6KvbasI7JWdiWwf+Ms9pALHsDcC2dxYh5ujVmlc1r8DlDgP0IpnHCZ/q7y0eX99+CEMH +ASj2KjzNgZKM6EpnDFgCzQ68858GRmdUB0IlmzGn7jDsAsmuNZfR2gZSlD2wQSJhZ4MhP9jN +GMeXpH6j++9YO6puINNFg1wkEtZ30DgHeAJmtPLkliD8bacCtmx3YhI1Exrv1tPL6mh4rqWl +ykMRBOUHidKtHbGTMy7VLEntHYpesjiTtutFEqdGyMqvMF17BTzfn0j50eNw3Qj0r124iz1y +n7VvFR80/1NbeYNJXVLstbYbdufpdznGmz9gE2VAr3pLbk+bHzBMqRZ+NpYOiE1frIkAb1ae +9YkBHAQQAQIABgUCT/xHrgAKCRB87t+kr3gpZfTsB/kBpM3lszIGJG9AFDarafWU7slE2Jmh +7QurTAo7HlUXY9kvsYaHKKcWbn1zE96ZtfVCSPu25FY7rg95S1SrIXlBNzmnmqYbS+ypKNPf +yGqY78aXnbmnjoS5vxkEzSwO9AUEqZoobIJWWQ4YGDMXcogXYQc6YxJm862yvzJjoiZMuXCS +dKTy3OM+2gotNFmqM+IAGsrqAD0+8mZs2dMnXtMxnbWWdL+5Oh0nNImAuU3uNJXnz1Z7/+GK +AVhVB69wT3wCt1B55DFhJodj9lLRGluCU+JaGSYMoHMIosstw1Ce4Y+ZynN9quBiS/gS53F9 +HKQtPOBsKPp6ij0Iybn5oa/xiQEcBBABAgAGBQJQCB/aAAoJEFqPqpdImdwJe0YH+wWdEjc6 +3YKCYj/HF1t07EpH/VMTr0xlKb2LjudlgbDlJ6GhrqTtU0IczfF751nLvI1sRlnQ+OKkbEq+ +qsghRXAzNAQkKdBTCs0Q64/dhGhMpNnjNqHMV1qeZkP82m86TNCbsl3igm+x2l8R7Um1uWHY +ZOsiItZ8qjAgHx0mP2+ORMmJpRdzGOKtbPb7/eE8Y1+02GgmkA4YHZn64jdXf2Q+SUCzczWr +e5UH+Qe/Qi97yUKEefnmlxpJyqA+1gNzcV6JpEVKcisy2ePHMJGI7aoV8pGEMGA0X+X9Mwx+ +suVoUwi1uFM/7GoauunlqMX9YJT6xtNxYOws6pltAkj0eIaJARwEEAECAAYFAlAMb6kACgkQ +kpY6JZSYgzLukAf/VG4kJ5kLlas5PfFDPXjkV4e7M9fdmxj7dX+XaZGY/eezfpZtk98aVE3L +DmwlQfYWA/+WunRBg+BExJ+kBx92NYz42M8OqL1WG42aUAw8kHPxoVNpl1kR/PKKlal10cnA +M3nziypVE0XRBaZG1D8ZrbY5CRXvqW3VmF8Uec7Fm4uLBINpFrNQWKedtQxzqWQTxQWtzDG0 +6d5BcrzfU+wVW9SVW6NjjsGselcxBvk5EUeQlDvHJVJI9igcTgNSe1Yn/WZBGoBWxqs6lHbU +/8Mt/fjFcSOzjydAh1UuiVfNjM4W7BpQPqEjPAdvk+yg0MTRZK92CmI8czKfiRcBtr+IaokB +HAQQAQIABgUCUCUAPAAKCRBeELltcU8IWq0EB/9jAtW6FBXzceJmG5nJ8PG9x+GscebTJHAe ++gWHHS6fD/5UD43V4rLJsBbqLPxKvcW1BlLtgtmcTj70jaMkxxEfN79OH2YflXj/kSMVX/gY +x66L1H2ddouPTMNbYfoLqQoFAZsUfGId9mOUJTN1b+flrl5XvdITOUL5s7kkQ28rwDBfSKKm +Lqmbf9UvbwTYlmCO9bmcpoDo/4uJ1O0pWNhJ2dlIkddDBo86Yes369ZLI3LShgLaYTudjy53 +XMtK3+jgXQSjJnRyjGErn5HL9Y8qjxLbiQcUSaiEucHRQGRnBoGLRKSoxiVTPdQXmWXrjmun +WKrAoy+Z2yPWTn7J53NQiQEcBBABAgAGBQJQScaAAAoJEDUQHxZ518nCpncH/RZJMBdfXzS/ +G87yu77pRpb0QoG2LfzXzGELi1sBF9DkRaQ+3aAp4OglGKmgn5S7GXTcvw6KUFN+smqGC9tL +I4CAuYKlzpMzWYe5mzZ7n4m/8qnNpxZuRRo4KXWUijKeo1aEUKqo9QUpGT8ie4ctjze+D9BN +FejqptZSbmfIuEX8+x6JQi3a/ijBGVkKU9Iai9VSLp2P8hSqcpzeiQoxRdX+TyQg/K1ZsDF0 +xuj1/hvgo/9kOiV58sXSeYtfaZ+04xPnNeu8+Ba2g/qpO7HfOW7wsDx8+/zflglAcYjJGM3D +gaL6yYE3pKr8Y8H8NdcfCbgGKgY4GdJhxrn6hdxiYwiJARwEEAECAAYFAlBg/1IACgkQ3TWR +A1XcqsDDbgf/Ywq3OkvAGPwFaCxsSVbNXkK7DDVwKpM7BJXB6NrVm3OjXVlaRUUWSfh8kcal +vB5OFqgh0m40fJXiNuV9MGb7+ntp2q0eOv1wTlZewP/D8LbzE1XWqsEv1n5/IQHgvZaV55yo +8XNIG3ngkFp+kwSpnsltFmyj09nc/X8a/3Wgrc+8Ay157dN5w11FTKUOICqqSkycqp7SAdpM +Sj5EZAV/1Jz5GNDQgUH+ITil0Gp2umAw8quWWSMkbj9Hj4HXB6qikCErZhTWyvmLgVo1mSIa ++qXYOYRLUz1+m2O9p8MH3uJhJjuGiS39t9bmDfVOxqKfsl87Zrp8I98BiG6jmULWs4kBHAQQ +AQIABgUCUHE45wAKCRBNBOnOfOeA3CS3CACApC21zfVGWMnSOzm7a/B6EUPfkbpUw6Fk7bRW +/AvnwafTIppPu7untowFDhaRCaVjrnRTFu1LMRIDSlcI0XfBALZTYNowyxi7riCoDYhYPirD +kdsR25uqloZFyGS/jRnczu7J3hytryM+QoAqtmqzDQ7A4FMFH8qRwKBAstda1Oov4BW/l8QR +mUQCYh7o2MBQ7HBhLuNbqgQXc1Bg96MSbCjFh6RrGAr9FeJSV9rnS+jQk+2sX1sf7uSG9Ofb +WWwHNJRxZYtRPdqeQUv5D+XjYLWhhf6XlJ1LuS1y74TqE+SsaZLKQbUdcshU9E49Vy4N0R+h +J6bz9mJPs8jpBu6JiQEcBBABAgAGBQJQdhE5AAoJEBHXd2iwIfrKhkIH/j6EQ8oEpsyERuLp +tQZyIuOsQv1ix77VgiEydaBOhmgVMbd1HNyS1JR3n0c74bSnxdSJh2ayk61HOBuf2PiNNnyp +/kQP2hcv+OkR5wNx4dJvgbOaHLbuPmAJxrl2UhBp1X7rVH9JckLC5WkoQQexS9K4/jLg3E4W +d6SIMgIUkMjAkjniHU5Sh529l0/GOCa4OmHF3VlrcsoahwjEkHVmCL+blmJU4n2tgAQr6q3i +9QzKJFL0qthQkEiLkUfKpvtSURn+PDwIaGOBeOn0NRD6T7HfigwzZ1sJTZPq2VNZRMy9JvHf +wlKSFOi1ElC/eQW6+oKDURYuoiSNriG6qRb4OS6JARwEEAECAAYFAlB+jYMACgkQrMwX9CQB +g0P6Bwf/bqLD2hHxlcUpUTTFi+QLzLvlEShlu+mhkbRiBfPB5EYWldvgHKzuiFRIcZiAG/EX +Snvv2jIucki+WLd/kHovIFyXmZ6Z/1KZjkfKnsLRHh35UrklL5DL2qvfdOgpZ5nWxPAbiV9g +5bVANCc1/h1eiq3UOIJ2L0qiXVeySR/Pl8p5nrKXninhS6jHJSy2siclJZNS+tG6Zhmsoehi +p54PXuhVX3Sk9r5Qp963eIhPaT2U1Uli+BPBpR6sF9nHcyOfSEk8uJL+lQLYFTY9GXz0WSV1 +bCU+kcj8/iJ3SOmyvWwERC0KkU+sMWayrYU2G+rf1ArUf3y8D7NzZFq+4ZKzw4kBHAQQAQIA +BgUCUImPcwAKCRBwMD6hmgtRHvPyCACmETWYQpheObiHAMzDiVj45Ht+4HNx8RmEiY6TOTwV +63bkL67qMg384rlFwL7DwqDKjxGfzSSUegskGzHrG1RTlJtXbUUyCe+RW9LpmLhkFnifLoeW +pV+ZhCaBpBBNFOJPMpDW6EddjXXMkJd6ADyorrQE2eANLbPGc3bNVo600bK9vN2I+pgGDxIs +dsH8JAPslzd2UoNTCQf6ArOcKJpkskj/xwfmr83q2CESYszHOOgY+0nmhxDIyij5VOL/+0zI ++PQ15XMvOKm/eQkSxZp7vDfos8k2wZu5c6ILEhQUwtSjn2LFTQTQ1mtyTuDrr7o3FVlxri0m +beNp8430wpeniQEcBBABAgAGBQJQpdUFAAoJEOKZd9azSbKm9asH/1B0fbfIWvgBIJgHSASG +K57B3Rzc75maWFmneuftCe6gYyPGwUABcwSnOXiwYtJo8WEgdfoAzVR0nBmCcQzDesPzPnN7 +B3DA91yIbUH1b5qqdJkVD76ihGeXDu8HUmU137M4q492BuapDs0/eE6KjLuApYmADJgPMoF6 ++VeZDWpL1YVgj51xJ9CAwZ+hsSRO+6YNRcmyk/wb0grctlMduvYSCCVlonDW2yuUeqj7Ctvx +ZXipnK/XoX6Cjdm4o7gC5gFIcGLXhRd62xn3A0sdN+vO3MmdW9MKrN69uY0KAOYo0H7u4Kwk +3OviF2zaIrH4cAJUOW06buRP9CwmjAfQ8y6JARwEEAECAAYFAlC5TVAACgkQiUx4Hxyt767T +Agf+P816dEhNO38841cuH7gOscQkLFfiwnhDnmvbvUKBezh9UNlQdT15vYuoElXBLcieYayt +cGGdAnhgHEgmfm9GdY1a7xhZRn5CaioWih/+cJEHecgKT2iXQ0TVyZYL+L9p9ASWuzRM0iRq +ScR1vvPp93bleF/JoY3+km6Ga5v/FTNxsk7Ddrty7vsIYATPKX0wKHOrSNJb3dMTd6R8Wofu +MD8cJGfpG6Y2USpqLYwPY6akqABH0sRogmuzhnCTDuIN7EXDjZ/qYZ7gfvCW1WBGgWwWsWCm +C2hnVTYlnXnOTi4vYO8LVNiNSQg/fx4melnAeMg+FbQ39VC3iTvusPoo44kBHAQQAQIABgUC +ULlNdAAKCRA6yyoZLsP2UctEB/93bI29p69CtASPvgY1OXIxS7jJvun0Do4Ah4RpE3watIPK +IFuKjqvk7Sv296mRNuh+I+gs2mPSpvxvZ2aBTMgK7ra9XmqjgugXk1lUzGtJ7RdNsjQCsLOc +kdmdHsmekXaVuzELtIqA1GRP6GUy6NNAtE8lTIbQCFY0xtYvaViGwbbGBMpokN8VJhh/2edT +AazTqPxAr3hRuM9UNhrqKXCenJiS5RR8dJGRvl+6UI/sY1LoWLBaOa6XfvYltmg5XOE2qfl+ +wzuOGKlL2Gjf5BPZxYRDs2G38wmFG0ou0VBUbPcAzIA43zFpWm36R3YjT9Ecd1xf+IbWJxkf +9J+mAx1siQEcBBABAgAGBQJQuU2VAAoJEKZZJoXQS/QzV98H/iLKj06LxH8sNCd516pOGCVa +ylm+NRwF6JfKeSc2bTVnrtxoq0GtLn3dnArS3930WMsEYSOHeDbwIEe2io6yBATwU6GE7Jcv +MrAhCr1t/ZEWvj3Ig8QEU3WxQwYb/5qlNJgwAVwqZcZBOzlNSjfuO1/+aGsgbmU0YB6ZSRHy +E9SgDfJ3FESDWQqPr38ztLjqL+EZMqdiWblnNYA/y//0LZ0Eh1UoCd2pDL9nXYEWWUzNNTwJ +N5wba64kA0mKCTLC8sULqxyaSclWfIC1qLuVf2qWseO/ysXGc0mJdrWRCH2TYTTRbkS6XEyl +Im11aPQAD+Modz1UCup55M8x7K/VOPWJARwEEAECAAYFAlDLBE4ACgkQ6nGrxauDscMKrAf9 +Hib08DOfmZ48q2wEBczsVTRdCyRxOwj8KgcgStBQzCQ3oWOk6bPRBNhRb7yogKem5WJa27E4 +FAt1O0QsELmOXnMlO1jvyJCvabe/rWDdj/72Jn0uJmH3MkVIdO/KDSaE1oUx4flykTVtDRGi +pJc9W2DBjHJGFurNTGqfBW3BL1n3LgC53Txa38DBrD49W9hNNrXNa55ptuRpdCdth0dc9aH/ +FnYpuSBnjObqx8yHqgzOs1a8ZhKCxB3SJlz4QoK87JT2r7fDV4C+yktmbq7hYGbKdhwenQnQ +YPEzearXFzYezdy2JAZRc/0CDaFyrlAcIKoGIk3TeovuOgrduLYxdIkBHAQQAQIABgUCUOKH +PQAKCRAkhDpWPc/3haR9B/9W6QVG95UK+KJlDkSqWgu3NWAtZD3DY9vvDDDx9oYww2X9tSCD +bsLgEAnbMz+jU1jcA8bXHKkwH9E7nHyEWpDrXx80L9DAyF04qZKbsZSyvwfPervJX03uaEmW +MvFU8/qH6ozPIS+aEMz5PeosLYAKnjt7gtkWVdvtpJXFlOXCCcM1Kqa+JFamX5G6dM9qvNl6 ++k+qB/OgUylrnZS4VI8zDZC8J72y8GGkQg+0fdsxc/WqbTxKUCxSW2Hqn5nBfBZyzr8Ql2w5 ++yhveSVPm0Glhs2k22aUDV1smXHaCWMsxHIR2caKEfLiIcaoHMrUXqyt3hGkZvH02J6whUTR +b7M2iQEcBBABAgAGBQJQ+03QAAoJEMy5CKepk4QrODIH/RvfGg2RE5IkXcfqeILdDDXl0RbM +Ie5wuByY0vr3Bl9h1u6FXAb5jRE29YbaVXPbLdJHXUReMGEKo1FdNu0LjJ3i20rKQORO0Psp +G5xBimdfYdtIEHWpy7ulDrfUGjoXTjwSovW/Hw/NRq3j6sGnAJ26sBijs9Yn0Yj8phchTWIi +2XE6k62/BAZz03EpL3mQzsq4frDC7raqLyWpJeD4GaFBQJSfiMT9C0bT6itiTJup8c1wVgCn +RXbfXEqLz8z1bvshhUtfK+1oQLnv1J9M2QuNRVS6Ghv/v552NHugzXELFHdLMPJp5APoDzGv +lcl+4qn5QyZvnDd2kClQt6JQBZ6JARwEEAECAAYFAlD8Q/4ACgkQpsqrIFRyWZUiDQgArT4o +LyWsXbn2FnR0L3zxZX62uBdlh480eDb9qbp/FfFrub29L1Rd37GTUzTBSqeIOrOljq9Ss/vU +ExkbP0jxjRW2a/p+yzdOX7Lyl7NoEYExj8ypRXW66Kc0fMQXnOEtiv+kksdrqqAzP+ly/e4i +D3msUc1lqz37T9QbNDLLSJhucSIVVGet5bxNLWjbU3mVQys1gKZa5VYQqzUJ90A6Aky3OM5u +ynioONYfCkR3/J5F+i2G/9OxlsSdbrlkET1PDvb1vlUgo2QA+PoGTpJoSKRDc744vWSp04VQ +vzQSEgj8nX33Iy/ajEpLu1nCfL+S/tY6ohstFE3Yakbxl7jPO4kBHAQQAQIABgUCUQrj+QAK +CRBVew4h+GQ+m0O7CACpNJcW8+SfkwlOdcYDBY+1lNoiLSIhIcwlhVPPebUG2ULY/2UvclyS +JPwor3/1vtKiIqGgrRZLMD/XTs01ivfY41GCrrBsjg6cWcfY9I/53mDjliAkWNCQa1aM31Yh +fNev+8s5AbxXtT0blwrF2+Xkl8cxN6hAme/q68JoJ5hDQs/0OQPNmfd6O171XJFMdknBG2iC +UuYXgOQoqPRpvEKCHA3/5ITZzW+AasB5hidWQaCH+X3lcU9xXTbm4Q+apX/qJba1Ag7Db1Aj +gByanmsuZLKMAYJOUOHo0vapxfRKVSCPsaZu5geTAbqYTm7AGbfHblHubu6CNnNS02xSuf1P +iQEcBBABAgAGBQJRC9HyAAoJEHqjltEiMq3dvJAH/0vzh1OwFwirAkte9/xG5M0NheArSwnE +yLTclr59Q6xZUQXMqL6CJE/lUEMJmE1VJYAgB/3mOJmskzng2C3qcp6dAMCNjeOgEJUfZQcQ +2C4raPcJy3H6AswZFGVKJ/uR59jl5RkNf3mfNexIqn989eK0YDPtsuMk/XB/xxZ5Z6v/tmaz +iDoxim9WqO9SCRCS68JP57Bg0l3g6soMpJS9XcO26g4qjEoeATEFVYzASez48xMco1jBEOW2 +tTCTppsZ+qbnYqijDYhRJ+gQv+Q93vCHPB6yPehQ7o2cwQlEdJyPT6sak1ebyg2etWtIV0NG +GiB9NEsCNKlCsbsH8KKY51SJARwEEAECAAYFAlEWm00ACgkQWFxWIBeEyAyCFggAnEbVtMKB +9KSfckqp+bBbbLpgP54lzJNiyMQB0DgLG3oIiZgU9sGtRWZNMGH/e3YMxmVQ1dfaGMbrJPeU +zL4n41RrBt5MN/UIfiggp16qGCelf6uzrUSRXDs6FSvos4fgrJL+VWcKMWMdiDYcdlIfB/Xz +ATjiwHVBrB9io/qkX6FouXXTPLSd6+/B7nZiZFqVffBHUH4twWMAaXUUwMc92Xt9Ar6s2c95 +EsfngnQlO8cooAGT32vk516ATtTW0AOzEXYTtptsrr5oCsR2Q1AWrX+cH/zjLYH5CLp9VC3c +cD+3vDBIOdFI7H96sLTOaIaKv3hWWxQ6TLYFPYTMU3/1KYkBHAQQAQIABgUCURbXLgAKCRAf +zA/3hU17V9SPB/9dVdipSyJ6OFOBczeW7zxiKDaUBmtcWEdc4D+VLxRiQMbu+02/ep4m9/n7 +BYYqK09CRRsHWkG0tyFtIMHqsDmJLvPXY++meGDf5pr86n5uGRuPLLBoeHNU1DzsFh4AnanJ +fRBt6BpeZKecXzhDvO70EmYyR+t31xV2JEMUgki16jN+6rnDX2xtUbxL8/K8NckdldWwL7bC +0CnMACmFdAivIRF+j9f/vR3BmvlNgM3U4NkMdMc7et9X6nZdhKBpuBIQUWz2GhX1rDYiu0Gi +uq5jMaQAJ/HBTwv2uWU3XDHyMYMjv7SHuwVdz4B41+KGRnDfqCgSNqbzQnKz86+XTex9iQEc +BBABAgAGBQJRJjleAAoJEBeh+92WaUVnOdcH/3ZY2hI384jtd/NIUIEvDXBiypnm7iPM0XBD +xuLO6W2yixN7doA+QaRMWv5AL4YBsMBpv20Ayr/5/sALS/oGenPHxFWDK2PKsDeB8593ZSE/ +zsm+UNqUWvTc50VZh6GRb43OuQPc6tcKa8qLm3xVg+8vew6IE1L3Jcsa5D+peyDSrajuPHI4 +zFpJBld29517q+dKRiiwcrHh2/dWFhuZSwtUWQNQ9iwAQ0Lmmtsj8WCud771Zwy67QY2SA3i +Ti/dE5ywlvdutSttPm4e9ILLAtOeaquZa80CXOGTSloMRnD8q20ltvN2zaaFJez1bqaJnCbc +0H+rKYhEctnQjB1gi8SJARwEEAECAAYFAlFB99AACgkQae7qFMGucg2c4ggAun2qNNlxbVXZ +5O1lnvEInn6nxpzcvPqQ6kZh3hMrVee8ivoJmJ1W9zwhkI9SNlda6Hc2NOI+MeQKhDWw84iZ +o0199Utex3OTtvm7OHt+PnNhdSlAW2MLKkoOmOwNAxnkCZ/jK1TcuVx8ROlZefiNn+cgDsy9 +osL/hGcpAxWvlDAv/coMaKdHHM5aX4wJr08nzBzTCjsYLwYH451IIE8KiRElax8r2wwY/XIg +lGD+vxxjtTJsTLSCAwu/GrtOR4bY7fQuZHM4Q1QbFf1uOTJ/CQvXvkHYZ8jpYtyu195xWrzR +goJmqDkQzt+wGupTiZHcun8Lx1YOMGaXH9kWsg1/34kBHAQQAQIABgUCUUJ8sAAKCRDd4hFf +x4SQV+FwB/4hQ2f6+77R+8PgKyK4TWCYty6CqZnJo6AkHlTR6iezMkLFy7UpkDH5w6JZnliy +CVl4q7JaOnxRgqNSpAC3q4PWbZHLmZuNNFwugzLhF4nULWBC1ZpyAcr/9GYIJRoh2MAm2YzM +G6gxYnzTob+XyIjRJSWVKRYIFYkHX6re+j54Ax+qRQB/tSeL/impIGz2kQeCYjCfbW941DKF +wUtunS1vnS/6thCtlbt1YrZzgm6WybPy7cAojJmGPfuHXdncNuUcw8zsWwOcLo/8inIj9PTp +U9CxKPKf+iFiUaceX2flCB2Q7MBhSX8tDS0ii84um5h0b9Fy6kHPJgACWbHgVf76iQEcBBAB +AgAGBQJRSeBmAAoJECZKBfwovitI1gYIAIptKeCd9ASSiqyIM5Xfil6XQDXCa96TXai5Njz9 +jV1HKJdtWyn/Czgh8aVauxm156S0yJkIV6pTdt3Zb05UOAvQcjszKxOD5YNkarcZ3U96osQX +APumjF06ISZOH25/4BzygNxnc6vy4iyAeAUgGVJES4qqen2IiUHOeOmxWriSF/igW0157lAk +qyifhdhDc5DajZrUfQtsg0sKTWW1t+C1LFLrWhtGr876vaS/nvyonhRyj6W2rc9x8qsPNXAR +QQGDvMCAFgmv9ynigS0pQXax+5NzRM9ETtxtpJYBBPqfHwAeWC6o5QZto9QFO5L7tJbpTR54 +Dz0/+t12monM0ZqJARwEEAECAAYFAlFfnTMACgkQ0zImB6Fn+5UBkgf/TDMZgNu4ny8Teh9a +st6B3fmLVWgkT7NjmhWb90fTwX93eUEOBWvClb35qNjtzNbGh5EqTYFuPbHWOm5/jWryGM5X +PmgHdb/1uoASD9buRUcaLjBIjpW0Zq2UiXIdL3PRFoOJQjakT6NUDtbQwRJJN1vrR1YNt+vZ +SNk1qcp/sXzH6pAXRc422gRZw+gOkjZ5gF5MzEXjHnso0+lrXlSE/FPp6dMt3ILtrQjzbKGI +BIY1jAZQKx7zH6GTWEN2TpK3jr+d7L8n4wq9vXHkxbPL54KelM58allGIGj23bwtKBszHwbX +9NoXZl29oYu9YWkykmzH1WrlZJ+ICe0ddkiYeIkBHAQQAQIABgUCUWKP5QAKCRBBg8gUBrId +uKfIB/4+hv3h0OK15L1RcNoozhIkpa3Mu3pBo7kEPrWamptMaYoHrMYPON0Bg2/kkUdWu/2p +tVlh9OF1YSAjJcXmW1mKM6OAOaVR+zOvN6xC1e8khBry04n9Q2ThSW06QNJYsTv/7X2vb0ZH +Ool4hiB39rzRIPW+0fWlXVmO1MZsVqsnnUqLU+FJ67EhNAqqhytzkU7pAJlkuQ0L9Ujwj+be +tGKQnCw0mMrj/cMU6+zM95NlZyOb8wX1AtPDUZW4SuWRmMWgk6I6VpZ1sXd6NTNU3+FU50dw +C/s24mmX1q3Ej0u5eCSrdVY0RP2SWpWFgRtD08tuSk/f0bGjn/iE0I3CUmX/iQEcBBABAgAG +BQJRY+XvAAoJEDuGthDu31umtWkH/3Y/PxNsAfKRCLIHsFYmi/4YtltCkKZaQHBYGriK3w6X +HIVUctmT8Qwlhah37nZmCVpJ6pogdMkaQYEZLNNcPPhJW4xx3dJ9gMaxe4VJSPqUyO1Kpeaa +0Skv8DUEJKZ4vZNxC8xlCvOWt5hifEX+kUJ67jZlqvVxeqXkM2TZJBi8z5o90mzgbD5RG/ZD +mUovkbn1ib5EMNPl3Ku2Q/MPfGqNoZYTbZ4Qo/51th0EgiRkXSaIpmx3P+K1pX0Imyo+h0hM +NEpNTKQFzLT4K03if3KWQvlqfFgv5XrGVME5FQpDjsilrAxfwWhFYFn+W9bVolk8AfaMD+tZ +nEgBEfEfAt+JARwEEAECAAYFAlFlGesACgkQwRuSkxvupLhT1ggAr+14xzOkaYAhN+HPAmql +yWrEMCjD/tcVBGgmq9+J2dTkMCgu35uciXuINKL0Q/8npozDccQ7AoCIw+wRvCK375YAw5Py +iiT7ESiPzqQf2cA/w1j9lzkCfCjeiBqY4GfMZEjUURCpyfyeY7Y8HS4lEbir+IWJIg8vG89p +LYg252RBxlDxMBxSnmg4ADP1Enqo8w4+ffcjWNqMtKQuzdTMtRQmj1IfNzJhYbczO2ZHs9jk +NBiW74LWwMRuUeH6V/7R8XEoTnqhVmvNxt8QL5+0bcUyOrrq4qSf6rYFJ/u0xXKrhHASQLi9 +f73fnOsjwPJo/2l54b7J/HCmjSBCdz/0iokBHAQQAQIABgUCUWs/XAAKCRBVew4h+GQ+m18m +CACl19kmdTdo9qdBIvTCyRMMU2R1ZMuX9uP4O+2puQHaXFGcgbIqrJucj4L+UiJhjiHcg0ZC +OM99Q2MigXliLqavT9nvQ5IHWkF85ykZglJMMkodyXmumvdw0219e3HBWKz7fJ5lg6wK2Ax5 +b0t1D3Sx8BWbJQw2DClfaemhSlkLwbeP3EDy2UOdxV54NhAUDzIQmz9Za9gE9SSgXpP+yIyQ +vhAJQMxgl/rlGKeuZ+ZLF99n/8izIeHNOXbRy1sncv+StVeuMsWmYcBcl8JGRzAoMXK+hujB +Z+yknn0cWspoGFaE8FUhRgb3bfER8Y+AXQDDGYIh+21lO6J6AtjFLsOViQEcBBABAgAGBQJR +bDfBAAoJECk1zCcp6lCSSFUH/jM6er7RzN5EPO/z0VoGnB0bK+awsG1iiAEszENnx0HLHBhW +NfSEjsdFnkymUv76JXpOXE26+DsKmGKIwa+hRw5fN9g9fLOsji6dLtP313rUhRWAAbkeNqxX +gMSM/J7ENV6+83AiBPt4FZkc7JMR+ShDMQi3xWPZGOlKRTf5wmyRfz9Asqm/DPD2R9qveS66 +LEvWpu8g91N1Uwe9ff4qTiDhwuROPtchfq2QEivzxKuFuNep6DvuPn/14s0gQ9lTkz1/l0g9 +eyXi5/ZCUHb2t87LhVEdbfCrpJbmeXLVmYSsUCjUCuwlMmP+yPGHSJ/X+h8OWf18XrtUTSzp +bLL2fZiJARwEEAECAAYFAlF1JhAACgkQCsex80+pdqd2GAf/agEi9aWivHuDLhapTvul816F +Ec+JlU3mmfwJIL3LS8yMbmL0fuSQ223pAKnyQ8Q4B43h+T869MXWjA9ECnBFd0uCLBfpdJdk +sauy+yQmADIhmlcNEXuw+5LqfrlP1u/ww9NXW5ufgwoH4SqAG/eY8dKUlgbpcLSSZ+icrpjc +7oMqA+TtNJDm4xaThki88cb1az8vgVk/Pj6GYy14TmgGFHaYq7UQ/Kud2WlaxEpsMSxAB19C +Z679RYglFq11tKDL2+NhhpfFI0621LeBODejRQPTk3yJVMpVsbK0Q6dCLztmwVXrG+Jg5hcU ++eYcgJcGLqwjYCRkrO2kUp9hsq+TeIkBHAQQAQIABgUCUYE/mgAKCRBlcFa6HPatjbUtCACj +1fbxm5dLFSvNougJZoPwtpZbhjb38sU8qj+W3WgeqKUWeXwhroyDZNWRcYSHWA+YP/0ka/R8 +2GCfO7AJ94jvzfXwDqs1pPFmNI8BtBAxY74IaNgvSjmeMTc2cAOlK6tZhPlWKozUOX01+/oC +PKb43oPoaXEe/YSUCaSldeBYutaLzWsiqoXVaWqzv/MuNXbDaRONcjBOFsISKCnMaz556qbe +eHs89q9ykdABfUrJXaMfBufDG/5qN0j1qXEMZ3hxAg4Jap80+DP46De0EogLYaO4Sq8xg3yO +c+R0k07PRQ99cIp8nD8nH10H4WmRbnMiDHt2Cum061f4XnspNh2viQEcBBABAgAGBQJRg1ys +AAoJEFsSJrzl6SsqT5cIALC52wwHo2vBIFsx2CKsz3DazwgP0HLiSpC226RZeDDouWa6iHKA +ESGQboOddNvuuecZjg6rVFmFUW+vOiesOwQw+74MMCEWZ2LeZSB32bUPFUJheaiy2u/rKm6C +DdcXdWFxLU0kFAikfmdSPl4VESGYeO6e3O+caaQ8et88ym/foROX2ofV+v4W9Th4PXK+9aXR +XxeQHz13Qebe4FD4JPtUdOcL6Ug6ZKAcyQbZwT1Pqz6fyR/M+0J7at7HIUQINscLzAzYQlkI +z3iSTcWr7lSNiikUXlgH4GWp4A1yAk4uK0g3URwfMtNC6wKXmPHpSi+Am2xqAmGneFkoI9R2 +hHyJARwEEAECAAYFAlGDbogACgkQoxOWySzF1k67YAf+IbZxHdPoSL+RMIEgg8A+WBeNuVb6 +jNKZu5/Zb0KW2C7dfxOkDMlDhMF7KBKYxSXPhs72ibLyo7Pcm7TVDCNwA2so6WQvLmH+neO9 +tPGgFmVuLzF/eNi5IBqPdMqSNhHtyAdFly/1PsLHtc6o92wgAFyJstqwH7FhGsVOhxB9UI4Y +jkIrmMmUX9VVv3U4MeQi37aQFEM14C5b0sIVvP4r5lS9+UXx4a3tNnKk7XwWyNYpk8oxTPW5 +tIT3vGxocuB9jOfy4FmmxbgJzhp8MD0i2l5GCCxcWEK/9tDT2qLJ/ockqq/kTbuBSQCZUkfT ++ykB8l2vRpbmMSD7sb87gADBG4kBHAQQAQIABgUCUZ3AygAKCRA1X5ykJ/NwLqntCADgY42H +cNHzXcn6s1eRt95KkZJQinz9SuFbmq1UNjg8+bZZaG5LHH9LIYPT2U8meiOERkKqkzKCTTnQ +P0hCxUv7WEu9dyVBnsckr1l/ys6mZsSWxkcNWwBKQHk3EPsX/ftMDRJkg14laPU/lv93UVaW +70kdeOlEAa5wJskX5EbXlHafGdCYmaWnmW9wxCkWFRRanrDW4nfWj1a3pSRtc47X+THKlSTe +mREZV2l2H6XLdrJ0YIEufNVkq/nVKdSa/4ZsgTtcn+/iVdKGKfjU/Hlfq1QktoBGMDs6d9hy +Dy6MjO/kwfGQuQAQRrGl2Gb0+83L/PRfVUf9mK0pWGjLt7ieiQEcBBABAgAGBQJRnc1/AAoJ +ED9CoAXznqAx4YkH/1NUgd65dpahx4VOcQNZCEkHXAZZHsREddP7ex3FVf8rBMhsLN8oRfRZ +CNtuqew6R+/TpON3T9LIrpyfS2MHPbPHKW+yROcuIj0yPQ2XiAfNb7REqHLbqji4KDLwwwD8 +LIzJpkPQzpSL3MNfAyOs/qEczUWUkH2vn5FLr7IItJix2//9nM0r2j/HQUr2FCh0VY1NvDiW +6DvoJcP/dC1mX/3SiEfP6CUBiXzaH2gAPZB/uEnvnhZwPSlid116/Qp2VlMFsUQWRaczwnbx +ipxgxSpxV9x98UVfkaSEl7mwkU5UmTlzHULFKJbbD0BH6dsx6HXEs3cDK+KzFS+YW8M8K9eJ +ARwEEAECAAYFAlGmG0MACgkQwrMbQ4YWXkuojQf/WgbnnB4QW8mDelKsd8LvIM0APbUAYtHt +TyFy4Nht/dyUMXtcE79my0OpFDIsv2uxDf0Cs9Z4BQ42aQ6p7qpbrJvqJlgIL9D+4KvJ0tvt +w3/2T7KyOQ89GWGnmv4SsjA8b6WNYCSPVfPd7NMt0n6Nuqtkksb8RD1jQAGRurwmNWss9THz +lPD7fs/hOvbuC8MSxftZv6MyKMg2O/GIbXOO9Hrc/7cTxWN3r74tQZ8wBeWpqrtDkgS+jjCr +7Mk9UQdQfeR493oYMZmGU2l0adNygwnPVXZOoTalPkMLByRuB+ToyRwq1tbXbEQzxdKvOf6e +zB+qg9udhXy38PynK+Ohv4kBHAQQAQIABgUCUaiV8QAKCRACGDhZjpW7el1ACACgY4CaJM36 +7N6T5ZE08NJYUBS/0kgZpaXINzmNlBw9ouv1jlBEG3Eo8M8aTbIBgiIozVRMSXXgwXD4G9qn +IxM+W8dDHMb46klQrN6gPJbb4VQD/CeWjdp8AbwCuiTRqHhMu2BVfWP7N4KUBjJgIN3jZ6xG +9FUCPJnhoST2i2IC6vD+TAACn9rpMlpg9FsBfswd/SE8hfZdwbAZ1H6wH6EuZheS0o6bs3ts +sO9gl7oS/WQ/6NOJ4n2ZwuEQ0/2foMkszBgouFkG/EfSHPwVJH7ZCj1+7rg7Bk27+c6WJevr +wKC6H5wqH66D16zFroHD9xmHhm+DLk9G7JeXvqXEHqN6iQEcBBABAgAGBQJRrZXIAAoJEO+Z +G53aU2Xh4zMIAJKz29cXFKUWVevC8GqRY/xtAnWKDO8hOd5gRgXkleTXDOufWstq3F5o7nq1 +FRAgoi09nJmE8WXGagDvlMFIl4fw0W/HrRmxOYmLJen7oswfLtwAa8TaQbn6xbSftoV0qPNu +Ol5pfIQBOefE4qIZzDzFq6blo2IJc6TiSaGnOM3ao7/poGapY892GThoOSUILlxv9qYamNnz +Gt+76NGwqhlwFqpSx8ZaAeR1c531o5YrLqNSPNDUTN8P3bRrfv0Sgzkr8KSRKKcuRVyvlCXN +3v5faBdRxIejG1yvhrazhsKhmWRp153EwtZEnxPnpuDRZRX/hfpw6WGD1aXymnwxQmaJARwE +EAECAAYFAlG0CX4ACgkQKw3FpI/K1Nf35QgApT6Pg45zHLy1lnqHLVGDirDt8IqDOTpI93wx +JsbNDceoE+Ighe2yuQJx7HuslupRRWRR+7Aos/DUJxbPfuUkOgdkiWDb3r9lvfEy9ik17DQK +2j/odtWj589D0vm43CUJrsFuLSFjWJqfhFsp3RppoA8v7ZTMJN0lbF/qTexNgJSoa7sSwv+1 +F1ja32S/R4AEq2J4JQyizsyDbuaoArftvf9Oh10zRf7jNUtVsxEiSwTCqZtLHbVpPoRxeNoN +29jgMgDvSpeFI/DhB50nLGIlshzMaFEDMCIC+67dieW1s2FBMjQMNa4WpkyaFNZoL+5+o5+M +FsWUpm13EShFwxp2CokBHAQQAQIABgUCUbXArAAKCRB05yJqZYXQ6qzNB/9PH+opVsq6l2Cc +yWgp4SOFPFPaRo66Wo5oYOCgOCJPoQ0u/Xu69tyT5NzfIl2xImg/nBLC3dU0Hr9v959Gajqs +PuQmh2nNlRtY+4Cn7A2HeXJ74E/RypsYlPr2JWwQjJvl2ZG+quKi5qERQgvOehZkcatY7J9y +6GvKa4g1h9HF+qHWpmeFhvUYBU5UGyJxMfMI6TNtEniDl0tqkZaYWR7MFiEgTJMXXaQ1QZvD +8IxXTIihaEvaGwQHOAlREEnshFCfH8bFj+1F5JNktLqhhj0JvUqR2HRSeuSHDMwUw6Kfol+i +ka1hA00BIVILvL69KnXbQbyOhNn+2Jug6TZW+Z8GiQEcBBABAgAGBQJRxbHrAAoJECooSaQf +NV00g5sIAIqtUn4mmxiezqvJ53MMKvSvehSW2WquH7oapqrlLCNUd4R4mQB5ZdBrQ4+XYQjQ +JtZVlxNQ5pwZCTqzO1s5V3j/F3/jx4wxnZnyd1+b4PRdRAxH/ZFqXwtSeOnr+ewSja7/uVSh +KE/d0gK/ug4cd18UuyVJB3AlCcVZKi2ZL8kcKELjy6NxLXqo9vBesqpZoWZnUvxgfeJIrOFj +u7opEH6lEwxJp7uqfMFo+GIQWbt2UbUDuGrvEHlT2Nna7B2OoQxBTnNPfbr0bTReayC+85e8 +rjWaXz7J46TC80TuHFoOo6yg50Y0oyqkYUy9T+6XAek+BuwgVhRUFcFC+NvJeg6JARwEEAEC +AAYFAlHIT7EACgkQ4MCqw3k+VflMewf/csH9xF9jK1EV7qhKruLbk1TD2wVtsRdLGUbehukN +YIXrsMRXItqtXrLArgNEZq6OMd5dmg2uuOLOoh+UxvJka1CRJ9ixJ66wKE8A5prpS5ABwS89 +nIHTWYkpGfN8quHX63jtnrkx22p+TOp9cyuhytdrpq7lGhbujV2KXdNdTwvcT0F0hFI3FLdl +DUPtXDKBPyQxQRmZlOzP+Tc1hSgE8yZ2H2/bdlTOX8eOtOBm9sl8RMbe2v9+VGwgybQC4TlH +jJnqjC141s+4ROnlVsrD4QaRKcH0+vwlYT4Vs9XOvp9Ld/f/H8trkm/f/d9ASUpM2x3XgJwA +8bR0BugCr4tUlokBHAQQAQIABgUCUdivfQAKCRCjzz4vOsXV85MVB/90FcsiLiUsrsVmpI+5 +dw8amoiXHxZHdU2NMEsxGPkTw/g9fPNC/aoSWd45+iwJksMwGa33yVoo7847q4QlIWFIlYD/ +FPc+yKtyGp1LtmVrWBprvAxKLqi44d3KSJkKY6VA1C4RDRcM6lrNmj4RYTHBczxeZ9T+YIpx +g3xsz1zbwQyg2Ii26ix9bgWfKCbBNgTROQ6LVpv6+Fe4+vX/f+Cbognajd06KQfndnRNHlZH +HWBQzjZP1bgEyLUKcL8AEhn3Mztq6MEDu0IWlCKvWnBt/GdU5tiZ9jQjpmY52Xgu0/JVpFA9 +27g/JlnXqVW2v/q1DDgkgTf+hBEHlDOU04L3iQEcBBABAgAGBQJR2vflAAoJEPpV1tS96/u5 +cicH/2OeUmMqIQ74hGlzLEAYeKo6Z7lttEJBU1OMnnZpR1kLkNhpOL92B+gKxA12s1Xc+q94 +hMaj1w1dl44qZPJDgCfV8qYmSmW6gn0aMR8SKm8tuKupIbXDwsW1SjP0T7c4nRjImuJAslgh +jac3kEGvB6EelU1badUHdB6Js3hzdv2eWvp5og6TVTTS4LMlxEvI8XVPUsQr3ifst3z+W5kB +qAPfJYCfDrMGcxLYzKjJiE/2aVCVyx2utC4cAMhqMeRM5Fql6OkpSovDg/bbnhFBqmuEasaB +FxagMPP2r7Ad7zMTgW3vluphBk1bYLZZKgqgdShn0S/s6dAy2kwfD3i11bGJARwEEAECAAYF +AlHd3DsACgkQ8tLc9BesrdBItAf9GGRh4EQJRlN9g2Mng/MnXkAMKBYDd3NFePzZdwZAhNa0 +UMEc6NvIJG1tASfLpQJSueY8iCXS2vCgL8uiTXcqXgHw2fcpEgpn+HkA5Nls/uAbUMMSagE3 +dsYRK2Yp0qCaIpG2dwP08mxQBoLkbkjVTveURbCUWui4ojpnvQA5L95qWfLyKrrtqSRTY62M +31K8xIGkJ9k3a3Ru590CsVclFuqLCVWmQesP9Fh0P1mcWq3anttSn80HZeKZPrVx32R+S3Es +VTaTNyLjJmu3fBJqqxXg8DnftcMTseZisjKb5D8Q8pIWUDpayOo1KC9ovor5NgNb9uDB4tts +C5MSVYt2P4kBHAQQAQIABgUCUeBSOwAKCRAanXQ/v/dn8M90B/9nmFPvG3e9dhZw9fKnJCId +YjikHcNGeUQFDf9ihzC1+CBeo2HxZWppgwxBPtg1Bbwv8/GzAIWjVlfmweOzwj+fUMYKAxnd +J5b+mXe4+0EWAFMRLhkwoHd6W7j2rZXxfbfgSbWM+QSg2JfnDi4RLxo8B95bc9xFT9SCn54q +uPOReWD5L5QHvYOHuG8IZMUz0IkxenOtTOztznb/TEjun8dER0GnMXa1cnMiQYmnbedAAfbt +7JrFTK3RZXM2YfGUkXUrvxiu2e0ws7Zt4PaGAMVMv+uz7tDhlISbGwf4HxoN4NNJ2zZmBTQA +3bwLuSNzL1GSmUsxiojSHGXrCs6TXzsciQEcBBABAgAGBQJR5FhrAAoJEFR7AJAV/jtFhBUH +/2nFnA7ROBOW2dlby/u4Bqfwi3HhA123ZrpIHq1Qoy1TWoXFFLkE1kSmYY/WfdL+aQSzsufR +NbCsWGR63qhXW3V3FrCHMoZL8M+ItjiV5+ZGLaw/h2lzIl7XqwNjAC2ygwVLcdp/DRki3Y/U +KvlLFzA6bcnyGLcKKRD6ZLd1d8Xh7uaP5WxK2xCD9+X4FvnXf6YHvHfUj/NfydXqlGHo57hd +X+IHVjCRIazNQ+o75Pi5qhOnXpfl6Byup7Pe7MpMrNhP9Ms29xlJmodTYyDq1HJbtQAAQE+U +bXpFnmUYRHH2IjpacEqsUAb0Yep4mlLuXFm0sYJABJW+r8i+bAicB/SJARwEEAECAAYFAlHm +94MACgkQ7ZcPmWbP4F5tyAf/e0fWvN+xR4MhxG05LsFHG953HWLQzOI4mzdx/0dXpS64J5iv +zamzsJLUMcJ1KrKv0uAHUDIsMQHCGVA31yc0um2x75hJ8KS2n2ApHYKBdp/W5LuxgYK+wkz9 +VJz+fURc57u/H0QttpX4xldO3OiITLp52iA2ibYIVotuZ+WS28ae+sCXN6YbFfoR5kOnePgD +bjBerxSrA6Xeg6FNzA5x8vs3tJs2iIRYjDm4STWItIdlbuvs7kKH/JYnZ6YvcHri0iWgMKWz +/oEgWP33feBoTmwTeFHK7lMOsyuDpOLTfyMjNezeFUDdmEIfS4MnPyB7CbMLz9m9hVALgo34 +g0DqiIkBHAQQAQIABgUCUekK+gAKCRDyZfAAHT7vSjR3B/9qzjQya8LLyiwhzSB8jmIU54Z2 +yqqK68Q8dqKLPlxgBCmc45ChKyJqrbPpqgxhdnDd4TfZtWApa52GJUgykq6QyUmZFs6MqptG +z2zqQ50OOzs7IBSWPHTXZsGCTif+GhnKJrNJgyzA6anTNGxxtFkzjOIp5Acw1I6gJfyVgFue +HzbMQG5m9hLo3maKaNihHKKvZjkJ1u6pZyg2J0iMkx3WQAFDIaQoqsENix0pEnOYjK6FJIEJ +rJXaBf80Ue6XpN+VOtrNTqzkoO7NAeER9ENSz0P/ITK9MrbCq4Wk5st4ei9dd4D1O85d6svu +L9GC7vGPF4XY/k4LXB98yBo8C2tsiQEcBBABAgAGBQJR7XnbAAoJEHT43GBDjSqAEk8H/jWJ +mv/EOEf0s3DnifZBxw6tV7d6U84bCX0rtQioSTkjqdGzVrHl5XfUp1gCaA6mNVg2jM8Cj0+X +GMvWfzqnLTATXhAn+ZzP+dmfOgBkuDQik84MWgiH/Sj7By4ltJgwiY+YpOY6vGWECCFWO4BT +FQu0EL6PoLP7keTrdQ8XtdEl0PT16ovdqEOoCQJhttH8SAuCa94m2tTbz5Zw20p7sP0V9w3S +FahujA74fuIF0CKyI1Xo7EeVlwR4x3FTfs7UmxvFjwn8YmB3JP6Qc0DZX4xvC9aD3rW+a+J6 +59p1bGmvf8q5NzXztK2T+OiFPerdKB2NXHswJ4cpodLBCFzzibqJARwEEAECAAYFAlHu4OIA +CgkQVieLNxEy1mKVvwgArORXlVP/8NFIwtovZJkzj6O2mPiSXTB7W+S6n7VqGr/O/nKexNGS +Yek+59rWJnufetyrGNXfI/6XjytuBbiAVOg651UF7L/tEcXdpCXF77apbITIOvSxIgJXyra8 +q64OCX4QTnjjYRVy98qGg9Yxsd1gf6Qsm7u4IZxksv98ceQjaSGlgZmoh7cEvXhOqMPsiZEs +Fi9ohPmCl1yqBK6X+yXqL7eALTyz6UzNbPZyM0smZZrkjeEwmjdFBWJO4H1a8QhOJxSKlKty +xX6k3e/5pQfK9UBuroWkUKQXb4FBjpku+Nlw+07K4jJ0PV1uPxtCcK6ezXoaOQ/2dgZEk/it +wokBHAQQAQIABgUCUgJF4gAKCRA74uY/uHzdzGbiCACbhjSlPLEyej126Ei3G7YJWFBt51Dm +Q2NDU/07XxjN1vIpLJyHId2BbOdQgnE1M6OTQ54hjuKlKFYoPz55QTpvi6nj+wweOalHON5H +/ZBGx9xZFq2oYNXHaOPQb46qKEyPuU9fSFNZps0DOHfS21TJDFgFGJzFVi8NHNNHo0OTDbKC +p+EVvrXqwG0WGLnW/1VRsHRfvI8CcdLUYrpKRJD3DYSJgZ10VdPB00ybItJHS5St/jjS22H+ +fgivGMPwsmLunrjXs7DY3cY7DhesoCr4mR0/jmszhN0YpRtvTNeK6QYhAas3tOk1gfJQjtZR +3rLD7D+F3lIuPH74ujAOYb4YiQEcBBABAgAGBQJSBfLjAAoJEHTQ/Jkd9Gw2+UQIALHrJHaM +W5c+CS1v350MACQ/rjbsG4yiWk71XDRmuOPFEeG8TeN64Q7PRNBhIPfyUcY10cVK717lkj/y +jbykD66yowN35VBkewPHzZLrznVcBpTpwHjytCH+1/YF4HvNSMHsNRpjLZ/3oy7hblQaqrSy +h0M1X2U4LR3MEck8q+ki0n8Okyj8pkiQ3LfheB7YZowV9dKlw3PUIeVEWp9cwY+9RV1/Lhtd +Qyv4ZGHQYrK2wJ4V9P6ALnuChsdY7cHsHq0bhT2FGGDWyCQPtpzrlkr3311GJXOsapg33/lW +jU+kP0rlCmKHBCCUV+R65inuX8wAVpzm9AGM3kWORN8bNPKJARwEEAECAAYFAlIH7FwACgkQ +oXATbFajGZXuxQf9HRtEEPMM1w1p0CkdLb+5tlJaVu6s74Nw73fhls5qbFRHKi2eRoV4j4os ++rCVOdvDzTBvkFnFlnsaypTErrMNwoieyfc091FONKuNxJORNhl9lNIIOsy8kKsslQzzesUh +ttRInclpOn304DKaRwmtk5bj6nEmqIVdanfW8pRLyZ32aBcNMG9CJyVWLlAy2jYYg3fsIF/G +GkFURJXGoLd7lLCx4o9D6s8XYqJlH4pC0O6F5PnzDMZ7w0rP3R4wgnCbvbwCqZ3FbMNfuM8K +WzUzJIe4TcRGfDw/YGgn/9CUjq2DdTCAH78yZjQ7ZmLDiB3H9G0sd8KDC43e3h/z+Q8xUokB +HAQQAQIABgUCUhcnxQAKCRBwsUi65jRVBQTqB/0XEjPrElnwRpI2zKD7cd2Z/qWfZ2CIY76Y +qP589NFiBePhqTJX2Qsc7OkyAxibdU5YW9gbbqnBO/pK/un0yMkj0wpoZKWiiBx31n3KT1pC +Lf2dbExkW3hRTyUDrv1RYlwBuuj1hAeFJkPopn4eU9C/aW313txlIV77f0wgGPSPZkaA/v3N +AJFLnk3HPZBekq1WOtMbqg3E3RyMWy8LjGXxcxy2KA04cEwK2DKy0WyjKWgWFNt24bjFGwBy +JFsoXaPbBK4NzOmeFLBy9YGHIcL+ILS2s9Bn0I9l0dfeE2xkBgbvBibsVYGuHZLj7+/7yO19 +TO8NO3opuSiQAB09jYFjiQEcBBABAgAGBQJSGQIfAAoJEO6XheIHuF47K+4H/0DilkDI/WGT +C6cGC3cKGWa8+5TmIjO2Rrfl4MXYZnZWcA9pCSEHbBHVqfNTwBuoESL5OQMvt8Eqtlj7rAoH +bN05Ydl95wOkbL5EoPCmicbo5JE9eTfMHn0tq1ZjEBkC2n1neLz1kOKfT6dz8BQVlGB3aybo +ZQ7WY+gmXrsknZIA4rmt5p+EJ0w/n0+MLTWbI76y2SjDhOr2Rehdfs+saCt0VeC/eW+Pwx4m +njNd4Cdrto76qm3DoiEbb/Py7xxM15yG3jzRHFrN0P+4ljdDxKWlmwWGhYihz8hxkn8FNy8C +B/kmMwRC+ssmUcm3RnWkWGydynfxmnd7LhOW9MVOY9OJARwEEAECAAYFAlIdJQoACgkQqMm2 +Gb0uHRjyDAf/ZTJZTTGWAmYwZz+DA/7knngJT8GL8doJvARFhSlmo0xOzCP2ykrLNFL2h6OX +mQIHSqzevvfjv5MVR1MQ/0wwphFsfdC2KcjYHMxTCwOiAzXgCFEMEH3a9fqpVDbxF1ehHAKE +87Bm/6IOfqecrn/jPmfCEEAKbPuwTvP14kipm1y8rYEr5qxg1HRvEzSyFO2nNBjF0Rx85sfN +aj6qk4Vt6lN9qopm3hjByz1OohuWk8niqaZyoWgO7PgJ88UOc+M1c7/3mipISaWfKB+f5Ltb +Vz+9o6cTyU1BMAoEwJ2k71+87o36s+Bs67lpxSoTsSISAV+/2DNFLv+dZI1OSjAc64kBHAQQ +AQIABgUCUiIFowAKCRDOAexgFpkC6n2aCACDghIu3jAPiCoqGc+hlVK9iJC8NK6ZAZ0kQDil +X9+o1lJMoojWLR0TATRZ7NBkS8cP7Za+c4Uubj5RjrBAVmTEr/em6ZwcR0ZNDwhNYe9gd7Ov +A5I9wm/slrTfzqpV+WxPTSVOWoWAW2zpR+mfL55njAqqNRvM1yD0S09IzHU6svhKElH8BEf3 +qvwSreqyw4DGuR+M9QyKnrYrYIVKdUMgqSNd/i6+xBKjuvioVGY1wVpp8dwIdEY6FQxu554c +y5ULw6SIOfA6zVkf0gBt+bVsoqjRDhmCJVzsnoNbjtaOPi146AcCF+vPq/Lkaw+t0D6D+gvY +YB7XrWL/Pc5ReuouiQEcBBABAgAGBQJSIgW4AAoJELyK171eWQw7RUYH/0ezM1UtekdpQrbt +IFUovYXq0v3E+wShR8ALUSmaBbHUfNaDKr/QwmwjiX2WVdiZadDF0Mp9fYLyujPbWf955A3N +eqingMuDNH9nYhZ2PbQnWPyQEn0nORn5OsRkqK6IxsfFUMA4G8qa+cDigCd4IgJtzUqsMaVS +1nnbJS7K06i/OJJ6F70nr3riNANwR7sV6RAfjBW0QxrXt+ElskHFR+ATrRHujSHpiEUrae0z +ysLW/ZLnKE1TrY675DK29f8R5CHv/xTkpBSXSPHTUM2BMjKM/9ShAt7K2HfRX1VnB/ZS8MCj +poL4X36EYMWN4z7igPYC6+u0caimdaPF14zmLgCJARwEEAECAAYFAlIkThwACgkQrdp1AmdY +a3BawAgAtO1fjXrZ828VdNOn8MHNTYT5Y6B8Jb3nyseZ5WIGeH7DmeMB9kcoeaLZ8OrlrAxQ +HZu+1kIAQXAaU9GMcrHbznB4NoG7/5ubO29NeviZeMoGbwzsHWeQIiiCiKQCvNEUGDwgzpq9 +teLMRL08hUD2KEuFgH66GQWK9Spg/g3sYGblw9CL3waHsW1Pwsr33NFxcOPSe8PVJX9PuI7d +E9Od6M8h9COimXsLhCKa+iSauf/tJoIpa4Viv4ShX9yRw7RvC7JeGFYJ75riWJb9k8wMF0UK +GSvB2850yEd2m5/MsyUFotNmRm0mdRPAN+J/cxnQq5y8Teu/xHTu3zdloKKbM4kBHAQQAQIA +BgUCUiRsbgAKCRBjhALaiWq9prm/CACqJgGhEPIrmAG9FuTJo98TrSL0mHJmF1GFrQhcQqh1 +vBYRpCX4t/qvVP+V2yfDnJ2XVaAgweiUnQNexUqSwD1LqJFBnyUmiYc+WCzKnGfo4hsWsVXR +JDXeVENRNR8h9eik8W+D9Wgx7QuM5ZVAmhnxJnhiSPUEDVMvhpKq4kyGaN9C2bo0NpsBMRcl +aqbC65NzzaSpT4P0raWGOynOzXRJn07KF9EnQ+Kle6tLYtrmpR/LQwXDVb7jayCebZbaRZ8B +zPK+Z2MCEXFQcPpAAcg2cerKSaG+py93crDmcjWlGHq7o9FoMG9TKMAKOzeNwEag02oWOrZ4 +fGUer22eaYyNiQEcBBABAgAGBQJSJ68lAAoJEDjCUpDTp+PJEEUH/j1pbzVafhsU4de/Xlas +nBoDTbQy0brhcTFkrz2l2wsBGgqzfbkRGFo8QAx5noKMA408ZeH0z6dDbX8a5W6tXWymBhGA +OGaO4jMfrYR+lJjkJUx1XtdrXAGqQ+HWUxgTbQ0pdiz40iCJyA9cxthOe+zEboVHkMg4RN8w +RvvVlwDfyfKyQgakhvEpirNan+GWWdSrf6mH6OYqI6y0or93CtyEOxHpUOt/YKVUumwz6QQf +1fjrwbO2tjxnWAoF58FDsWxozed/syRkkxt3yp6v4t5LGvby+R36rbh/+7E1sSwn/xOqUp5f +Y0tR/5DnqkfyixFAvUVynLBi+EkU65PnfamJARwEEAECAAYFAlIoCZ8ACgkQDCD/JsAJHJnJ +GQf9En8A5BhWm/kx5fZJpwnlZ7V6PzJU56o7ZWgeERcglEZkAz1uG3vAtsykEnSCpbI4cjUT +C2QQfFJv9rjr23uentC9I5xNoMy6VCMlsEt4bPV09Sue6A3u0oLcs/Wju05mfrYnqrOwo+40 +9ul+9LR2fXU09LIqz1xmRiOgiN+tC/i4q1w4phg3SjXePaXC1rSm7IHZLY4q/yyWciyh8RfS +wvdf20Q/AmMGbhTdbUkcCk+t4FLYPE9LhOWTnHCNwX86gshfBnodUB1LHb/3bVuNs2ln7i1+ +Dtv+m5vsA8S7SzGmMQYGcFjaVaHa6RgwLzg4/xDLN41CJri9bvhLS74Ai4kBHAQQAQIABgUC +UiubpgAKCRDAm/DZkTx6+MBLCACvHOtWf4bhMGpXV3qUWUMJFbXiv4+GeMydAj6GrXILq9S2 +u+vBq4tqeYdtvUzXP9FOdy4UP+ZOoh1i1Im1IWRJhuRmhs2G4p/eIkJjQE7KRXrXwDTEQlzy +EXcSHCCntMc/kDe9C49KsbB1oL70Og5t/hdbMVpqmxVCYDHd/siGua1VEiRhk/X15BllAqOr +8G+BGgt/HkwwoM2gi640IE3CvuvyHRurRwKMMSUkHXyWPT9nQ+RHkbWg6B7ol1hJpHaqBf3t +NWBNknmfwmYwKYgevHEG0ggYnY7vbJLSu6rVV6A4pgaUzKNwh8nW2mz6f4hH+g4uLiinjzUV +O9Vgs9l6iQEcBBABCAAGBQJKBM65AAoJEGjoO1fLiqD/wmYIAIti4ydMJKbEKkni1dkxdLgx +du+sTVzXRPW99tX3r3Zs8sIYSTUZZ0AZCU0ra9+V/h8QT4Fpwsda37FwZB/AFBL61IsNQ//K +he03mkCUGl6gSXJ+R+IeshUmUXVybnU63hr+c0wLMnzADtcwoIuYiWXRWVCfd1TW8QLN5gnO +ERSRx75JVnUS25fsn1cRBgQsw1sthAj+m1nZcby6AsmoJdudyBRSfNFe/NhQXlHdUVQEF8YK +NP1u3IBXFrPC4zbd9PsQqS//exGScKcE0/qzPZC0QDjD0Z4OOR3/LvndRMN28+xpaVAl0aWR +87HjyPt7GxhMkjDLRpf5JM82U1Hvf1uJARwEEAEIAAYFAkomf78ACgkQmwAFc+oKX7L+Ggf/ +fYB4uayHM649MgHGTe2D7bcDlrUNmNQ2r70f4Ad3bgaj1oqqfgq25CWvH+L1rkXEPKIJ/w8m +qZuBKu1k6shTMVX0P5zhFQL3UL+kChbCPlZQL07tPCApGhsK0LOlAdnBRWq15IVktMKdq9R2 +PWOtmS9d0P35tv9RKuiuc+sqezQWw7NeTfQzD3jMW6rgnRysZQue9135KwYweJ25uIcg/nNt +N29o/CisGWyCTg0g8SWqAAV7ggSpqdiEQuyZdkVC5auXLpj5K/44e78CQ4secWn/nKTvwMB5 +GhM6ZCrtjy/eLQ4u8Hsk7zyk9mOIucfOo4YoByp6dKE2B/VZt5IDWokBHAQQAQgABgUCS3vq +tAAKCRDPqH/hU3mWXS65CAC0exrOQgT6Y1fb/lA69kKhi2J/hCL7ytq/LsoTGxZuYQK/V6aK +E49Lu+02cMb8vdrlvJ4avqTYFDi9kZwoD9IKUACI+hbtmXm96pJoODZm6PtD4KFEtBGy0n+U +f6wOVwb1feRDZMuyIOzXRePjHFZIYvJBLGZBIRxkRF0EmSqKAo+d+32yfojS9xuMimdhHQIS +4YGzW3PUfVVnRY/HXGkqqftTUJQMdVjjVpqdTnh8RngjKHC0m444nCQNfpvpB+/X/gnoRmDW +iuiKsDy9ObVK6xW7+tHnyZJxSJulkK7XzD1rx6FNoid+ejrICWfmVP4t8qq5lhMzZgDw1RV5 +YoIWiQEcBBEBAgAGBQJNhF/QAAoJEBJjlUW4oI4v8w8IAIjG4CEKwngPl4Vk1VPJMD+maA1F +2Qxks1Bck3saRo3Q5EqUdgKPJLltzOqfPBfOzoeZ+yuuFLBBSp+OT01eAndQcrFoqtFZvHlx +Dj67mxE8hQgMYMiQtjkD15z1ZWa8B1nhcVkGUS64AVycv+EIkKLi13a4VAsVv9eKkBvaxmPU +rSWk1tqe03MySDKS8HqWN3Dq4+PkZt3Y8zdVSnmseKd2Q5l5bXoUESdGu+er/fEUsXSEIt2T +nYe7JnhfVVmDUCRK/HeCcDV6n/jJ0cj6Q9zMqIouAGh3zizMoPBpudoNcSyH+hzM8p5KBUix +OrIiEwe8KZQ5CetQiFoCvh+15HqJARwEEQECAAYFAk2EYVsACgkQEb+yrRzuPBclegf+IFr7 +Hn+fvmHTrnTvjNFdWB2+poQmvbLpwllwL6/MbK9e4ak9g5Qy2dI4ks+tS5WiPkQliKeny/Jj +cQd1/O8HEcCDq+pWv40wpxdLpRXZ8qqEotEipmI0BmBHVkMqqnPXzYU4lcN+hFGXiu6l1JOW +05K1FUZ9eN4zW/pgRkS8y8TuL1J2OF0UL13hhx9P0ysXjDvaJR7zEG/XCVGtzyWCJPpqx09C +5mscSXw9h9aAmFVer9tWQHDdzGgPbsFW5rrvgQeMvewPHKfjS+N/PF4h8h/zun0ijI6l1zeJ +5Hh3OtmLiw2EkXGjg/lT5NSlsljAdeqWOUORwUOtl7X7SaapaIkBHAQRAQIABgUCTYRvvQAK +CRA6rA3f1MAzrsh4B/4hHGIG3Ly4ncYTNdvUYkfxaBFYpWuyHQ5s+ytIwyxot8KGR+clVYdB +FXmmNrCecapqGAo3nu/E2dmiyDXqaeAVzFY4gJR/11MF4nU3hhnCV0PMNb/KXqRoGrwzEdTs +sNlJG4vUzBC5DRMg/0BAHreci+k/tGdSNUebyR5//j4axUABNPPoWhp51PYOWUKDJldffOJE +h2UeSVyLcb//9gVqTB33gOMaxHvqHuFvqQhsorWkORiOzzgVR0CyCH2qLPQKGVwHlwSnopF5 +mrHBqSFJp99wtpnc7N9Z753/qktQllz0DqHmfJmaW+CvTnZGqobFJX6FxXVHKLBEQ2xqPP2y +iQEcBBEBAgAGBQJOIecnAAoJEIoZZJro27UV4xEIALWJ6Xxl0V9cCSwgNKzjUzLppFx4Lw8o +PJ+DbC8fGSwYaJ34eJbvCby3f0SaBGKTSTHQB34wSznH1rxNGLxkgeRfXr1XvNBGq9cayWl7 +9bgHbplVJYqUsxcJAQUAVj+zaGqUqWNTxKN74eXXof8mKZHVkScGG5VGUMdm/Y22M13kZMTk +ay+EDyAX0n01NOjtoi8HrAQ8xmZxYqJ8+r78KhyxKTBjlMx56R1Gj+2AlTaOMJUt4b4VAIWt +B50kyMfI4Y+ttZ6gXNmwSG7i0Ewr5gz64E9kCzEz9XsU6jJeDuKUcNiUKXkFGCCbIK1c4wNN +owxjC3AgtAGnqWtcVHygolGJARwEEgECAAYFAkhOgiQACgkQryzNxfXa5leUqQf/a2/qlnEr +4eas5uXn+vlBiPAv5Q7v6FOZlwaVuvFF6YV1vFAvMjuMoLjOFQdfDIbFywGy8wN1o+fULzUF +jTdmd0OWS53118WYDEj0UfZjXfplfC0lhdYdIbTnYwfy0H8/riC2UCmmmomuolYY6aV8Ccgj +TFL6xO8siYDFF1GZ4u6FUB1RJ2fKW+YjoBSkMIIs8o3IGmSLqB4mi6ziLZkA5YyAI+tdrzkd +mr5J6OTNj6jprd+QDpscG52D0I62M1BMO0Qxkq+3qectiEYD/Jdz9UrUrpOgMMzJkCy2d8pU +JGZahx4HQw53ymE0rBILvO2OCWfvdYxLVdxSCAJ2SSCeP4kBHAQSAQIABgUCSGGm4gAKCRBB +C4Kjaikikb19B/9+2bbYFLeom/dqEd8iIcfbBNnxevRXn/4VKJ9zyPfymc6yb29NPxPfm+0k +ENgwaVuAuzHr6Qv8yWHAHrr889MUSSnyobi6WIklRIXV0UslLaaUpMp5Szq6nryCdxFzUIRY +z8vKspHD9AyYZgYSQa/YQ/Nm+Nc5fdw5eW3ubtzUpfrBeDTzXXOIfMudC5IusHroJI+9pO0a +vG065fbavuNYKhjyzO+sCAkRizyndRbvwUPBMD04Lq4na8UVRsQrAdwm1WvDbSGT9iYEPiDx +zuN70j3OxczXxZ53TPg4QxA12y9WTE33dIoUVyh9FDKcmKj5lrw4sHSubUhVISQIGMb/iQEc +BBIBAgAGBQJJRmWxAAoJEMVZKsuAx9ZHLqMH/R5rGX5zAl8UgACVQ8yQnlHcA0xC+3BNA8Lb +tfYy8Yn2Vpv33RjL5b5DMHRwoe1EXzHSgakTNysogP5N8Vi3W4n0LTItFBKEYuWHozcmWO41 +6gVY4+QVqHVGKewz8IH0SzolBqQyWKyJpASA0P8McGgf+I5D7VKtnkJkiiwf50/jk42gzqK/ +cnvvUS9mub2C1NqeBNZEVpDIUKlA2y5ITBAgPsXnlQXaRDlC1E+UEVekLm4oCMMWi8C+qGoi +npOcpSIV30VUqiCRBK+WgRJ5r11rFyI2plZ2g0RKjIzHCq5KqsLovXlKb9wGsU6S4VmlgHbO ++gZcM4Wmw1OfvSHTV/aJARwEEgECAAYFAksoxykACgkQAPADH9zusCt0iggA3Aef/A77Qyio +mU4VoTxRx+JCEU+A3njhP47NUtOoH4aSz/CHxxLBapBulFl+chH+pXHpkWugmbbLsBFoksxL +VumYrRHCDkRhr7hRW7lzIucomAcv5hgVim+wgyZbgrbbEzspPZ+wY2EGsnelPaEt7j6NJJWg +YXeUPV2b0eDf7oWN4eaIJVQ5pED3L3td89ACIY1fK6FWwI3KQOugZR8EG1CHZ/GGM4cpDCL1 +GrG5CX5J3LmIAf4uoVFkwJzVDVNaoTIlNnlxZaOxFzzHvkaJQw20zSg47xThTfRlx2xo5Rs+ +B2/Zctcjt8er7Aos/9jDcxxapAQij7dAx9nnWHxT1YkBHAQSAQIABgUCTMqF5QAKCRB8lMCh +SyAdG+UAB/4pIjzl4sWTq+h4LDvQ74nTryJ9MEtuxtvHCKtw1IT2mBn4YLNGcGV9z8KT5XVp +fcs8Z5E/+ti+Q7aVXrGCPO5zp9Dy73/qrHSQK1jklSC6eRJtzSi4FdTxYZdKn3qE5MTXWd8k +I8YQghFdqufTfHrC2nUMitEie7j/JZzgsRAG+uLNEZiB4WtfZHMQb8Kz8orcEGhPtCeAFeTx +eVxTl8C8XqYst57+MCiVQOKPq1OzgicwICzkXfVj8A+6wqWHBiMeHErhEk0VurzovofNUmrD +N1pgg6ZUqhYQ/zE5wU6iiXeYYDLue54dlYB0J0nV+pGBpu2L5bYYh5I5vmXGYBVFiQEcBBIB +AgAGBQJNF3iVAAoJEJTpLfkqqlw7YFEIAIZCe9wquqtRDQGnfdXtBOuE3WbSdOX8z7YCysAV +bdhVTLPttgDiLVU5vFlnSmStzs76JnvSTlTy5EoOntFGO9TzXraGbMVdz6HajCtOungD3mTH +gAqPbc1Z5uxOtsDD33U+C5Od2llF/WQnW74t7ez2P4B35FZ3PJPcCFCh3e92z0vYMC/ZVyC8 +JEnlmodxJ66/nv2gahsFlOWaeLYbhMybrPuGJ1FgvXeKFqt8hJWt0hIqS61ue9c5OQpqtxh7 +re6a4reDvusz1FDccJPMS9N+faS1SrlasVyMwaNLeY2zE9Kz40mgCnvtT95NGOCJ+2m3Jrso +G3EAsunH9QzDCraJARwEEgECAAYFAk9fmjcACgkQzCM1XGLipjIxowgAlRKn/tO+kyHdJE+D +YoTP/VEo8Kzd8h3vLZwIQYzXja2JOT5BrIOLUinSBwPVGrUHQvFNL7swXac6hph3Xhv39kTl ++JNaZ7D/aZnio8jFm47jD2Yhp0vy1Tg/1xmwgCaE7V2A45CtIKX8Wu1OrNmSiRy6EDf03KS/ +P4iAh7TA4mIDhAxwVf/I98RQ9l9zguXS1HEBDQG2iq4bOdxJ2BTfW6eCl9pvLTG0XqEcERlh +xCTJRUIyMyUAOGklihziQ/KEAsRg105A25zlN6YZRHSr/cNCRT9tIsbBapbCLJijXC2fyu6S +XQBvPR5Yy1AH2aHovr0xXMbPCmJePaD6prmhZ4kBHAQSAQIABgUCT/IOWwAKCRAGxCqhKOqQ +3dZ4B/sEn9tCdML6B9qfzI/rcolsS3DpUwdUxnDnFaCQn7QDx59h+K41r4DiGq6cLDZWE00N +sNWVlvJEV78NGXN9TYBZv+JdhcWTWjBUeJOs+m+XOTJMlr/ogY1tB2lV4zKr5jV9ZCv3zGcJ +7TDRCybQbwhI2QAYEHbKOboCUQ+6i2xL4uuUc4Bpw/DshtyjApjX/QxnJgui0ReH+wqjc50z +9HkF0T6SqoSC0ihf2DBQnBzvJei/nHpPe6rOoFICoihDizN2rlq3uHx8NMHDVzpakY9Naiq+ +eryWBP9hWPmBTvVm6n9BFTTgMonJ4BuLk+RGySThsnl+ttvH6ydDjLniscg6iQEcBBIBAgAG +BQJP8g5yAAoJEK5F6wLdrDl7vjAIAIpjbcnbZoe8fVW32l8q1ezbVH4EpNS0WEEJsJkgDdvI +nbTf9O+BkXQ29iZwhqqsHe+mPPm1pj9y2X+gG7CRNc5W6nua29yofMe3fdlPCUqdA25DAt5x +z4j6CXGP/vc3toljfsbBTjt/8TvVkEWS+Q2klP4s/3Y8Vxcnb0zAnGsDbu5hZst4y0JXnH2A +KXGVlz0yvW98/gVyDDGtyABwsGzYNGSRHHfNoKFT0hI7Y+Et577k1EFQpHskgLodLg3geZ8W +Xn2UAaSUNQdp8H3ETs20Tji3Fi230cLaaNoaYWFD9vQlfmGy5S7oisT2OYUeM/d0++9I1/0U +38lv4uuk83WJARwEEgECAAYFAk/+tzsACgkQw5qVypVWCZWJywf/YJH69HEEezPIUrdI+Muu +Tz/uvtuHWCnMT+PIAUjPfn36Bs+oiMJbBWKpuE8xb+EnNWQ+xKcs6cEfQly2643eHmDjAQi4 +9erQMZVXSGZKERyG8sKIA3AlpiJip6GH09rifGlXlounpjrPbOXaxRmagqBWfT3aZcNFQxez +n4E0h6RfBu1XT9QvrW0fFyrTU+iIkwu9bVGHtROd5RQvM9+qaM0CxEormttR2GWoaZF91zQf +7g4MIFNnweGUzE+uBANS1nVgGMSxpdM6HczfkV6TlESV8K1d2UpJrg3dc4Rowsxdgtuj4KbI +E1mnL+ZVs6JCkeZZcopobCukvDep41EQS4kBHAQSAQIABgUCUARFUQAKCRBWGhaUk6V+h3ht +CAC5guYsHZ9tKilTiSUMIAoJwnsvXCr7cysRxpf5A0B3WKlEXK3A8lmF8oZmfSpLz/58AzRN +7flqWqMijbpwDuKD8ljNyh+tqd13IWQ+EWQsCqU0vfxVKs1xG49Eg4clckfLhwO/57/FcOPE +8NeVaYJGLwHSer3qAm5DMqfFSlT4/19EyKXLwN8Qo0Z7/IcPS4pGDywFJxWfAsVKkbWvceR5 +V5QwCFjGRwfLiQEh0y/MB4tiz/Z1cykRtXfMq1A791ChWFtGHDYBXrv2TqNzpRz91nMLp397 +ShuPEwjTfnsFoM8DrOv3o34mRlG435nak4BxlpQfldVXDRtRmnGNrXceiQEcBBIBAgAGBQJQ +MfKgAAoJEAkoCB3/VtEu4CMH/3IPCXWiUBPXP2aF2RONuv6CfxWwazs4IcoZgeKWLIaftXwH +kYPo6hNny3Z2EGrIbckqUSOpzVQCB3+Eypyc8Bb87fYY49CxgS+bpopHKhJPwTR4JK4Hga3f +dqzAeeCDTzaGfhxoapCHE8TBimsF2WU2g2b01jQLlfEw4f4MjyEIHUJOftczR/cNkCQQ/Pim +mopIzEtsc0etNilz4KDNzP6momD6cO5eu3junMq9PHpi+o5wMw1BCLxLVtFynQtVHkgYJDP4 +bTE5WLblgwucNVuRe3P8SnZVANjnIcHqjErd/1iKLv1Q76+L0fOhcYeKTO1kdqXnhdKg2B9D +v0OEHmeJARwEEgECAAYFAlDO2okACgkQp1iYJApv/9IXAAgAho8JlyQNnPdVRJ/4P9JQD3Kc +3RVfuRBF1IHJ0BQMq1/K1Fi+lqqw52V4fGywSTb33WKPa5lLOxNOPn1Lm+AxGD3AV+sK3aIB +VPARvwBdTZeZGCbyPu3n54ZKnIHJne51GzkXpJ91xpP4aXaA02aY0cplbL+Ff+RYFcWmo8ne +Sue4AzYzOOxZajDAOpJQNp87rEFtg0RLPT2G+MUM5AyPAMorstMe1sUAcYDXWsCHGcvg4kTn +TkeHpELSz8QnXoX8LJH515gzScc0uP+lZnJslwdRrn14kt7nc0YyrpkJkRujFLd3bJNShOnQ +06HXePcH6BlzawJ5wdWuWqbRvfNOiIkBHAQSAQIABgUCUWAQzgAKCRA1HskUIDE+xgBwB/9Z +dO7mu9X0MAu6C4uonG4Tg2y7fORhZ9nyDjjD3RIR02lK8+guwQQ1it34Clw9XnerrUwbKDLp +MezHmnuyjnxgKB9zn3tcv5QQaRZ5hlKdpGGIXVzKkf+MvARLkS0PCVxVb+NZvddh+0cnMoEh +PKTVC7RfORSETS8Ip8VVwcwvsI9bjCa4FamfFrDfUxo6itehAr6k890nPl3s12EhLlCyauqU +gmpTC11XyG1jWoFQD/4xTj8oz8DCR7REGPeDSSAVKsrhIRJ1ECOlVRrU52kSHKrdOeoGfw5o ++w4lXFvzCB1cGBi5x4XlWyw+xW+/ss5DpN8r6bgzEehoNQi7/lw6iQEcBBIBAgAGBQJSEuCd +AAoJEH2x+T5dv3XiRF0H/2OkFvAv9/MCw2D0E0eDDUSbL8BAB4vRbhLXQf+RRMrnaXTfrMZX +AF2ZzlXv1BFOmO4VMoYdh3qqkukUdcvjhIoAk+p9JPIflKFKPnS2AxGHtoLUJP6VAEscgLQH +Avh9nimmK+FOkP2jD9d1VeWKB6BgW6Q9UTLWhfvEsRE+M1YPgdA2JZawbsy0VXzwVxmxHcHV +0RqtAlTRBk6okQ9FKZitQg+WB34T5L8vK/ImZ3fBdym9cSKv7vMbbYgQpp4KhH96GHOPeFTg +c5NUQrDvlj4TbTho/1c++7oTZKMNdLqU5FZ2xTonNyBtYlGru44O6xlWywcKzbEeWcuK+TQf +30WJARwEEwECAAYFAkNpHyYACgkQm/9Hnubf1fkv8wf/TbbKbDisvlM+q9rHoKGs3dSKkZ8G +7mr44kN1RVU3RKPXem1XtAu8QpVf3bgXthaRyhAErzaRoJDo+HctfTDzmv6QU/pr+lzbnwfr +35DjvAfvLv0t088IY9LhEHwscJ5YyKKDcqFYjnDeOvCYQi9eXXORPTfDjJjashKHj1DRmBPD +la4On3eo0bH9PWBOF69aIKVXRbPzXvH25Vi4eD+kI0zypIoheC391U/Be4HKTdUd14Xxhtb8 +3UMlLvDRAlENl3tVUsXiEjYWCygmqK9WX7aMpvy5sgBFtAaTr6M0qqYohtCThSmSLPPVk8AF +hjGawkc1wMSp9yFL5BFdtaQSQ4kBHAQTAQIABgUCRWgXVAAKCRDhpJ3uk4CaFYn1B/49kAMX +CWebXcqwwk38omI4li6BUMEBtXN+CfV9I+kF7Mf/syaNHHghGKmcbgmWNToI2NHpCZx1lYpg +kOk+48fHbWyhXNWVVQ4tGHE+jq51fH2TBZmlAzqu24uwMMYQlB/M3+M4g5Yamvpkx59/8wEz +OACAXCvsKAQzsHoyqUGtXvzGVe1UPGp+llhlF2b7qkpy5piqFbkdcssRkizTvYaI5aC13Z1c +we4mAYNcbNzDowxY8Qh4MZcnDkD9NFCtaQkxAe6+bdE3t4hCpJCSn6NLkNgaUQLJTR85GUhI +4eEgEQe9mUUIb+lRClood6YhoAkk393Gab1Gdyb90bmoaGlsiQEcBBMBAgAGBQJLkSy7AAoJ +EPLMJTZMSF1G1NYIAI3fyBRrDLUHg9R2aMXgIAdX3TovbCM7VdpPoCdM23zRoj612mhESL7S +4pLIY8d9z21SmlwLhtrE9v8cCkZ/1tYgG8QJk2zKmjVj+XHQF1Lo1WXAHwtHl53cxRRhQV2f +9nsH7tPs7+6zsMpRD15Kgm63CP1pDqqM60YIEVl8iApECBcfJKvzZ/bq0K7ejkenAGbKxCTp +AB0KUnZU2wquUczg79UR6o6yvZQwJ+X9X7bKXJm2ZxhOuq/yvCZ6FjAhQDPrtVW+qv/WOliq +whdbTTxRCYybs4iwUF42RSqTsKRDPxM3QnkZLreEVaw5m+n723JoBlLuGCL88QeaZXhle22J +ARwEEwECAAYFAkv628oACgkQd8cFRABP0MxuXAf/fVsPITu7CF6AzHvn6bDdL0dgWKAOjYly +OwtJG3+nNaPB3YSvHQUkddh7EwyumF5kigObH5HWEwvfbYCsvtoMafCi87dy/KksKyse9aj8 +GML7L5R/mQR4geLdzTFJGi0KJfiyMOEm03+iwjg142YBLwHlP0NKhVakgLZ8fJGXWAK3TlIL +ht+i81quXmlwQn9x3BCnn1UDwasMHbT2TIzvHSvXtRNsBzu5V+lugdiwi2wdNByfPWdAocAu +ESAWYlwg+ePF3rMG3p7TlGldPP/kkps8iL8GVUlCnstEuScIRU5W/vS0I3P5UDs8X7PgsUAO +2ABjbGWcgrGrbzkikGyvwIkBHAQTAQIABgUCTD3UiwAKCRAwQjNvkc7CrsVdCAC5t7TQV4Pj +HKRXGj5NCbIK6mShyLjxBboGnbjXRdd91SSvFt7uR2CeDR/9vlnZ0viBJCyFFIWoVZCqpr/1 +IiDJkTaxhwFnQt9rFqEk6xzJy5oH0DG6iWeH+TEgJ/zqVvv/HgXW+tzWQ3ZhFMq4ste5TNZd +XwDYyOZ9F+CCqOxSR2A0OfEx+UQ8X3eMy7M47C7lRezZ+vWhlJJgGk2I/NPXnRP2tj6xZb99 +VDQRJoIe+QWri1aNiaTL9GHT59W33hOkxI2F9mN+6dU1e9MP9zr2akDCj7BUUrC7c24LtOpm +6xLAleezXe4xGVXtIXCJ8KMLlpmT0WYiVVLBR0rsUJtLiQEcBBMBAgAGBQJMwdIkAAoJEL3B +aIjmLPfN8xMH/jhp37r0pHdOdzUCWt6lV8QzStKHaY+4xb8x2/Bli248n0DhQJ7qdF2mTmdW +VO3H+b7iHNpJ4IekOdDevHo6vnPjy+L9yLYAFHoXwoG/x0u1tpiUP2K0WH1znPTkFAPuMGGM +L/iu5lDckpKWQDHqQE6s3jhIrTMaf2n1lzKaldUukbYOj9whc5YPJ/2LxFf9LhXlKLqWYEUX +Gr78xjBBNaDcaMqE5WLbmJ8RTdCzsWEfvShwyHvZiKVlMICxm/4gfUGE74stKg6xR/7uI+o3 +sdZx4GSIrAQwEJx6nWJGxXAqkHt4Fzf6ZdIG3SpbY44QHIFtSf1aTsjx9qrbwpQARmSJARwE +EwECAAYFAkzB0i4ACgkQAHqTKeFqiCUbZgf/RP5s6lcE0V+zRx1bQCpkUcz9yBVpym/P3c8X +GT4vc4IkOr835YCJA2naltvUv84oSw9MeB40pdubMd6RgfvF2IrA6UL3M6GrhOTRzbdBaKMZ +SMzxTZC9ruP2Qf0Bk7K0Y5hLxe+1ie7pZMNXPnglb7LCl/xfZNpU+FTqgCoxVz+N0dUZiswu +BIr3hcUr5vIe32IMOwY6bW/wpPYikywcY1fUpHcSU4sqfEy6zMUwzqXBiNRGwyYJdkT25Mvg +mVLJmBVA502VisWuwiYy12cmDs11zXXKBWbEPswyBHAz39MZRiItiEW7mjsGYhv6B7A4z1wq +ptcxEKK+0GeEb2+RhIkBHAQTAQIABgUCTRf/FAAKCRCSPPtEotKT0Ws7B/9xvLqx2l3fNMLb +yvq0NXaMV36W+vhgLvHpjwdOsPVHCF7iX360s+fwc2PxitAGyuuw1eAtyvSFSOdx4o+fEdDC +OnnkEGnLwvOz6ugH8ffOx12b+3eVp2aUGeP6v/L2AE12FO5nt+1YMcoyKQnfhIABB2EMhiPm +65NTDOKL9HAAGpL/tUARxoVHOrUMFFGunLHpTwVZCrEU0NTtqF15/jGZf3wROl5/JesSS9I2 +ehHoZlF4H9KQRAFfAltA4RsExWemtJGdmst3Mt67nV/MQMgQkxnzuu9RVVyFVrXtUv/K45jt +4lwY97SsPIMRizl0vOy7VrlqJuKaZLx87+JYaxpPiQEcBBMBAgAGBQJNJnoGAAoJEPx9Wmqv +65+WaJ0H/0mPFzKF8BhXv1ZGbIdukJQFlvVDJtYYBldXeRcp69WBYy211OnBVQoh4UNdbFig +sO1cW3Csk1s0KSBXIFdBhyIibrghjWmzSghrq7QmUA2Jdr86TgrqsuAMqtUp7D8PB24kOQCZ +xq8i/v0SfEYAukyQjYU53lBYe3lq1zF7vVfV/XXjzXiDNyuFsjQ4JWBe7NfQtHucvZ6pl2M/ +8s9jBFmq6IMGPyqf9h8i+RaU6zeyfxebDCuKxGQSanJ9BdQxlUa+Y04+7T8Cbjlx13b7jJtI +xgvQz6H9pGRU7FtKFqtQY+GuVGzx1KEJXX+QV0yoCFWlJmzhv28MYli4maYTZ/eJARwEEwEC +AAYFAk1zjskACgkQ/atkx/V6NLKt3gf/S+snl3aY2qIngCN3XhbUbIesoB8Idyq9KxwXmBvi +MXqYDOMy7I4oSQDNwGgXkThQfkkOlnVPKPMSJTY/+6NvyOoDewg9GMdMxQ5va0GTlUxqt/yQ +bJ/Nm8WvYhE6OEV62yHka5857uI4KGAtKrm4BZpij9HRPr8V9kty+RPDtuhqtYN1Ai+BitHD +Rshes5NXLUsTG93VA0pKV6fkwdhbScyLIm6W2Iu5s1jXuv8EU8geVlK1jVD75xU/LNn3nMwR +FPOx7iKAiXFOurOA49WTmO9V7fhGvHeRjzBUbABuqRrY7pSxORaQOCAcGlTP+7sxb5UujAHb +LBBX6qpEI4j2eIkBHAQTAQIABgUCTXOO/QAKCRBmbopdk3T5c9+jB/9J92uSVfoXTxr4tKLh +fuLRBhJWM1KUqrrZvrjtptu+z5XQmN7etDOh7Dpmu3teJjthXfRxF8LMUjtGYhzmNVGmKsPn ++tbSkG6CIEPJwrkVTzTOqLW/ltMadD/CF8G4JBEMJNqA6Jfpw6wqX+20xY+jbuLCXWTBn6eV +e0iMPMj7IUbpa9mz+wc58r3sCG1IaC/4e9A0qdHVoxz5O8vtYsoF1zn4XjcoxjYBgGaqclV3 +Kq43MoRsbkTIXyfftwaCFrilWal7hbrxI16njTmWJ46MGfN7mEny7a+jEckNhpSgjtd8XzlL +L3Pmiiha+lrCR08/WxnwW6X5VP+I4ZfEoGNYiQEcBBMBAgAGBQJNc48mAAoJEJiXBqY+3rVz +C8UH/jkwpv4XjraLbFvfMRvLi+iPASeApTVjBGqD1YQDb8DT6ho5MJyIAgl/vKhsRWGKS0V/ +pxBhSbzLF8UZRx5zfACGRT+srk5NFLvkyvdXAh+70AQe345dtWI2r65chsjevbUWhWknJC6s +yUFvzRvXfiuOra9AyJFqUdHtipwEKhZ2T7/B1Ph51qqbQiQGdEbQPYsHiAYMdZkuNnN7Ufdr +/UO1hnpQxXtqFbhROXMf7TKAu9F5l8WA4RkxaPx7Vy/L+F2e8l/5JIhsQcbmsIJhpQOEu1NY +37w7i+26Xs/8c/Sz3zbRSk7tajOrEPMRpxCBSAd7cTQzZYBqP+UKFeTSihqJARwEEwECAAYF +Ak1zj0sACgkQX7EGuoKs4Nw/Qwf/d5wINyTEShrx2ZfmXc/3H1Ahqy7nRFruODU986g+Jpu0 +8GbUdui2qf7ZXq2wMGTdkziLdTfBitM1rmEfLASRX06RxaeUaQKWSv2Xca5ObcdEx6oFqkD6 +K8HoB31ej+HouHQLfRmpqLS7p8kdBO4b9Rb7OIYpGk/q3980ruPdjvftztRSSDYjzgfwB39x +VK2ccK53brNu2gxwkznsrs7TZloGp1OYX3IcSJmsX2Fo7iN6b9YU5wjSlda/Sli8RfuEUb21 +AYgqy1fGYHXE1u1Xy4jAYpm5CzvGzNnv31rBccgVRhf9BVJr8Tm7yjupOGnisDi1CyIKSOWk +Hf5B2vec7YkBHAQTAQIABgUCTXOPgAAKCRBU13SafYdHawM5B/9DzXxKogJvy3+fmq9MW0H6 +W+P7y/X07iCgcq2FBCMnnYANHPy4CAWds1bOW5Un2oNJgAi82P/iYkUIo+UYqSIWIyEy8Vyv +kA6Pnvu6eNekNAKfxkjJEfqqXOWknr7ALJWPmpdHJR9alln4CQF6jivua8csVVpZ7G5lCRda +7rdXDhLNvOSV7jxS8TJIrwm6o9lru+2y4VMzRHCaiqh+hluLUCh/1JF6hRLi3jUxidkA+7pE +WM7dxXoh4n6pO8KYjQibwP+V+6E4dKFb68nWCubrgyCJNFzFpJqHeTwCgFb7n5Tf+kIJpQGu +UARI8U0VHffIbICPBGx+9CAwG3dHOIULiQEcBBMBAgAGBQJNe3U/AAoJEAwylwsdBIl3QrgI +AJA4u3jBZXyy4KnfejjmRKXx08f3iNMzB39UGNN9vGfl4kWNkq2i5I68YEpHX6ar7P6VrEi2 +D0ZuDbpQA6tqJ/1OT0N8R76eAruxkowkBOa3dQL54oraHY1Ax4hcXczL0wMO+TsP7UL/4JcP +Z+9tK5CEP+IYL8VKXP/+6CjusNyB/1GBzkQKEe6Q8Va+oe88IJkhbuAc/D/boTwyKbtUOwxo +YsZOqcgPMYSiAWTKofiCLO5Z8UmfTstcL5K4GKltE8WyRaP1JJdznhTR0VTHIZlG7/4F0P3q +2C/gw/n5vYG4Cmu68a0L4EVbOJDT1o+6v1d00liJt6fqMD1Z+u53H9WJARwEEwECAAYFAk2N +hGcACgkQQrtOAXstlLnN7Af+KaUduaYv6dSHzIQnqTstP+VxanSIg53qbutuqDYi0emGEpqX +d5BHuvrv3Bb5f8u+XTjOvR4IS89dsQ2Eotx9sBV5hAYj9UUpsxl4ISHExOhzrW8i5cGnh0/X +GZzGPc+lkpdtUiOvruA/ghBZEHSAxZAdmCOu8V+ueLGv/aCnKDpw+qGjIjC6dHc5W0igH9yA +EL+QVcbGW0Yaki3wnODm8uoA5UpvPO24hoVojths3n4b6Z5Lfx9XFLs9Gvky7sR7Vvduv9CY +vkcEU3m6TQjtgmQKY+YfMdb5Nb2W9pF+KVpe50QCqvXBzb9mxSyvdQVgNdC9HxEcrT45ioxt +HpH3uIkBHAQTAQIABgUCTcNQHQAKCRApd77BrG1Nun7eCACvs8MBf0gJ3BxlRo7Yij0dO4jn +tvGM7FzerTaw3MvUVz5zQkJbk0YA+3SXEGMsOquQMgbua/RffoJ0002Pn5CYLtnSk7N9wzeD +gPYZoICkLdOB/ji8pV7qItXf+hCwDwYbaNA2myn0D+gDk3u760K8HYBOtO1aLQ7jEHYeQJA6 +d2nbwYt5WDpWSeEzDo+Fcte9B8CsowEN/xCOPpuIGOg++Yk0dcy75tKR8g6PlYQ2Kf9LKJan +REbckRLaFYB3kzvnZSLcRt+92LQ8rHesQltsiya8RR+E56ZsqCbTFeGfxP/iOtwGJYHKnVlU +b7oqcI2Rjjl5KPAcJ4jmdR21Tv0ziQEcBBMBAgAGBQJN0V3PAAoJEMN8gF0WSwUsrSEH/RRQ +87uTN9SIhmgpj+0jXOYLt54eDFZMWD9wkuvoqQpePhPFcADZdv34gQJICwWN3T244Lz4RuA6 +y6Vr+VUBGj71XuVhfevAzyAMIRVLB9JhO+8fuyPTxGDioT1K+KcLyhK8+bwl2x/jMfwW8mPV +aI+4gT68LWus5tJ2SpYj6W/xA9FDDs/9jwT2QOiM/77kvCoTc48Fk9dFkdmU63rImEazV604 +b7He5oGko2idB3xwCH6A+IGdzS0jhYZSs288khDZ3sdyCHTJEIJRqPTMaZCaIiDD7QhG3F1+ +bOKcMJczB5Pl8hinJHOGZtTNkL/EFCRvKxc1qA2+owqnxH6wy5WJARwEEwECAAYFAk3oLuQA +CgkQYfMUo3LH+FW4Ogf9FPxmKiEswQ9dQgjga4ECnFwCzyGkYd6v5gCum17uzYpygYRfXi3B +fgdEOyWc5UdU0m34JXJPSzra0/iATviuZ51uwcaUNDfbnqxBnCuZ6b5nU16Ija1PLFzACJPN +tLvJFyg1PPJXa/sW3uQmJAgAbx/o4EZIcupEjj9T0MB6KgUOjI7Ah5dklUSrl3LYlfOlgn+M +aH5MGH7fQW9fTCHYuvmuVz4YTUji5uIIvGckrAcikR1eob/8D1bUb4uJMc6B6+Vkhisamahd +glEH1g7iZQpWHn0Z7C5HTYuFuzBoMyHr1h0tZKgsFR0VGJq3knupu5w5dC5WJxvvQ3EPdURt +KIkBHAQTAQIABgUCTkREjAAKCRAeZ8FK8Z0evU7vCACmy7bDWzJF3P3ZhkI1XNl1JEEttUBv +hpr/DxiTxoPQ7t/YDaz/4yL2/+wiUblreAwkK0MbCEyx0RMWMbqHX3wDxudiMYD9rPBwt5Ze +EmnPcMTYU7Bw/zv/E0NEkt4CCjgbr3RbAxX9zopurLPe+Wd8D6mF5iiZT887F9HZjYsApdiG +cFZY6T3XtLWHosVhyrpKSdnZZ0O8ZbVy8XyQEtXvmY45CSw3nhsI4TBLTdJ/yu74SQ67D/Gm +1aa+mzS95DJYMDpH878nr+VCiljTV/lnTXlw6E+Rw5DKCNpsc1viQpWYP/jSz2gJHcLkr5Ca +8h62Ap5oLpv3MHcp+O0cwzHxiQEcBBMBAgAGBQJOcOYNAAoJEPlzTqmNLDx+CMYIAKpQTPq8 +0BOuX/MBx+dCcsX0KOmEP/2EIha8Zd2gEJDm3AlXJThIp1qVl7wqLKjyY2vNVVOKrOlO7TRz +vWU5aagywmkum3vI072+GL+Rz8Q4QQqbLHKaBn0K10ByVZ8y8yIHwMWZsMwhAp9R3QkV6H9V +8sLI5d2XeVmpYFtJb2eY9O+/kyQ9leDmTJox33MyxqMGJqO9/jTeh3qAjgGSGtSBrBsS47qC +HmYgrjYMXyv7AP9YhnX05m7Pwet4cmH7dnOfnkIcfu+7y6J4CbMtVyIHjC8RDlfWrISW03Sn +kvdo07VWbDTp/v1MbMpR1lILDfZ9PUJksrilqzAgFJMWFCSJARwEEwECAAYFAk6FMVAACgkQ +wJTtyMVbRJP2MggAvI+c81HZOu6zbP7ak89SF83HXBRtBSpNEFd1OHwmIV97ejPIvJvv7X6R +t6KpS/l0ay/7Ii5p3BBGuqnZ8xNXOGff4IZ1sDAiQ8JEisD1avJzC1kfPUR0E5RAdIDtxq1R +S46UfFUNpxLPKa2di9jQRS7NC4az8wyxEPS6E0t7FTeh48bdnTqOHvMRIRHeeWeV0QQGCVwR +5v+OotI5emO2GGp9jc7O0p047ZwiDANQL4fHs4OIM0CCQjA4CSQAmiH4L38FdzAL/tnYseJD +vdTSXQY9INIVzBcH2VxHtkV3r6mzFf0XcOfYBqYL99Ne46GuEat0nB7XpfNdrNQ2U9HZwYkB +HAQTAQIABgUCTvRSVAAKCRDjE+87fLYx8LcfB/9lJZieEuVFdHJKfUyxlIjX0s8u3OMcU2YI +ibWTLmdqf6CA2rAP7n/vYM7RbV9SM1zRQGlssJeYAuYrp55j6LerLdsd1NAYxmVwAi1NhmJT +Wm8kjZwdlDeJ8xjtHjwb28XO7mwADdh+3IznEoVgk6qruyaOSQ8xhGNliU5F7yPc2esrLgoo +dkYqpEr8jsJYKFaUaXsQHK7sfxQ49sIz0ii95g2o280GgXl30vtUSF4HobKMouG9h4Uc2ZYh +WekXENkYhEnrJYqZBZwDk+gXC9pyOvDFNFGjoJrfl3t33pPFTspUmoc3k2x2pdkrhYpKqUB6 +FmqN3UvpyFXtz+M0mlM8iQEcBBMBAgAGBQJQMEUMAAoJEPyYJ6tXZenp3cYIALeiBWtFEfJb +x+qWmegGPN8N2ULKmP1J09SdK7wiTxFL4LhAAHGqrfeN8+L8gNDnOw+P0QYvWIjXCjEuKyyq +MPkFwHj0vxvKf093VpahPV2TMudZvrqKe8dzJULLkeXt88veVpC7DyPVdLvjuupZArOTO7iL +/ziLu7pUCvxc9VmMNQlabslSn5XehoMfFv86SWxCauqTNtttchquQILlS9r31dVbhQi1pPJC +CjyZqowYz7hJcw6eaFo6T06NwZl0GorkBFOAcL//gVNAfGj/W+nw3MUMOwfR0Fh7S9M7UTQn +sPx8B1YQ+aTGw9AdWU7/UG4bz16HoU+tEiBzrngDujWJARwEEwECAAYFAlBRECoACgkQc1hc +LOM1Gs1mJwf8D0S6ck2NwGq+FFEmSIzJcfCB8avEbMfrpu+hcH2A10VL8prIUH5EgkZUI0kY +MRNQ9y5dVCSnZB6pRR1EjElnNO8fx3Ig9e0SjLa9R7b18eCa1UQT2ppRV30rd9z7MUHzDPhn +llkLCLLJOqloPCjAzP/1R4yqbA/gFUEabcyxfP4vqO47fZGA+4/J1D6edu61ODJFRIulH4/K +ly2r424KGqYyAl7T7bZX78qgf3nCwHuz8dXOZWRrwvibnju9YvmzEnjMNlUG/9CvdTzzAjmv +6xzK4YnBTYnsoRKEDpRp7mdpmmyXwWVPRoG2Mp3YG53M8HIhp7TWaTVLTUSNR7J5lokBHAQT +AQIABgUCUHgNkgAKCRC/2kAUTWCWc1SrB/sH7O94KFOXZqjlktE0ERQi4izwP5kF9EBWVLZ5 +hVyeNIcI2oldf4jfX2VjYLt9l9FhkuRJGdM7sPvIF2Ye3pLuMoSHpPYGvXMIOyj19YtdyMAT +/OrjfxSCNxjROVycLU8mhPP3a42VA57APaQ4y3qob7ZUpi23WnTbNv7vfI8/wJ9aOwiZimmr +5wtvWZfDGcJrqnm4rtFHKP9vnejIAGlJaiGIqwQRa7EYVnmUq1BJjlnRZx9nmvmp6UyaT758 +41EZD0ZjTT3Y5hq1uwtqeLnXHPIoXu5AszTWtazIsYZZfaKH1yHrZmOmnm9DpEexzJAfXdNk +xKKDMhLj0UrJ71G3iQEcBBMBAgAGBQJQ1AFHAAoJEJMW1dpgiyj07x8H/RPmYZgeryvbzVjr +9sK17aj4GYKZKqfQQO8p6z8NqdY6+lna83CPVkeznBcE3qHsJKz/aThzip1yynCMa7VF5GWl +6SQIis2izkI/iZUhkmMLAWwIwD0Dwgico+inJ/d+CE5JFXuARzZFot0ZTipbCbpGYTbrGUm/ +k3ABssH1RMSlm1EfqS5UsOP+8l1m5T/6qmabKS7B6oe8cfSi8IJ31vocpVWWUExw1vqV7Pe3 +GSoYuGAeXBo9HZMQaNeidL2ybpv6JD/A2pCu2vuGoWKzYHAyEfOkyn0oUr97EcbdM3CedjCd +FcFZZl5t06M1nGwN/2lcOILj1p7f0DGz7zRaKj6JARwEEwECAAYFAlEL0CkACgkQ3wAxD2Vv +y3/TQQf9EOSlpbunRD7Urx3y5cPJT8YLIurb1bLIKJG4ZMYasOLGKebPbQ2N3AqIV55vzFR1 +SPOK8M6ClCi6KLVkCm/Nd0TLSwGJZBbNV0mHbMMQRoAhOFvFonAJQWYpLJTQvzaJSsMI85wH +CvuQnYTIlbJNtLW2hUzH89SzvYLICKM5zUsn5/hq+W+fPyh4Ap+afZqGflSszn2h70ItLPPZ +8iwzysqN+7UyumUUNGrBmcII2T6bJLDQyrs3WdOpL+ONq6PCIlt2176fGgsGx4pdBI68Axss +DOgn4LNdhUQqPqJGiqj68gc4cnvrU2Kv2cXcwBTsmG1T97R1Y5UECPRWhAL3VYkBHAQTAQIA +BgUCUaruoAAKCRCJvSxwVhtTDNakB/9PpCdVjw9TFHopZ52TXCEeEzVnXIU3ebFa37YuN4aW +WOUmBKu3sYaQhnr5qQmWNuvH8mUhnZ562dcFpNr9ybJ7bJsUilx/ZLo//hhxcusBcRvbEPdK +wEjbJXrA/EgH3pjsnqazCL6qoxIm3GB4pFdVzHXA33SvtcItgv81BnkMmLoDngJ8YkOPrnP2 +wefwNU8WHFWm+slyBZ5FCKQKuLrxlBoafjcNC8hIv/R1B5aFazjbY4RwzIFiWS0ILx+e+Vzq +KHt95CKP1IODqupMmeEdVS2SkkbYIxuo6VQ9i0dZN78G3ECJr8T2xA/TpM4x95HMjMwMt+G+ +8i7WEokKT2LliQEcBBMBAgAGBQJRsJxsAAoJEANwtkGsTx9yv3wH/0p0Ajwc4oPeDlgTsLwq +zfn+mDNu/4yUWi4X6JaosRGjccSaFXuV7mE7nBO0d6HCn5z7c830ydo3jJUiqaXbzhYzzpSM +MvSF2zt3nElR7daT2OhVAKLO0bk9JQORBz4TUR0ykBHJaV2fATafzZLb5zj1/+YD71ae6mBz +bktZgfITj/8QK+G/pXnaq3BqjEYP17DC5l2RAjtD8HmMiEM/kTu1FlNOM69YnLpr7jW7Aztx +1uAJUlRGxx451DaWtq5aIuo00eMLhbbOwnA5ejw6AGbZel9NUbHDVQPcTdGNFBFMsR4Rioul +8pHcPQB7zntC8HKH0t0qRZthiy21jZqEBUSJARwEEwECAAYFAlHDltQACgkQh4mxm3K/L0oi +sgf/cqmASfW43kyltm3phw3ZZ7upz/I/S1/gnWYaUN+dUEBXf1P0KWe0aI/DFfByIKfW/arb +CEtGPx7Wp8F5tp4aDGM38ylGHdXdxIGgNZzvDHwC7ytZ2b/OoLqGcUW9v+knBHM4JWIno71l +7p4sjleyUEMRjwNPIuqDnIFoV3WJ60cjSqLpnk8Pd6KmmP9fr+AQqqJ9QQwgfcuMp/0FbMb/ +iKArFvuPPivYKlmIrHroSJULUe7+Iqojk8t2tAQiXjr/CxCIW0zwsEw5HrkOqui1LEB2M1CO +CyKS1MoX0wLG7Edm9Ey37yidgG1/cTIYrQ+zqAwUmV4BZqWy3Uu6vuA/CIkBHAQTAQIABgUC +UcOW7gAKCRDm5BTmntDSjsh+B/wJSj2R0Fhw6N3zlhJ//qBFVID48iamnFiIuLhu5taTEogp +CipTGW2me/dM9GU4+QF4PhdHw0L1wcNUaquK1EJ+m5plrrI1mQKlI9d+Ln6cvrIaOqIKFxBC +Ra5sXo09Nns0ksj015idiYvrgGg8QrT1Ac0lzDMD8zXCs7VIsUa/ocvYw2twc2f8752D+e/Q +OCmgooFANB5iYo94bXMDZ0OUqNdIe13fPZJ6jZN2NhmY4uWQ1SVlN3HS+01vEI/eIdT5MqTo +WoXjebCuY2yKQIu2XX78S9S9pKfyKzaXIBphh432s8dHkAm4auzvFpJ0AymV3q1PYG74pGPS +Qwe2GPYXiQEcBBMBAgAGBQJRw5f/AAoJEIiNBn+goRHea7UIAJS67gkI1dHBL0YvsJPa41i4 +D76h3NpJZ6LAUIQyw9toEZYg1IR1W89dAV6b29ARPz2ETTuhRDTOj85+0kvyS+ZsVkOG64Yl +aT1w6eE2syyUXg2xbxmUhIc46DhHLenJBbBtUcVljPoVK8TztdolrdjyCBrH4sBRVlvFsWzP +nfYf5wGiDY5UBr7lc7QdocRlImIDUHcWIa+0qY3dQTpNLOZdCCQcnMhnrrWxrBLfAHOTAB/u +tPI1RKFjRGtkgJFru8BUbqEBsb13Isvooup8VHuFON0JEnGNnerEmd40Rf8wpIRAP7INQ+Be +SCuGzgPah+XiBK7LTFyiatHe19k9zqKJARwEEwECAAYFAlHDmeQACgkQiI0Gf6ChEd4VnggA +0DOi8FQH8GYvBF4vfeO61QDl7yGTMe7APg+EmSyrR2WT6ftVwDIPkgMGnwa4h+hqpIHTT0sc +r0BHwy2s7h4eaCgHcn1uzbzS39GW57jDo2jW0TpSRnaUFVP7g7MxXeQAprwIwZmP1LKN0dDR +cKheJjL0/yW6h+0wCJzrZeScP8GtRDWBIMTXg7VcDQJEvIaSCynXzMbn+FXvxQ0mwGMq+0Eg +EkIX+ragCajQjACnvrI2AJnjin9RBnjiT2/TJ/hnaXBeNAcwDo9WIeDgeygBzTIAYQupUwi9 +oz3gM3++QVNQnvNEoP9vhY/zjXGLyxvynYAmWwzLmen2tF4nC6TwaYkBHAQTAQIABgUCUeoG +nAAKCRDYdi7Ot5Eef0wOB/9dBNC1y8eh0JsNPoVxPq7mzZKhFu0Var9L1BQIn6y1SXSLWVGY +zYns9aifQQJzHeV5jdMJs5a9BT/Mj8rIB+pB12ZkCnKBpqlqQLZ/6Rj5nplnyA/p3RKIHwLC +yXXdaHnaNuJyppfHx4NMkFrKZ9UL8wwggZ5m6FD1NgM3LSoRGtH8Pv8CVV0TpoZeZKSVvaf1 +t5dqZvlgWu6SPGoxZRRjUtQg8iUspkFhmaPj5MzI3yEjBPsEFfY1+QNEng6+TcfPlJBKuvs4 +xo8rsmiI3xo6G7W7zsJ+JLcxeARQzd/uyxY61w8AVqp0qrbYWxqWyY9ME87QT1lVqRPI6jnJ +EpDZiQEcBBMBAgAGBQJR6gl9AAoJEAShI4c+Vp2eWVAIAKIz/KVexp2Fhrg/MSv+BPEIbpOG +D8M9m6dcHRUXGaRPKbbfwEnCEiKVrpKb3v8uP7rBp2Az6fnzXa8PdnjE69F7NZKDEUA5HLfd +NfFNKMxxuT5FtmbeaqvHOv2W/ypX+g3VgegpSW4IFVIjEv0nltipiWjSj5r45Vazfxuu4krW +W8qCzQ9zTISymnlLcJyBoemnm9gmUm+OKOvV/fcrOdIL197efRLdVKSgPdShBfqYAhAPXgVw +tw2xRi1GsGftkFrulZ3oA52JHvPtOS3j5kjtVPUW0rrIiy5Cvp/LbZAu/hL+cnec5chBOUe+ +td9jO6GujbD5XD+I50KPoS7863eJARwEEwECAAYFAlHuiMIACgkQHEN/Yspl3pnDOggAmOr6 +/hwSnZ//TRwGSqlQe6tmw6VdymAlxRjxrfUKely33vaZy6f7/ppO1Pv7EhSswBU7616ChpOY +aghSRaHRrNdysRjZTyFdus9M3TPQHbBFEbf1Li0aIL+PLyS7UWxY21Aj7v9Hr8JXU/NJdfQJ +CpnAh6m/C9St2cZCRXVid6aUuRKsTPTE9ssvHVZTDHFDrPh7tK+fomb45k59oFDF+t5DAuck +qgbUMGdJGP+tzqM03Ceh4JZOnIEitcJ0/ebyVWrBSbChbWpv2SCFvBrKfgOF4LQGkoTRP6OS +VylYpiZC9arTj9FrSkvUkGZlIQdm7uY8YVwgwt38QiDwp8RfOokBHAQTAQIABgUCUhaUrwAK +CRDx4bM5bbNV+urYCADMMw445AmJDhgy1Ss+cRCbMMGsaj7ZwwMfkHK5yjqDri++5WtI4aK4 +vVsnk/FFlJkO778PwwO3Fk5AC/jban0M+yzzWz6L9sA57SkNS1HnYpvV3Vz7TqyYdB53dNq9 +1Qfj8/+35OYiChzB9BYfBO1hFdGhHSsx5GXls3Q/c2Jb79kYH1AdCzck54RPgiaShh0Zd2AA +iuWmS27sUz9hliVG10xgpZgaxZKYyQYYpjxQ84fI1CYzpkH/P58OjL/sLT8DTIFfsf50hrO8 +fdmB1qZdlw012OSctXFS4NB3uFgnkGHWneffHv02GaJnz3K0BUm74qu+eU4L74bq7hnKafIK +iQEcBBMBCgAGBQJR+k6GAAoJEIqpBEt+uqLL9+gH/3BIpzCoqBLLz2fzn26cI8cQB7i95YQi +f07KUSCtxmrPL6keh5N5qwK0Be6+6AxB9MPll+2290SxmHD2cwti2q9yXOCfLXJEdFqQshTp +Lo7gRFH55f8cJxM2QiQijslQD6VUkmwRstFEw8v7NIEo1d5rjwYUofFs1nF5jJwjyCS90A2K +l+FMLn2B1VJxEY9JKvGT/HwnYMQkh2YeiBp8encZGrG8w7vRiDInZLNi8W7EexbCafNJO3R9 +ygSsdmSYF9iBN3ZFnlc6lqROzEwNG7gy/ZHGYClvcrB+SJstocuk0U+FFzG4oZjCBNzBpjFk +JaHs81Mpvz3RVsL9vcYaiRqJASAEEAECAAoFAkm3fQ4DBQF4AAoJEPaHiuyrru5mfdgIAML7 +HNXkO9l0i/SrfUiNwYCG8eisJCVs6wSpmcFBdVKqC+cicKGDGYrTxii5a8AJi2ijJqc43GeC +47/V0SjnunET4ZRsWamUFlbvH96JG41NDUxdWAM2WcHysZeRk7tcosYvi0J3YyqxeLnXS4cJ +/dRa6RI5LZ/qmKMJHYaXMIcFXxXsoRQ4l6ZJLY+cO1IeRTuk5L8qVB1z47GAXCi54NuKXjqK +38bmR8wXa9scHqH/7ewYW301QC5FEYK30Qa2dgWdAqC7tN+Aed3U83mTEmYKWAMoE9GWGmoR +MjjGQQtPA2/e0OBWO4kJ8X/y8EnmA62xJKHc/RCib5XKIWhP25+JASAEEAECAAoFAkm7LIED +BQF4AAoJEPaHiuyrru5m0MMH/inH7YRFs4PLIMroWr+lBHNpCXZuj1qf27a34fcyt1UQopzj +elCgagQ4ydKh521hAuQHpu5bGy6ub0bgf1DIxqiSZ8zOTY+SRfyoQ/5iL78bkWqZ66FdDn8Z +Kh94PlKm4Zdt/2VsCk7OvfvXSGJjpjiNTlRG6gLu5r2lneAXnG7h53NltpY5dZam2TWYqCzA +tWH3fhYvUVQOR5EW+81lpkZb3OcfE2lHXVLnSxCdcjU/WModqqFDZcnv6x/aO6GuYCReu8cB +jvyUoZ/hYOhIgDBmk7xzC9dF/qCQ9obH34wbEAJzBQhktE/XjDxcNZY/s0TXrznci51+Gr5k +lws2ZYCJASAEEAECAAoFAkuznZ8DBQJ4AAoJEFP1iQSrzmXkMcYH/3TbJPup7v4OKQvxfeR4 +7hYp+WlPB2B8mwkxAv7sBft8/GoNrDKiM/gIxRnYb30IuI51ThaC7hwOps2WtowD5Y4PLgvd +WiSg48zaFHv5YPLMGKZVCOJ80t9/aeMAdoY/ZZyXBvPHoYy3isAZ9DY5ZUyg4Z3T4wQzNtM1 +plirNWTQlrv4zTxTBBtK4lgFhVgaNDVxXlsH9agsLKKLHXAIgAyFFP3N58qG4gsIUpiG63BH +j/dEEqZfcsMhWzIkL4O1d7+4eC1TAbv3V6TvwUWclfaWPMypcRh90G5P7PBbb3CweGlfsEd6 +v6dGaq3K9q0e//diyY9yr3MpD6oe7hb5VkCJASAEEAECAAoFAkxXAMQDBQJ4AAoJEPtn1svB +8QoV6BIH/iFyIBoNCMLZcAszCgdmXc+NY+2vXYFyM3Z0cleGAUXFOUcqVDCELcsVM2YmwgGQ +FbNVGG+kZPm/SMkh5aerVWRlNFb3vUgL1+Uf2H4v2xBabW+84ArVliItxmrUMHSoSVgOMk8P +vALGeifU38aEyLrM1kvSyvRvqvMpzL+G8Ui99Icg0ov6BnS6sET1BbvU5ltMayA7AdNZP3s1 +eCHxYID0eGmOJDnxgg+lh1XxXyAQEESTQ3cfpXCXU9vrQuwSYieaZXK1rZbLXHxGbm4O6bjg +zZzLIDqnVQA8WlmRDAUqVVU3IBoroV/tzvDeeWWFjyZPH6x5tbzI8w4X4i4odX6JASAEEAEC +AAoFAlC3X9YDBQE8AAoJEF7B7PpSrpzuY2wH+wfBEbkBxEp0NyDtMMVyI/+Kb6lJVN2XcLGZ +gYWOiSzniiZacBcnO9+9asa8aQOFORNlozYqQP0brnfX0QlE6EzeUeU33oiBMLI3fRReQNjO +llrD13qQZXZr5i9sBiqkr6kiE+hmiVRtzmaQCq/fhG0AWxCih887lHt7sBla6V1XO26PJslG +/P/I3BoQ8befGnZZTgQ87lzNmrYzbt0C7jtMxQBZHi+t3skgdvptbtECqN8n0m0i6+ZWgOi8 +K/W/VPFkSfuIXLhdZtV/lJEs6BrsfovUL36LsOr/i7T5BGVuqtVo6RtxMk0v7eWUVCjLSjeV +IF2eh1OS6z3brtaauL+JASAEEgECAAoFAkl/HBADBQE8AAoJEG7uKETZYb7rzOcH/Rtsanap +Cn8l+Ak/5HYS/o3YHt2eTH2Q6KOzv+7PRp+LSs2Yp7U7FnBDyg5ya6AeOFXeInHH1rN8d2AJ +ItTSGZAsKQpNeAhQH0LQFZFQrXz1CG9RFuICAxczMtFBhViXeHL/QBNXpFf+49jm7SW4W7MV +YJJ1ondg10XdzN0uRTL7g8zAZYivyR2fbPArUo5CNKTxkge5BYioMKd7iHVGRc50pF/FbCOJ +4b/f/yUNQXCixYAHUtLJgs6iKNq5Pax67GtmUHFTtSK0z1lezpEgmbZ3XnVd64eJKMVSsPjR ++CltxsYBsYGEqkgTgmcsP+DZzsR+Z6n7CH2TcXcIbsFdDwaJASIEEAECAAwFAkHCCr8FAwHh +M4AACgkQxjx2prH9835Cxwf+L97BC2UebKrKiTztJ6RXVBJkiuqjheQrWbH8vfJNHN8mSGuE +Pa3vTNxUFCxWSmQvaoG+gTUEah2m49Xyf6RoiAyHgBo5beEr3KwhoR2Y540LDSpSc9AyB/cm +nzwEUQHAq4cR//wrTCXQhMy5oBxRO6HDYqRon0zyG7tPsuIrb+5NNM6KT21QMh78lCpjaw3F +0nIJIlF8iv2VQ+nP3Gox9lw4+tV/Nkv56DjMAt1NrAy6NptmF0qGAVjAJwJKFFU6uiYpBVTJ +419kEm5/4jiHOEUVDfH9Uk7S2oufDoiWBYnLTRl5jWELq1HhDWmwPmc5DkPE054oAWKfJLi/ +uKHkvokBIgQQAQIADAUCQcIKvwUDAeEzgAAKCRDGPHamsf3zfkLHB/4v3sELZR5sqsqJPO0n +pFdUEmSK6qOF5CtZsfy98k0c3yZIa4Q9re9M3FQULFZKZC9qgb6BNQRqHabj1fJ/pGiIDIeA +Gjlt4SvcrCGhHZjnjQsNKlJz0DIH9yafPARRAcCrhxH//CtMJdCEzLmgHFE7ocNitGiPXPIb +u1+y4itv/k0kzopPfVAiDuyEKnN7DcXCcgkiUXyK/ZVD6c/cajH2XDj61X82S/noOMwC3U2s +DLo2m2YXSoYBWMAnAkoUVTq6JikFVMnjX2QSbn/iOIc4RRUN8f1STtLai58OiJYFictNGXmN +YQurUeENabA+ZzkOQ8TTnigBYp8kuL+4oeS+iQEiBBABAgAMBQJP+stgBYMHhh+AAAoJEHko +UFIjawAvxhAH/RQzy4h5anNPOnwVrsdmtR2R1o5Z9Gv6AauGgCW8qTSnI6dL9XF2Lj2xEL8Y +EFrTWsKzCNghiZ2u0NS2MK8WzWqFVl+opvq/Hw/FidBaBr0MTodylLJg2rzmuL62i1YSYIxL +xIbWRlsve4dAj4MSBdy/YlLmwjvWVzmf11sidrpUd8PyY/AQfRtsbsH6TOUYHG1lXE4OYY0n +7NliQjCmuMV2pIlQ9+m2DpthJ5QIGRZUArX8iKmFtKP8uBEhm7JdHvctJSXWJdTl5N3UUw89 +1acm/TJsENp3dLAoaaJejaVYn8MwnKBNPhmV7Pc0IUi9WuDypQK9VAOaB920x1RHU1uJASIE +EAECAAwFAlBgMEsFgweGH4AACgkQGIIg8JPUj6PSXQf/bMFpMX/VO+TH8Glk42cWhQYVuggf +te30kqA+PeTLOclQaOXtuPT23h4cypJofvMkXLp/PJeWk80fftAgVjmpz4NNen4empSgHngu +W6EjvJyNonH1ZREaYukHRekXohnq8V8YK6W9uyailLp90krSnbdrHcEiXvH2q9ZWXxo7V0UZ ++WRarZ/rvvI3vvD4iNEis89456BROzZjHO6VUzmCBS+O6Gj8qSfYXPAITmQ7MylMXJ805Pjl +rS3XVRlmMwxz4pyt6IKRUfZGNBDmE8pKjwBUjfCeVO6hmdvl8MC2u+nBbIr3+fdGWFhC1TgV +Fnp97TwS58ZbtBWiI3I7HSXi54kBIgQQAQIADAUCULvBXQWDB4YfgAAKCRD33+VcIwwqlWpv +B/9KQdzBza6ntUUHfURYLVVat5OMB2f5d1bWDjhPIvW7AjLdg9JLkL1UzK/niHAmzlU/2oRS +6ODxhwD6YQK6dm8sbiDNrNDXRswo88B6hKR/8YjKcMa23MWF4TerjHicmeI4rb0dvP3x+LDw +nDc/dPLIzaKiQws41e5FZRJw4aEAbSDi5B6clQwJO6xk4UKeBH02SEvKKF9kSI1P0ZuzJDQw +o//nK6Bn3z8KJFdYM2LB9khYvB+PlhbfBX+Qa9lo0UuO/lhD3YiqlBcff82dhSHZccEhqve7 +a0/T1/tgTstHfkFY+UnRrZ2yH8J8H1ZL6w4wItM1zosDsdIIaJlTQX2uiQEiBBABAgAMBQJR +ENSfBYMHhh+AAAoJEN8AMQ9lb8t/M+gH/2odVcIxqnbycqTchWlmZ3cnEJ6/OWdtH1P1AGkZ +mNhJgiT4IUPJPW3lNBEh84t2vKCqjYU+HbFLUXW6KBUQ+sx2wRZxDoLhf+s7jgQWfaStSC6/ +AxJKy3BID1+4XX8e6Hffk9zrpvoDwhw6KfjEQyOl+jrUR5ddlqMSXaTkCvQFUOanSshZrKH9 +cbTudmJw6CodqSW7fHdMHVEVUyYCtrJZ8SVQdAdHPeZWbJ+Dxr6Pfxz4NPCIJuIxNlhhuMCY +FX3t1nuYRIvqEbxT1C0GlkBkffeMnfDWq87PPtq6B3Zt3G2NNQWQOr0cAVGh9kJCZGJMPasU +UpVVIQZ/+3kUR92JASIEEAECAAwFAlEt7OgFgwCfhYAACgkQqwon7kl7c00J3Qf/e14/e2tm +WF4EWvMJC3WOzEKc4u2XbmNGcX1Gg5nqfCGZQ2Hsf9uOma6j+06LMDt65vm+tvvSEWeKQiGE +J5RfHTJKzlwq3kndTFd7Y9n5WainzYV0XOv078iG0V3XD0Pov978HeNIetuKj6pbUuYE28Hy +ClIf4h/g11zNfyuKqx56q2Jcqp/g/QgSlEGU8nyLj9jua4rR3eWjaKAyfehLxk1R95N0mAeQ +CttUERVB2hsPP6+FaB/isQH+h/Ul4FWqa2IDt3i6uKZSZXQiHY/97+c9pkXcYC/nY7bvADxU +Cbf37plybty+m44u81EeCB4cGADuff1qO+ylCBcbZ0AQPokBIgQQAQoADAUCUgDAUAWDB4Yf +gAAKCRC51y9qPN7XPSQxB/9YbIw45drCfcipCR2bW8sB8eidAPl3RrJBY0K6QQTPPfiXfCa3 +Jq/YoQfvZcgF0Iqp84zP4H0CLJbK45wmp/s5+rLdqAtIqVyl3AxvmSlMBo9rxvQGHe+7adyT +iAld5NZ9KJu2jNeOy1iJfipxLwL3WboqyH5q6AJHk8cm0Xk+MrMrtG8QtHAbF2CjuAzXvp1P +d2zieMbc3ejsHM2uc4dXb49lfDv/5BQ31dtctTcDyupMZoel8ZcrKcRXi0oK0p8ESwmNEjmh +L9u2eCKGzF5GOQ4P7chqUUBFKDLj6ZEw+JbF7kLLcc7L0c3WW8NaexbzQut9FVQkWD0Hq3UL +4iO9iQEiBBABCgAMBQJSHUdFBYMHhh+AAAoJEMAoupHawhiOTnUIAIAZgltm09xfvYZz3lKe +quTJVwyoaWP92+pQizbKcYPf3seuNjhF0BwPNcNn1iZV2gaO4kxIREbDthmeswX/rr8aH9cb +/WNZqWwWo0grglWPkwGlsF3qXBwdSt+L18fD4scB2b8fdbBU+MCHUYKUqNMYJpSpiAmw2X5G +QTYXzjDDBhDZCK2ULQx8hC6B2xS0uMroWzmf2p9xHtqNFvXGBIBh8UkDaryYFjQd/SjFFl3u +SjNFu8mGEK3DKka+ONpeP+iK+Rc+8r98f3Ec6ojm6VzScXk0wXQ+XbvVT7CAMLzwyNgF7Vtm +eFU94tS7zhAmg9RmVr5U3LSL7ESkrXUq26yJASIEEQECAAwFAlBX6psFgweGH4AACgkQEQTf +sDyQ34dIEQgAs3qBPLSTbW+yFUZ2/54rrObBO85uZ+dp0b/EqEY95DkMycB5Mh5kweEEUmIX +EpBAVctDsrHgV4J/TM2wJBD4ceEOCSW9YGq8Uxifk425c4W+k7ALgS8iQOADbZenOYELvZzs +F1CElsIpjdwS1FFSMf8JFqHYYzbBpWG3GND/H/Azg/8Md/jx2BDaE53sgnvgPRU4w9gj8W+c +UxlvYJ+BdunPwK1MKq3nSlyG0vFtaaAC1pJbI/2ZvmRV0z7g8nM5HyDoSfwDym7glyhFgPH9 +67OobPWMJNsn0CDX/saOSf09wQw+hqblqjIfz1PzuCAmZfTWvwETbeQfkK1GXzfyT4kBIgQR +AQIADAUCUdwkswWDB4YfgAAKCRAq1SB2CEitd9bsB/0WFPO0bGrCoAuiH3kISIdnpUc5Yl58 +R3vBAT6OR/vVI8xkVQduoSFn4wDwVj2mTKhys+2wBOzQMXdD2prNUJloFBVwKB1yAQrOlcbs +wiCd7y7u5tKVWMksyJ74ElCryVoAGokMXJ05gux1WjbUW88dRUJcvwydYD0K/Fktw94XuUjw +yETTmND9vBhr6U16+sHCkGZnexzKm20uduldC5GzI2qtcVvjRL/Xi1gFAW8MCrvcigLiW1cI +5/YforkwMzOfFgxynkf/+tFDmlrCE8G+Vc0tS6Z9PpJNzelyf/Njjdtll4Sa47JFpfEL6Wph +pRFKZddEGDW3W8LSTYKg02L2iQEiBBEBAgAMBQJR3CTIBYMHhh+AAAoJEOYzmqCkWMKdl/4I +AL2KrCvSwPDrCk1mB/WUWHRkunOTQ8rIbtd4huAsCfVyWHnYUGRjn3hhDZ2CZohVW2tICVPF +GiUECImnhLQOiibb+28eHbK5BZ9uBsmdJkRFn1KRoqcpIbw4FOeJplHJVSUQFXcGfUpU6jZY +GaeTXUQzM7KaGD1v4yj/IbaINvZKMpBJyvM7YambDjUCLhCc851iVBi6O+S5tumVnEK4if+f +YrKTeoje88YnUwlFImytTArDwYLG8qCSLEJbrEtSmsQpn/+wsUsoO4C+q7fGXv3jCBHOiEoo +edOOkhqzJ/n+DM1qimuHIHRwuuiwTKnao6M+1Y1CM/hrrWMrSw3BfYqJASIEEgECAAwFAk8Z +xtkFgweGH4AACgkQFl6M+YakCBdidAgAoX/5EexmHyflKRePrv5/57JQtq9XvDnd2M6mm16p +P1+Vf0amdxbLrXHkeWphVp6TbErcNEaoC8IFS6FzEkUikfe02O4HGJEvSs0Z0GIX5F1JEvvD +UQhyHwr53C0fIUTrPA4vrNHtcuU78NhlBR2Qc/brczYdEk52toJAmS933YTdzQTLJ8BEFCQj +YoRNskYFnmYRLRHssp9oIdE5mF+122k0dK/25W+juxHJ2x65YQp0DJfWwqNv1O4QPv0Nl3+9 +bY7yMkKkPcM001wfVpgsJlusd3DHwn1bQloorYqTK9Z+iyxJLBBfEt4AiEvLD179B4sFQfga +pl8IimEibeqsaYkBIgQSAQIADAUCUCXEVQWDB4YfgAAKCRAjk5T+f2pwQV7fB/wL1C21Vu/3 +mZDWShVYI2lMFVzDGIYJa/ptJ0jJwZrPGZn2/Ai2HLZczLN8y6H9I4jBJJ/Rs+aiGJuEGUnM +yfblLg6kH6X1I/95R92V2HUzEtlNu7wna9xupsdVPWIc5L1oWAdhtAKW8fStTpBno4SwoUIM +HnkAC00YrbBZGFOD9N17ocyVJrGRSl2HKq7+SDZmSBIWBcPpGbyeUuZclPTK+A/bjILcWUsR +B86yx1CiHqy38m8OPg+mS2MHrk1OJK4KrNqCC/Qin0RNmqMAUs0oNFVhPuwKz6/EeOXrI7zi +JsLSZb27ffsKjEUjPim+it0yx2Du4pnDV+3BYHtZF1wYiQEiBBIBAgAMBQJSBUGUBYMB4TOA +AAoJEE8wqSYxzf9vLhEH/3aGv7o6xjsSiChC5DxWcJGX1ajropQdRxYZf5gUhf2y4XISBi0B +pfCvd6XSLKueUTQqJoesrPqO1a1KEVYj1t0BdaNEDM9/4IRg2v9gi4DzczyuDDFutz03f9fi +4qZzASgkm4xZcP4wxoT8gTtrqhE6wW50bhvOQRjdwleYHpJfAncCzY94P777fd9NOTgPHLdX +1W/Gvdt8DrzgXSMEbaTl7RCD9ApAU3sN6cJ4dSZGF1R1nZaa3l02DqiurYhnBNWJXShIvJzg +9Kg9Xf+VughfV/NlppLG9MRm7eOiENb7d+dKV9QoLt1fXdjd03TiPSYT5lJcfBUmnakRwwAE +EeiJASIEEwECAAwFAk7np8MFgweGH4AACgkQCk0btjR8pxUp7gf9FAx/oa9WxmUaxvMLwmmv +i0C8rzq62C42bCtJcbrt87oMNngXu28i+HWvMMBobLtWMj4p4ODkLyML0xoJhDYpooSJXZWW +5TSl+K1M/5iCyn9358opgZzVd+n/clCsQo48a6XkZmKo2R3R8qL6kP0EQPSsAIZA6HjeUFrN +V4nhNTrZIytAyqOtABXFIb8UW18NCFpRTHeUffbE3Cu7jg01sFQZ8LZV/GYteqRm56zfXSfr +ADTKXQTus942N+0v8tLYdeIl1gjBFdTbDDsZ4ZhZd8rdrAzHhlWb5OwVdtAd+RkikfnIm369 +WfPQ7L7IKg5VvuPRBTVTmm8idSZWYKM4xIkBIgQTAQIADAUCTxDzagWDDvsbgAAKCRDTdP1/ +ziXyd/KKB/0UiYslOVUAfK56tV3Bw87rtyHJDukRTCMKWg4+5bFrw8rUM9rjuOunJ4N0Qyr5 +Jd3gBxH3cyn3CEvA2b6G0Cuka0jl/sO6NIamGw146F9392rQEuqXwoxeJdyqIWddNS2pXeAi +NSnd2DKFW3xrKMubi1UgJ91dP2bUp7b2nYT5g5M3oa6zUgld4pdELDX2H1RA2QMsw38I+m/m +cAwdVSpXcQNDAv9dQ9tXaVsLjd+ii6FbFczIvPQQ/Px5rxcBmdE1lSauADatmOBhdleyeDeR +POXcYOWktxfM3Esg3u1hvgR1O9tAnybrAVmwqq4ingyzuiQCM7xSHRHY2a6vBl0liQEiBBMB +AgAMBQJP4i6UBYMHhh+AAAoJEGM8WVFkHEUOVr0IAMN/tj7Ne54lQs5TOUCLZy6XP/KAuruZ +IPE0pKp3RdUF6qe0DmHAZ2e4GpvXAWDgUQFSTR/h3MeIl7kXQMAHdRuysW3/ELgjjUUs59p8 +koc23gHrxLkHS7XQEg/vIRPXKTcCUnUEeCLfxo4rILnI8gZkKEydum5zLNDA8I8jrR1fO6Rn +AJ92G/2W2DnMWQnKxD5OUBkKH/ThREuXoFfL41RLBrsvUPdq9xVbYLVkU5yZnzY3zx3ghccY +1aIw4ZjKwxkm2aoo7fio3wcfKq/ElciUz6Q5mPYGuICK/KXnyfbtZnGqU1jSQNQUc2E5826f +lm8QqLfW4jY1BFgAYZNwp1+JASIEEwECAAwFAlEt6poFgwCfhYAACgkQ+tt2nMRpeF1siAf+ +Ju/M1QceVfeyhU1J0UEnbyQJU/3khQg61Pihh31X6Rz+bu1Wi0EPUYYpLNanUxzZYQajX35r +jvmGCUGjtY9XyBaQDJsTaOBqNlIQBYd6c2qxSynPPcYY7kqLXWJ5qAZ1ESu/YCfKjt/U/mO9 +H4o+TsJ+u41Qm8g4XOItLXcqd5UKXEMn37Twe5LtwdtE8MkYMgbItZzvPIYRzk4gNCqUvlyK +BtYqW/ePPRzdUFW5ZMCf9lzfbZHJlNwKvthyjEFrL7Q6YceuYD2H2g4GyG4dVkJyUn028Taa +YYxdJ72OIceTdYNqJJ0EHVea/zKYFyVZaujAez+G16cJS/e0Kkg8P4kBIgQTAQIADAUCUhxe +lwWDC/FGgAAKCRBfroP/tdjwjF7RB/0RdEew8Nz/vdX4NI3z+7eLS1dHhhHKxo+etqe1ybP1 +jQe4PaLOY52wy+h5Ba2VJL8kkuHirta1ompb2IVxVV4jaZfuNO8rwFVhLsLBN9WIWPwXwW1N +V1atzoP80uR8r8ImHh+2xpinhXTOadfExvRSVp9h1PiLNOAmz02INFqlwFjZRVrrYER5TG1W +1VciczAfegBllLk/U74KeFgZGlTJmvZyFfNdmq8nHn29E046LrErVc8s1YC9vg5B5J4Yr4gc +4nIMBSFsgUmZESm62eBMUupFgs2QZbrJPwgxE4efrQHjkjTIjP93i+9JlxiWlIT+zblnvGCX +jeyqslTB8M6TiQEiBBMBCgAMBQJRwRPyBYMHhh+AAAoJEB+5sS3+29fTFaEH/Ap0NxVph43M +8R6qWTyla70NcEhvHB8vhOkY/P9dFA+J2Us/Tb6tBF/w67GVT3COjtSYu6VYAwa6D6dG7/b/ +T0YCmNHPo2pJ0zM9+KxJlxQIgDXfog16AqtZl6a2fNOfgwYWgsvqcgF5i+x8vZTGMG4gyhrh +6CccsODgrZDsvNZvcJsZzHZVxvYCDUDanNwoNroth2D+xLqcKe+Y+68lhuz7D3ByXnz3qcA8 +wIa2YoY+K9EEN+kcREPwZ7cLo7cGtEORywAozDL0cF+m7+E+Oc6s/4RWXWrhGHwY/LZR+8cJ +CqAEya2cZFksNUjjtkl5cKU+X5+c78X8X6M1j8Zl67qJASIEEwEKAAwFAlHbzhcFgweGH4AA +CgkQDIXPyQKTJ9J7BQf/ZL9Bjv4gK19yyFlBwOupP1xAodXL9TJdppYeVo2Z3L62bAlv5vBk +TpxYkQsKcVqlEFgXnySCQGVCdSHfEwp02gjB4Cekh2qBLaiFMKQTKwAfPScxu/H8KkP0bFTz +jSTOJNPmjqDs2AtrNSA2PhU94H/fCflzqvHYaNK6mpdclt/fXntRFeI9B6YvbGQkx4cQWDzd +KWmY8UAdMgsuFXLLLpBpOzYoNPNBH3/p5y8C3Et5EtEzWONhb5zXdJiHcqddK+/EDDI3mqAY +t70K9aD4fs0Q9nHFOaaCCNierVzHqg3kPoK//VeP6/OCTD2luKKRaZhFBhIQtmm/oDR8L048 +m4kBVgQQAQIAQAUCQlG0cAcLCQgHAwIKAhkBGRhsZGFwOi8va2V5c2VydmVyLnBncC5jb20F +GwMAAAADFgIBBR4BAAAABBUIAgoACgkQlxC4m8pXrXz35ggAnVHdAh2KqrvwSnPos73YdlVb +eF9Lcbxs4oYPDCk6AHiDpjr2nxu48i1BiLea7aTEEwwAkcIa/3lCLP02NjGXq5gRnWpW/d0x +tsaDDj8yYWusWGhEJsUlrq5Cz2KjwxNQHXRhHXEDR8vq9uzw5EjCB0u69vlwNmo8+fa17YMN +VdXaXsmXJlJciVHazdvGoscTzZOuKDHdaJmY8nJcCydk4qsFOiGOcFm5UOKPnzdBh31NKglq +w/xh+1nTA2z5orsY4jVFIB6sWqutIcVQYt/J78diAKFemkEOQe0kU5JZrY34E8pp4BmS6mfP +yr8NtHFfMOAE4m8acFeaZK1X6+uW54kBWwQQAQIARQUCQbPTrAcLCQgHAwIKAhkBHhhsZGFw +Oi8va2V5c2VydmVyLWJldGEucGdwLmNvbQUbAwAAAAMWAgEFHgEAAAAEFQgCCgAKCRCXELib +yletfJusB/41PL2YVOzdS4gTSGAln8vWUn4I/+E5W1X4sPf2N3cH4PbZxN4+hZe2Vm+Ki0ZW +RGsNUtYuOhfuQFhJSTRCGKOL6DdEHe0ASs4uxHW4E6/IwZ/K81E315zFb682ywBRpesckmyt +QAmp3qJTZSrDeUQ+ZqoFLGY/jcsRc+ty9wfAphAwsDYtehSydnvTXhdb8U1voeCC41Vihvvz +i3Kl0GNy56m/WZ+Jf5pqTJCHFdjI5iCsDLVAGQhmw6EYsV7WywpqJ/uAQf/w/obWIQXoKfNT +kBLR1otiR9Ib01KkbW4lm9Rcs8WEsJ5C31TKEWdczsWWvyJyjFbXoTfrTi5szzutiQFeBBAB +AgBABQJCUbRwBwsJCAcDAgoCGQEZGGxkYXA6Ly9rZXlzZXJ2ZXIucGdwLmNvbQUbAwAAAAMW +AgEFHgEAAAAEFQgCCgASCRCXELibyletfAdlR1BHAAEB9+YIAJ1R3QIdiqq78Epz6LO92HZV +W3hfS3G8bOKGDwwpOgB4g6Y69p8buPItQYi3mu2kxBMMAJHCGv95Qiz9NjYxl6uYEZ1qVv3d +MbbGgw4/MmFrrFhoRCbFJa6uQs9io8MTUB10YR1xA0fL6vbs8ORIwgdLuvb5cDZqPPn2te2D +DVXV2l7JlyZSXIlR2s3bxqLHE82Trigx3WiZmPJyXAsnZOKrBTohjnBZuVDij583QYd9TSoJ +asP8YftZ0wNs+aK7GOI1RSAerFqrrSHFUGLfye/HYgChXppBDkHtJFOSWa2N+BPKaeAZkupn +z8q/DbRxXzDgBOJvGnBXmmStV+vrlueJAZwEEAECAAYFAkPL3o4ACgkQL/HwBAJGLuJCSwv/ +X8kSD87x1ui6unx8pSkBA2NDv58yElx5g5JWFR5dmVIrhnyz5YCO6akItD7jpwpOrn5bum/D +6vBDv72yZ6r9JlBhiT5kiGTsI33leGTLC/a51iBJJLUHVVHZcCTiAnYevdBzaRqXwhmtHOcL +ELJ6FH+QFhD2aSmYQNiCNqGFUvBg/tIsrINv/Mgldj0Tdh16lwNTWErZ0kWx193cHDMbojdu +KxboiUCRE+XSWHnYPhYSWUeK2Zf2OwlRE7bQ/L0Ai2nDUfjAhr4RijFH5lG35PsyTGu8et0R +0EkU4fgRWb9z/rsTWijc0+D00/aWcSPTxUFZdaneRBjeDsmxTbywNySL5Vgs2oHlfnlSKlNo +hsVUJzhjQuRJj3pVLhpN5u/I0HIvu4xr/Q6XKWMb4nNIp96//4CNBcc+KrajDPVejRMIftyF +vhwtJKEAl2QaekIwH1OHWRGKPgdZuNAqkLf/Om0lQIrsNCinz0cHSAiggcwcbV3Q9Qp9quQu +HOLEF/0HiQGcBBABAgAGBQJK8BMKAAoJEPyHUjbfiPV2nooL/1IbmljfeuTkx3tIWDhT+v8e +QZXY0F8rvOb97k2RP9Gd45xrkCE4CM7A7QoEYr/yzwwflNtU/J8xuvXEQVnDZZbS2xEM5tJy +vchlC2QbtcsCRGBbn0fBqasb4vf3kbmPU86lXFEyp4ssFNBrFX0V5kGDYAhEzM3Q5R3CPzPO +BZvoFmiLKKekQ5l+I3QS+l0iUlJNKQqja4jaHFjSkMMnznfKKPldK0Dp9VJ8WGvpEi5RkFvT +U9OEf5P2+qsNTmjQoi7JXo/OpR7xLvfgIJetCOpTz4GZcz8Qn1QO8iC2KAvNO3ans1GKl950 +XRqN0Ehy7bDoJmDVKVz7RQopGW/FDiNAP2MGxZXtX4kKCfbcVR+kzRLuYvLG6oVYhKUxJUap +hgF1ejUifQwuGwUn8A0f9kT65vwe94L+cCngbSYcJ8VRtTKZvz1li/5QEy5EAvKc5yfDMojO +OMJXQsT4Vl4QNeNUQYfognVDllpHRU0lkkwazbi7MTXiqqNxIxoNlshL1okBnAQQAQIABgUC +SvSuggAKCRD/lAOxg88U+Z8BC/0Tl3d9+rOi+1xOsq1x71iFGOg71nTS8MH435UZdjp0dDJM +083RcPqiiYVqMD3pMUjrdSIlqU27WRujdVfHt9R/Nu6susgkLT4spxkiOcfc6CCgPRi7/LPa +SjuEkHg+ZKNVE6vjtBOY7Or+iNAcMvAtjiJqgPijYjxthwklyuohO+UONqsLsr2ohD3vFQ3M +KlfShyozDjuvb9rXJ1L8C7JoyyMmhl+ZxjSRTXyTPUTbzJ77I+xSh2oERlkF7hg0jtccZCC9 +8RtH7Sy9lE1Fv4eGOaJuTB4/Y5v9GnJWZLxPDkvIIur7BoAJrdeKWRhqSAm0SANhkGW3AdNx +Y9tBRzj1DkGzmFo+TBq8OrLs+E1zhRuV6z4j6/ghKlezAe5/iOpoUr8KLUAu4Q561qcXixiI +l0QP/a5iC1PAhH7MMDy0mu7p5yGls/iUj1I5rcknRMfj/aEFXG8a2trOOoqmc+8ysg6LABXQ +hVpq7BKfgZBPKo2UM7Pb4ozQA5UWN8m615iJAZwEEAECAAYFAk11KPcACgkQJ9oR9nbSyJ9p +RwwAoqJcVBfbdKlqzG9X77OQ9CUN2IFHsxLMlJJ9gNWhQxZmU6pPybrwn9u3Hzh0WwVCKTyk +vyKp54D0QCBskpoy5jnETykmZpNQd/5FmG5vGBwMXvvryUGAM+C0z6PCbNCcuPCyiBuO+QlK +Otigv/4dn7br2YmcX8L925uTYCfd9g5hmy2uasK+1Oru1xnmws5c6DSr6inVFKi97bDEVWO3 +/Imb3t0jOeEHlI+vW+8UZ2LgGr3Jq+TWAiaFeaofE/cFXe4FL4zYJ8S92FZ+HhY8Pl2JBqeq +BNrxrTI+pBAEJsFk9f1bcRhvy636plCMavBxGmL42wQz6ACEYIfzDFLhG6HaPObaVBEwJ7CB +ecxrnZ0310zL1V8vyI1KLamfubL4kiy3/OQlr7OB6R8I/yS3PidnetOw4yS0cXGGczx31/XL +PGj9N8CPKmYZas9DJ4MD8Qkiv+G9blA8x1BDZXB8Iit76AohfQ48X3LjR6wCXqSkrWlI0+Cu +hOqhe1QD5XKZiQGcBBABAgAGBQJNxrhuAAoJEA1BNKKs35Hm8/gL/1q0uYm7Fb5M5b1f6S4/ +vlH+eVDkYQDv2XECTWIY6l8paht7h7A0WNL7BNG5PCY3ab8TYoxywSDN/CCtGBGNM2izmYuH +PeZcqSMvJEcpCnLDVcz8sjhdN937sktvy7Iat3P8wtEia2Dp6HOkac/y/OSzkmogcPmJ7ul0 +bJJnFlyh0ONCmayuZPINeZx/+lhs6fwpURerjrvzxhkYtzRH0RtpaPUNnLiJAm8G9tzBn6E2 +Oe2Ib19qwQvjXtFdbEkfVpR2GKsh0IxGqb06oC4PJNyLmbPOCPw2IudmcEMlqdRFKg9UoYrV +fCDCJcV8gbyvpYgci/7jWmRg8aS6sQD3LxXpEi60USENWzGqEUTHRC840lrCVqPzWGAyatTO +ApWtj6L7691OihXRysI5GNaqShR/9eJ1gvruA1Yous6Vnq0a3BAwOX8dWX5qx3NT0UhU0Tw9 +qn4ajHx+uNPV2ZseICoyelibUOaCldAK/rksTttxcSCD28YKlsw9slEXcfu8OIkBnAQQAQIA +BgUCTin2pAAKCRCu7s6/+NjxKByMDACPfcAv7dn+hd3T98Pb/eP1imEEAS189TGx8GR/NbcT +I3vvleIMTZkK/aqDpMlmXq+tp6qMq3lIWMI+tDhOyb4R5GqyglfxzCYZsK3ajGoKpl4CYcBC +JGGk/Is4Ae57fU6UboxY4YjN3IQzj+rcI2MvXABchjzeSFBc7UdknvJkrC+Ih1GIZ8AWCxto +W37vOT+Lxvc32qnu/N7oZjznfYXTqyBDYHTPToIZPLNmMl8+M5/9ZEe5J2hsgW4DBv9LbTmz +0c1BQgSqkXNUPmrtA36CTMD/WHK/ivGfCN3wIbeGJi6fvypikfvky6KjVjOW4uRfaNJ5ssyP +l6oHx9nUkV8D05yB2oHFMAHN+IWs71CFTVf9nUNPg3XOSk2A5HvSf8KhFX4AwEYl/+PAaJCK +QV81B9b9hgs16d01b5XB/9oiWmfgX4Txf91qvwkQWxI+19CeD10XLCqUOVCFbvNmgT7JBDx1 +rhrXWWB9lxWo9mViVwuLygihya79fw5pwqhxDeaJAZwEEAECAAYFAk54DA4ACgkQH2ytYtIu +JEQi8Qv/czOSZm2G5E+vNUGpvYA8Jxhubba6szTNVJpsBFYORX5G7wVj+RUoV3MnkBZ35fPs +xt5sAGTXM7kJZEUsIvU2DRiX4XDTf6gFfrULkbIhY0a2Zm0N5gtQsjXBJ3rsh6KyhrAyzqFQ +2Nmxbe30BhL1CsQKSaYh4dsX+2kKwoB9oOGGi4SpYt1uTPWhkPFFLWj+IrxTWzt9jEogENfu +pgx7MwmpVY/bHvhPo3pZ7uHDPEufRX8fuhHqWp4M7yyQL5JOjfLle9nqplkaClSsOMDyedrL +lReS2lsIwShJqCR7C703tgkWyO5EZyq+17n00fc+4fQ1cP92rvx6b/RHpUeD64bVPvjaWA09 +leTUVfkJYhFoJkzJYFYjUMHzsOQwU8YnGeXbuTCIJ1ZrDR4ZPr9UEGo0P1Win/Jq4KdQ6HTZ +VIs2KYG68GXjqZHg8u7tS0VNyxBUSBDVv4Zvf7fvHhVQuYet90G7WBoSuOgleNnk+hmf0c4D +fUM4Ih72acSzj9XmiQGcBBABAgAGBQJOgLAzAAoJELoWe8g0SGOj0ncMAJwXFKmAqiUtCGM9 +KUVu4263wIv6UcJ3Fh/toOGb+/FoFTyur/PmteAkAx6USLGkqHtplkMZcJrLDbIP3oBmMyXb +LewgtJXN6EA/V6odM6jLKgFzPvR6azwgswD9k2HA1utbp4XamekvXHmLeVZ9XkgFfco/0tF0 +fcJxTqyf4gtu4ujT2EEtxNrvoJsBmPW/WVV6qmsgwZ8S1hBt6SECzygI48uzbfHkt1ClsQmW +MfpzTZkkgFl8mNP2YwfDoLtNCK140WJlNxWVBbk4AbhA7UfNzca02g3IBaY5jGRtdCuDmBG+ +akNWjs//wwmFZk9gJ6Sm0lRZFwwxJk7QxSoNdC/MzZB/z7DdnUwWwaLm9db5ZiYFVRpDYAxs +MtqTwqQj7gCj8xg8osC4krmuhicvDfdxJB1TSA4eXeJV3Cmzh/rP/EmEqVaVCmEK9D7JAMS/ +T9an+Xw3PdQghc48yqoRzjBM3y3TGKunSHx9xUwPLQrWbwmjpKKaj+3lHl88DD0EAokBnAQQ +AQIABgUCT5qzcwAKCRBXRYXjSTKuAQSfC/4mNf6BFBzLotFfnYcDzmsUe5XYudWEEnwBj4Vl +af7/ncB1XKJYS5gXRFjPUtYBp4EBRYoWw2qVDVwJ89xsxIEcWyye0kDZAtjFM1BlU4c3yErC +jNDQIE3jefHkVlBJi7RHDoRfGPyMZYW2nLtZvtSu6u/wHOT/UyzVOfDieBWyzPsxTbLNdBJj +LWKW/THzusInk/UipmUaV2oiSPueBAM6/Kd5oz+LanzOw2CGN9+BUNE61kWIPOJA8poCEKVl +gvIo8kDRaP0Q7w3Mt1K09DydplXK3fgPp0u+xhH+PskvnCnaL30U2qOI7k/8P8qDELE0Fyfi +KEE3ZAKjXIcVtNF9SV43nWzysuEhYRkG/8Gdtl6Ij9wqfPWSSVwiOdiOREdPr7//JW3u2gp6 +aX2eBRZ2HIp7Y/c0kMKSB8eTREun9Ll+nHDAnT3sYhficUBgynncN5R0ixJhsaIaqVoZ3kKp +Sw/kzCpPR3KN/Y67whviRyaFzTG1ZLgxPCjcpUX9NCiJAZwEEAECAAYFAlBVLTwACgkQ54Ny +jqP4xZiNDgv9HkvZRV/sPhGAlscQkAXFYIQzNUTJHFMNRfgtsxRdRLb8xNbAjIx3m720JfMb +rUxf3SP7ovyUrWji5NkF3z7PUSzMhoBjsBSz2TY72TWaxmg7kMgR5A03bIiljgrqBOdFIuRg +jSps2aP0tBnCTwFOPvSt20kZZ4JH2l38iPvEMdg+C3gRTeN4N+6JKI8WOHe8kLZ+BEPRVlnH +SGzyhHHKLfYjOAG7FEgCj/grLK5QlRJ29jGsmZhHCjw6HsAK7g+K3lqzYo2x5LwGYpN3vfUG +FN5p4Ssar5cMSD/mPPWPXDoak4J3MH/lthEoZ8CSVl50q8DWY7YDSGaX2uUm45gMLt4uKCId +kByMuXiQLXcT3Q7G8fJXXnD3ec2fjfOGD39E5cs9sLh9st7LG3ltltoc1oSrV/SRQHOiR3HO +1FGzky8oLbt5hnaEr2aHmjV9yHAB3B0gX6u3XUCoinnQltKHzV6+S1KaVQv/qv+sjp0I0v0K +69vbD6zTawsQcI6SKqd5iQGcBBABAgAGBQJQZpFmAAoJEPCQYB2qrYpyMi0L/0x/IPC/2CAu +DmnzGkgAXgFBhx6PPNVXRyO15FgUIXSr+DfQpWXycB6EyGPyqPaAWHTKYgYNEZ5o8TM1NYWH +hePh9OZ2AqX99X2UTzApWM8jIkHphTuGicZs18kSOiw2MpP/0uoGmFkmWW4Aq1w2GwqzdqiA +plllRWUUG+YuW4K8V68Le/IryehKtAdEud8BqwvUHHlW+vNdkQrbCKEzGJy66c+8wkJq2UAf +f9zl6Glahyz5wBAKuokDPflxh3zhLcuvhfE0r1t7MfGlEzJk4yxhi0Fb1KASqaJ31g2Q9CD4 +KA0t56fICbqnMm0/BEU2EdfItpKwBKOPBBMMl4/vJPnD2KKxOEeVF+hv5O4IKLOXkJ/jAPnv +GmoVMVYlYFxwjL2/PZvwEOisuxFKMqSAO2WR6alVMwNDUFmOe6ThkquJ4oTLPwi9Db9AY3Fd +vnswiBzEBeDHyiB4kODWG4Uz7gQBFBgF8/AlxfaGLNutKKUducfhKje2E/UcwYFfHLsCfIkB +nAQQAQIABgUCUc2T9QAKCRD9Jdzqcag0XESSDACXf5bgsc52nKKFreFULpQ1GPRwqHAvw0FY +iwypdT66FB/UY7PuPBobQNAxmGFu0QSbr65jiwGC96VzJ8x3WjYaCXCVlj8e8DGOaq4WedT5 +o0yOwsif1YeoVysepjgQA//EAagGnV2wMDU8Tm7zcZelP49fD8TTrmyXh/4hb0MgA5+tLFUs +90a83dJXy2qOS1F5+/rrYnYFJOGHxEX+KP/nRte/0QmvWM9r3W5Oa0Wm5EHC0HUnFf6k5yav +IcqXyBFwbqzw9ODfvCjBdx3AjoWqWv+aRtbIqIHWBmueZqYeWqKSkpUQK7JZ1g9zPZqZ5OSD +R4n1+RXFX2F9G7mCYWpMk8CQXsCXY40Sm1pfN0K9OCLFE2RqBaXG6Eh08GabaArKW8WnUibr +fX6o1AjO3E4UW+cUl4BYkceEsAAfjgzZgXN8f7stilTevIgtM16fJtvmeYLlZAaoC1tCfAul +bAnWhAfNBiyO9+DWnjbq4cmZJza6s2B+kWGlEIwZVikPqnuJAZwEEAECAAYFAlHO0WEACgkQ +F35mgTrEpmgjrQwAr2kG3jhUEB3fWGoX2DbhwHp+qhx7BRpN/yuZnxbZwPgp/6kZDFk29lI6 +uTyiqOyKGa7TZutpBD4k8FtNXPqD3iKiC/+3GJz8d2gkpY286c4VRAXU14QSWp7PBD0peut3 +asJsHW1o1dCZ0uJBHa/nY+AIlDAmB5zQA1fB82Rcq1AxK5fuqotKjnYzwvIU+gyc97QHnXhE +QoMGSWl8JjmEkMaOFcbo2aYupH1AAB0nk11zFnc1AtKnM1JSW5XJAMaBPa9oJSj55HjkM6oJ +oietqBistemAtbIqdjl45Y2ShpizRnpS2nOu4gFqu85pzQu6ET6PmFW1i3XjN8GD1rYY5+bz +zixMdF1Ben1HXuozddUsLwh+aNEHwgULTbOgbCpKL9fpWj0r70DbNlKcC6O61oy+wsj9WfCI +UeaoG/9GyKyz4eeatzRSHx1ueORsRPVqEbquHzAiva0Jhol+hcAqmotK4FOE0kaZaFBXDC9q +xJ/kmA8RFXMwRafD/JLRhcGoiQGcBBABAgAGBQJR7XijAAoJEDBuNTCPH9g8fAwMALC7alwa +b2nCaSQ9+vRR/A3xS/q0Nez5k+yykoADGxF09sU9WnB41iCXySJnAlISInQfjG2/FXmbWwEK +e4wjTbshQPjSkS7kexZFg/nnafGbxVdUchTo0bVggodlrrOWP0vgyGWYHwmsregsd4U2MHLq +eT35fdNyJnmGh0RHJcV2JHiCKr/peHvVE1xPk3kZhj7VGbtz3w4UW8cLPBmNDEGIvEe/UoSe +RJJVO97y7cJNqyjg3ysHmOr7J02sBzXBrhPZmvaWWhyHLVlrwy8HQYuFktecvbW6OMK2DQEk +SGwVRyQvGis2zs9/rzeXXCNHJWMEc1zlgYyAL0m5KbhpC77/Vy2T18BLMfVgt4e63xGT52BS +wn0caAEZTtM27kR9QJSF7BbRmuqOpoP4fhLrrx6Qwu17mB33QXfvKohRrwf3Si2+UT6Uj5be +kq2vsPOizyd91VidltMXRRd4/cm41ogEhG7hJvBzhmH/z5L8ptHPt+CZy84bIGYKdTy+wc9n +DokBnAQQAQIABgUCUe15HQAKCRChne2w4KRJMkYPC/9GtOJAIPCdvmVWDsjMfitreJP0UFMG +BXCrZ3eP5xlyD2OElel52PFIWF1deumE2fmNFCrbs3bu8fSCMTYc/Q6wjnfv4kpl6KWh/xeO +cDNzmnxaYj/CI7jfFXVdrTHce3moMFuDrMOxEyvRxduTz26YhBy6zQ6WUS04k1x41jsMrN3N +bfM8iPOyTiyd7MFfvJZbSkZHs86T2c9B20NaEbC7SJ7AZBtZRMP4WSrPz/tWKovkstulzyjS +itBVKu19S+s44aU99XNeIPHGUzVV4noJnZkPyk9Ma2I6/DA5ja1fBAmCC0rwOIzLYzTyUJ97 +K7y0AnfAvc/3l8dH0BwQ2Dq0OdAXd/Cp4itZgxYsNvyjnanyHSv7RZb5FpjW1BVRjTSJkkFl +f7xF/PnEpXCW8sftoSP/WC6SWChJKqy6DX2dxmYdBKqM+6BTYgEkhgMmU/1P4SteWDeffBUl +rJBFjCs6FMvrDvssmhAX4/Ululm8Ny+3XJ/6Wr69lzrBOjxKn12JAZwEEAECAAYFAlHteXkA +CgkQVCkFaggZ/AOq2Av/QRTcdg58SdnnT6waT7YKD9QroUeWio1zhlxGD03mUyMUB6k0SiIc +3Wkb+O8xH23F38ox9VGAgtEyrGBAOIf4HhnviDve4aF1ph4ZF5/W8bZkVUoIqp7V/K27ZWsq +yeWrhgcaa1txRCWhuYUBqGYXG+r+drjW4evevozRmolGMFvLRSwveexPdtcfa/4FN6/A4w1c +lo16x8zeQyuVQLKJVXZ8iywah7X3mAhF+8EuAOXvNC7De1HkRPveTcD5pvwYpGwfuoxdtqVu +o2GQINx+ufaVxic8y1fdHZ2xvWjyRpaGVQoEY5z/SanrFDfFKLRV5lZDE//MNKnsRTmPKVZs +q3JU7wPUTOOpwnS8zC01MwxzX+RX+hjKgGBhn3Ez4X7bb53rPC6Plap9/s3PbYfd4qhhapwG +2ztLkS49JgSDI11iquLvYRqGdp+aBOf8oKJR4ZBKb6rT3gzPEmv6+2D5lB2TFuXsQ8fefgwh +54mmqzGCFDLeZXV+AvGqST6NlgI3iQGcBBABAgAGBQJSLmzLAAoJECrLnuugPRVQt0oL+wYY +kWa8/gk/+OEzN8yQaq1tksIYe5xtk2MoZ2cX7ABB432umRGo79OMPU56Oqwo9kl/eeLS0/Vv +nfnFsPydYzBoYTIX20WDqspYDelh2Z4Ks/ChpoMk2+k+ezsQZKUxyE5XtX5dgULzkOR1+P53 +kgvzn03UDrot3XD20+vdnwaeRbl32hMnwPErstVTOVgnTzV32OFp/Z2I/wNHMGz9iZ8G0KsX +k4RedDLqrxLelsUC4tU7acCp4JxJ9lhSqngZtJ9dE0n4fuE8o+4DAxo06iC66YSMO7zXOOfQ +k92XxWTp1xM5YKfaZ8UhseUujWMDR26VuFHWNCw6aF7q0Ni5P13wHKS5v59jdBtyqkmWZ4Xu +Dx0FIt6O7Zdy5E6ZHaIR7JQ0MJSKweWRxVTjVzuSUoPDCq24TLdsqUZ+w+h24o0apaeNdIVm +aqWNrr/KIPNbrbth37pKzN1GHKzZS4gJcYMrWzPdZp5rku39KeX4rGPDm+Ua2uJG1+wPFbYx +v982f4kBnAQSAQoABgUCTqmjNwAKCRCg8hPxRutYHy41DAC5ccTJOHEcbWzcYE2BKm7+vS6m +Qsp/GxZ69dzy2K90m+f9q2Ey+WkQLOBanj789NaQffJaflf9GOMEQwZbciDi2Cmy0pN5u1Cd +2KzDBESPNRtc1jN7Ixe2zEsmDFsSDaq0tJfTUOvNaZ5fYHYlnRg6w50+PlD3cYkqpTWweMK5 +oIIS3Qg0Rv3LvsGDA5RXHZf100iURSHc9MiIPkIlM3skZ8g93/HomWm6yt6o4NY1KlzZhoZz +RtQ8W5mAza0ERoYdqAe84/Fhbm0SzJ1QTBXIGDbT74IaCx9TnpBbumSLEVak2yFRq924kWCH +eeF5LF2cqLko9jpUlQPyRMYjuFRfFIm6wtmlT5oSNkMg+03J8PXpAMsEWBi8Km31rKYVvMyr +wybJvk3AzWZBN4Bi8BypgQOX2Xfmw9aZ/8OfNCk9klYQipCkZ1tWSSHNxsCFo/cBfiAd7SsQ +g9aZeDrmRwiGi3civPtNaISSsrUQMi1FDrIMPKaAzORZnmuKYaFrRKqJAaAEEAECAAYFAkvS +yPoACgkQNfM+8Lbf1QxQJAwdFabrUyA1+NMeaLe3mzCy24G1BtWhXvRvvdRKIHDN5/BRIv37 +oWpfMnx3Y7BuBt6s8EWRQDvh0lJBXaApghkUDORm5ozoRePiVHL2UVhLgVfVh3QxVgMEoyPV +Mq+L00pJD87lYl7oGAMvAQyEmeir46bqYPVvhhjZQahAnCSXs1R5+2CL/DMMrE6CqN2oynlx +YzmeOXr4f0RzWtxDjk7Fq05hqIi19P+k50VUNVw2Qwny9jvtjVTbeEHRzzHeaDcragJNtF37 +T8rwJ6lvthJ3iIaat8NAxxLQa4s1Yz2MShsdm5dxysmwX5CnLknXb8fUBmMHLq/wXyW/tT1k +07BT0MMWsy6aOBRXKhg03QAVkmOk6fNhURmXRsSh1EU7WUaXRsUNT/uiD/ny5B80hQz0iwut +ZyjLVfnZVaU0MVP0QZC31xSvpIfi6CorNLA3BW9SygRp8AoOm6f0Ljp+doA7TcCwP4FMJaKw +31ORJaIZVlthbsnPEvVvawad/ddPDsy2bTLx+IkBoAQQAQIACgUCTin8GgMFATwACgkQru7O +v/jY8ShAwQwAuHNF76WlYZt+RgP6cfDDdi5moyPuGuW8yFXfsvnozDkyCLKRThQo+uME2mWP +wUI+4Dlend60BwGHhVlbLWUBbDLybQpQq9b/Wo+9zJTzJhKhz6XmV/cT2ZUx8dTAMLf1l1DQ +eqNuN7czJcChy00r04xfb9PtcjJixjftBNzWVRAlJcdNDMwuNyGx/sJ5hzOpewSrrYK8EZnn +SoNHaVhlAJbnXiHj/rcYsFxtf22fiJj2/kgC5aiF8arkO3I3LdVf9scRUSkcCXUQ0G2QEaoL +LHUcRuVJM8uOY8ZofpKsCgjx+UY7vS9bD31xm8g8ReuERK2Bo2X3jLuXQUgqQxMixsYOlHYv +BU0tQStMtybbFqW8P3X7IZQHeB01bsmlaJc900VxvzER9faNG1jpZES8FrLxKA0khaP8N3Oq +vA1I5h3BkBmu+2f1X2j7xFGGRsvGGKTIcHbMsSbU1ateiF5joc2aQQ8i60kvUqs8y7Bnw0+4 +SRRPPt0B7R/xI1EYOjvriQIVAwUQQb9QFtxTisjDr23uAQJD2Q//U4RkOChlQadeIPWaMhj2 +81KZQfzl3E3m44r4d1+Vp46PXOXbDlY3enIYn0I4HFUb6KWY12wgbMBNHlLBZt0TlsDwpyD/ +wHS9cUnBK16JdPvpWlHkI6VcX8Vt+p+w4avUt8QZt7V1iVEpniuQHorUFYcRQdHn1UT5vTZl +2Ph6tEyHYo6MLv2WT5fNQ7uICjpY+KFhXIjsOkthmmus2MNq27faQLAIhcSCr7tLU1GzxSFL +M9wvi5ZWS3vyPHSTTFZH/sPTlfkPWHRtjinWhXSJp4H9aT/NdyKtpNd8SO6AlXAMBl5SqWGX +sjEGHUdzQS5/wSiZ6v1NDXPHLBvXa2Zwf2ovQUrPNVU6L6gGEaNWlL1NnR09esLOapVcxVwL +jUFGIrTc8moceOuDyqxaQV3P43QXQeTCf//XuKaF/LVcP8Hc/T7TM7cbHhIllRQFCCdnWxOa +L7Qz+GuuDSzhh0njb2wwI5j/LuvW5rz96YfX4HLhdHiLxbmgs0MG/enRqpX0wv+ZwTHdgAiZ +Q7IPt5ApjykRqxlhllsI5q9AwJOw2GR5lBEwHndy8Y2z0eVOx1SSqy72yz7Nsg4KTbOIlJjM +vm77MYBn/kt50c4w/wP5gxpwh3mb6zwcoA/SllnikG61pdwoz4e2BdBCZyCWQ3ENysZK5/9D +vmi86RyV/XbDjLaJAhUDBRBBwLMthux6FGOytHwBAm2dD/9m4mToRUKxjcyn1Vz5uiW9Som1 +7De+mkbIBSV4DqJlgnfjB5yNZ0fNDXY0+OM6nSQjHYDeHGgU49N44p1llyTGgE1pmpWLxV50 +mXd/Vg31QZySBFb+AfgDUIg/+h6BQidQ3rK6r25G92qCDWg68z04vtygOxAwcVaPQF2eyfsS +aDZRN2cFjymzRudhCLVrKGE0EYa3quB2m/DbEqIEr9ubcRRltrYY1bsKG20bXBLvmGFLUk5i +XiaZtpeaomELUzl99Zf6uwg0V1nilv7gAFF3dYs1wioPDyC6hdEtqm7WVDo+RbmuXz/jZAzX +R9j8uUyuLyfEyZO7GwRRK3nJeTl1j9J20OZlb7/vvY0yeZJiMvryKgAKhIMQdItvaGDoTbhN +L3r8+MqXgqlur/UBt+IYVtKGu34ONUcTVzD+BRjme+1SEmI1AmnKfJjMurDacSWtB7cG8KpL +mxdSRF7EobyO51BUCekxgRVxkOF9byrvq+XnsAizvogWE82Hzqpj+kuRCfq2HgMSiDXKqZrY +BwicXC6FObF12nM3SbzRLwbTxZJzn8aT9kHal4Cab4u0c7XqYTJ/wsD0eUDrbciMxo2xcjjY +Wf+47ygGIt9SV7tTnEW7iFMLoB9RlhVEgWygLBA/eV5NY9+8baM/WTy9zqTVpyw5765O698H +bzalc/NFwIkCFQMFEEHB+Toccs8+8pExxgECVp4QALYTWNt3NLVbRSvi9VNeoYfWUB272+Ia +fhaYzxwzJV8MBjyl87B4KKw3+FPqYS2ziJL2UDva0r6jbQ0X6rqBGuuSJJrVVum2P93/faUy +X7X+WIg8w3oFmRgW/LMkNN0fl8290lt7d8KtXWNjjeGt2VCm0yLt6a4vgdEOrfq8Lm6w5Pqb +7ZBqPo8DIY7qDKB650GkF/wXdIR38IDdXru3Y+hCIQROhp53w8O5QGEjcZeioj432o4cQICg +6bEnZo2UCH9B5363oxY0mdCc3C+pICquBNDcmimYgNeF/6+vcLFixBHeo8+UiU3EhvscX5bg +/UgmHSu57q4a0wxptz89LenfugKbTCfvMcRZbi8Z41i5A77dLwAJUQ1CX/K1+PD1E37qbBQt +0Drg/JhCZXzASvg6pl7D+oBcyn+VnIO+MIGgN62T56Ldq0oEKsf0eUu+VqxwEuCptiXWdiLx +VsqJgSbjhQksd6SrgOeCLAOJm0nnlXFKBc2LXn/klmwBAUxiuITI8P/m6AHM2pAhizpx7jbs +WW8Hucr9XGOPg2Iml+upfrl5uKyzvFRaV93Qs4me1V4++dzyfWJGl0Zp7tX5xt7VLI6Fj4Xx +qYvZBMk7jRh6amVUHcK2Iswn9stbeNfq4wAKl9FaCxHay/e5kHAlIUZmihbp6Z8rBd7tkOIp +cxugiQIVAwUQQcIMREljPlxiKUvBAQIlXg//cQc5RMAZ2yo0aghmp8AzRW3fmYF9Qo0QqYV7 +ByRZfb1M5az0VvmN7MkTCrAoNxYCgD5aJWK96+SA0oBx6Yf9AU942XjkNhnacm2u0ny1WYZi +gb3R8c73YJtR8oTQ7QIV5pJdoOiPKPIedKv2QKynlat2Axhm/TeHSsTpo/rwIbLvgCqMV/ql +822AnIDfiUnfztvhbFctNKJv7NMKDI9fNo2LftnwyuCbPu3QeOKY/MEKKTdQ7/5EIeyv1pig +P7pwoolLbFWCGH1ytB66w0uSUSyd9bfJYKXPgKJWsJUkQz4boav3XmaTwtqr5+tj+e9vO8Z0 +K7MpQG3E/GzSSe3/5+3jGPZW4QqAus2mtppPxzehxOkB8Kbj5MOtImirvu/JZQ9If/NT+L+4 +dqgUHI1v6/x6bTexbwBXsqGVVkrZByn0e73IItgazXYs5ZIbDBt9Fk/MJJspWLhcPH59cqQk +6TFd5clmA2FeazmKDEm1xz7J+ijOmc2u7DHQ6ZcnHlsbs4eRdHIaZvoIQ3kSsq01QVcHaHaY ++gh13HFcleoCsgrvhU1mI0tX9veT+7yzv5wwSt1MA133RRN7zE73frMSa9GRC2gHYU3JkWUa +fyCJuPA3kVDimEKJpYtdO4BFLsSYRK8JmvWGipQ4ROJMXKD39y9dSIj4IgZETscdUn+j60+J +AhUDBRBBwgxYtSskcsdHgjYBAqBoD/9J3w68fOYp6bpalS8co0ABqjZUdgI3mBVQgsGikjVn +CTFQMmORjtnoxpztz19qJ1K7Jz9J2h2M2sDamsLwKHAW8Ni9PISoOzIg5uYBOAdf8K3zPVjg +Zttkae5qyR8diC6eGhvDnxn99x46q3lE6qezd7ORTiMHSZt/k4l8NIQRuxpx8HIBID6GxgfZ +zWxlL9nBc9Hj764h++I+K5f4StqLWNKKe0mmJF4WT3SNLPIOmERqwrmhBgcFHAr+opzUCUBm +VRBXKHiSHscYlKrz5ap8Oux+b15A5Ybxhwxdp1u7hmoY7Mz/VBEd1NagDKG6S4hL5uyxv0WL +QwyBsvECyYHfVlNE09Q5iuy164LJUspa9EM+aC8u/e9uY7JOAEwPvcddTFUKMv6PSX2JOItJ +82nM6+xIlBiWDL4jSjsnudjJvQI6ez6nbimBkgbykasafHEfcVbnHD0vzLyk+fWGt/KAjPRy +ZsLeWateQ7tU/YdqaWD8jha7kpe1fqPasWBHYvwtUcAcD57US+o+4F0iXfWliYnfkOqir77+ +zMmpvPWHEURzl4yuOGQEq7lcCHthB04xhUUdRyL9AchQIb9o8rb4t05u1MUlxVq2sw3DVPcb +Qj6FNfWsVMcsa0m6ze4M//AwyrQQd+7RKA2/OCenO0d5JOfV/eNRkFfkQ7ASX6efeIkCFQMF +EEHCDFi1KyRyx0eCNgECoGgP/0nfDrx85inpulqVLxyjQAGqNlR2AjeYFVCCwaKSNWcJMVAy +Y5GO2ejGnO3PX2onUrsnP0naHYzawNqawvAocBbw2L08hKg7MiDm5gE4B1/wrfM9WOBm22Rp +7mrJHx2ILp4aG8OfGf33HjqreUTqp7N3s5FOIwdJm3+TiXw0hBG7GnHwcgEgPobGB9nNbGUv +2cFz0ePvriH74j4rl/hK2otY0op7SaYkXhZPdI0s8g6YRGrCuaEGBwUcCv6inNQJQGZVEFco +eJIexxiUqvPlqnw67H5vXkDlhvGHDF2nW7uGehjs3P9UAQ3UxqAMobpLmEv27KG/RYtDDIGi +4RLZgd9WU0TT1DmK7LXrgslSylr0Qz5oLy79725jsk4ATA+9x11MVQoy/o9JfYk4i0nzaczr +7EiUGJYMviNKOye52Mm9Ajp7PqduKYGSBvKRqxp8cR9xVuccPS/MvKT59Ya38oCM9HJmwt5Z +q15Du1T9h2ppYPyOFruSl7V+o9qxYEdi/C1RwBwPntRL6j7gXSJd9aWJid+Q6qKvvv7Myam8 +9YcRRHOXjK44ZASruVwIe2EHTjGFRR1HIv0ByFAhv2jytvi3Tm7UxSXFWrazDcNU9xtCPoU1 +9axUxyxrSbrN7gz/8DDKtBB37tEoDb84J6c7R3kk59X941GQV+RDsBJfp594iQIVAwUQQcJs +AV8OhxiKiG0qAQINrA//X+Xs9TVhkM9kgaR9e/rQmYvQF8NLrmsI9qjMxVZpwbJ2U8l1kYa0 +QE4WlO7A/Ct5fQ+Hji4fNZ6GOl8EjKKBJKj+fuvNptPQu/fOq+uBPHPhXUpX7D9zj7+B6JFn +uWTD+TUXO1hlD1qJ3vDDF/LdNpwchiKHDV+/eR+NcCS3jd5T50ezI7UsuwsTobjRkPJjk5bM +DgmhwNKMh0tYfw/JmlBkgp654r0IvBCa6iHfYu35Q3pW+/81t4LIwzPotHyPSo2xOnhCzgPZ +jMujnG4MkFFeLLAhz/sq9mHvawS4PsizI2bgSVY5vmzeVN6TD5hVec0UAgvP2jGMMSw8LTHU +dYqD6hcYwxYBMBOV7KLTFzHZXZMX8PZ+QpxfxyTIldaG7rEm8ASLasdMDHse8YxQLszm7IGj +sKKQ42vn1BhIkcx/I27U5PFLnieGB7jy3byJYhLNfHEFGYkraGcFYS8csH6GMhX6VRxDZqIq +8chDu2FUgnPSfmV1ZkWAAr23m6Y3FZuhpnunCcTYsijvUGvt2fs11ncfCzLh/dUCOmO91rlt +3yJLqhmQ1lEP1qkMYagBmC9WnEGfRcyuf22Nxhqhl9ey1dC/YwVeHIWBvRgdb2ykxPFQ7pTa +63aUWxW9iX+/FWaZGAhmIHh475NBYHXXYBYeYN4unYbI+KEyhV8QOKeJAhUDBRBBwm9lvjCx +8Xcb7ksBAg30D/43YONSAi5a5yNRDikGC6nAwRKXYxd/29ZqpJMczZTMq+14Z4L72xpn0u9h +6LmzCoRaOxUcYJNzlTEQRFJzcLbTqhypi7ktL0DM3r2kp9b+hyBNa/M9jT/YnQcPE+joMJtO ++ZxHXa4A4vZ1hkASpg77TiORP7oSx+TjkkGrEJF4Uvlf72/nqEmUrUBQ77OFPLT6EE+FJ3cB +uWOVgQQnoqAyNZXmVOLxJd3fFjf40vEn0F/+24C4j0rHUFYByuyHyw+rmxCnjSStUzPbuNDq +W8BKHNQx74AdkqD+otKDuvjYoJiYOicocVaDlWWr1yapeWmYSwS5a0waUkS+G44R0TyCIU6d +gy91rIr7AMB6cLGWNJSOTKy6ZNX3Bhh17qUW9if5/7WQrGe7sn8hEKJWB+Wt5uhbKDM+I3qi +lUX4hrlN4nM2+Eid9rf05+/oK8RP9WmeH8axPOpw2Fm61vTTVU4Y+FMHMQUFfW/liRxCwhr5 +ZOKfqg/XDXwfLI/n/D7duydB/ISbnud+xcvNn9afPIXjyni2mHEQh8aszoE0qzsS4tdo/z+f +rEapfyUsYHGCoD9J83p7bYnGky+u7yYgZjxsgh+002Hd3mQ52kTEklNOgKJYU32Q4WDLqZLX +j8FIsort61JC9dLtuK0wSY0a9dKkR9yk96Zps/Cr5Fx+Ezxmy4kCFQMFEEHDJKQdTgL6WBFA +pAECl3wQAJp2M7MBz3ItjSEe2iPp+rN5bERxXPzXTQmQHuCcJJdZpmalS5as5bdpk+3Jl5K0 +aqWS2Q+vMW84Phv0U69zp/TAX8wGyp4+jcAW5dI3pum/yQXPwwb9Tj8ZxHsifXc7nV51ooik +eGvOq4tYCcikw1caSpeuqZNYqCeW5/F4dgvGh4rUgjD9wfWzrHtm+M5OWUryuIP9pt322Ggr +xyjb6jlHQqoo0qAmAkGTjp4Aa5Z9xz4QkHomTHlKyl8bS2i7MR8302to7zydtWII9qM402RD +A82D9PRSGl59f0b9DKtLmRTXvPjsefGkdmin/bFyKZCcLc2KvXhHJ1/aXwMlM2cYG5TjO/dp +nYKEd4naE4x4qklPdpLPFB8G//wO8bTD9NcEEFzl4WpUUZPohiT8NY5g+VPU2fAIRuvBTRVQ +eLApGAm8Nucoi+lVlXosG/2mfHfwpzdEA8OWzVtJ4EugZb6j68G3WA1DNq0SOkRKMbqvzRFX +UNyjPx84NX/F9SXBZn8xZcmSJYtYZQ7uPT3JKP84tpJolmwF17SwYGGYs+DTUeH2riSjoZsM +3IJyvPW8JTZ69dOXdNCPA8Epa4rszGgWa5WGWJM5f4NXobVMHWXIwCwcejwF+lT9OaFfkRS/ +xLUUNoEU2hgQxegYqXu2/mkkF1c2adUYirl/V6dI7iGYiQIVAwUQQcNjVImkvBtYMMYnAQLn +kBAAvxkjHtEg86rBWtrpoaMifoikWqBTMT087nq7Ov7b8NZusq8kKMq+o5o8PEdMo7fQLB22 +K+JuXynAAi3aRQCf8VW3XD0en5wlMAq3m0Cj40EKwYMSP/695pwbFgpynw4TIoFzWAR2o0+K +6RM2yLqLO8VeNGW7L/jcaQmYKumOxHAX6A1e5ocCsxfCB96bZRIzt6TT/zSbiFfSoaTUu+hw +yePEuQhoqgK2Ys7w9TbB1zinlGsbV0Y7qu2+ZhlkDbY5q1JPByShmboDtWEH9HlBDERaS00Z +u4KyhiNeoDxrA6x181XohmsVTtreqJbrdPNXwgOyvX1HgfKA9Pj1RSLExvb8vGkMQTmMV/wp +9I9fjhlCSg9bFxwmhjx7fb8xyC0drLmPDaXI9KlJxJp/fxs0W2k9rrPAU5DDOgMJYWmnVMh3 +Av9+snLdxBBhyFPxsLCymTpY5LTYYFIAAvO0ksuN2EIBsJmKlFT0CqD2BSUS6Uvy0FuwvQqN +I2rxbXOqtZzsJCx+uL6Aj+U2TCG8L+yY920kUxwJSc1ytm7U1eBU4T8BE/FB57DX4lQd+dVB +HosdigPDvBm74u1fZUgmUWecLts9hlZEHXoyMsGZEtTTXv+CgkoRjTEIDnsAPsIUAgsQPv// +//////////////////////////////////////+JAhUDBRBBw2NUiaS8G1gwxicBAueQEAC/ +GSMe0SDzqsFa2umhoyJ+iKRaoFMxPTzuers6/tvw1m6yryQoyr6jmjw8R0yjt9AsHbYr4m5f +KcACLdpFAJ/xVbdcPR6fnCUwCrebQKPjQQrBgxI//r3mnBsWCnKfDhMigXNYBHajT4rpEzbI +uos7xV40Zbsv+NxpCZgq6Y7EcBfoDV7mhwKzF8IH3ptlEjO3pNP/NJuIV9KhpNS76HDJ48S5 +CGiqArZizvD1NsHXOKeUaxtXRjuq7b5mGWQNtjmrUk8HJKGZugO1YQf0eUEMRFpLTRm7grKG +I16gPGsDrHXzVeiGaxVO2t6olut081fCA7K9fUeB8oD0+PVFIsTG9vy8aQxBOYxX/Cn0j1+O +GUJKD1sXHCaGPHt9vzHILR2suY8Npcj0qUnEmn9/GzRbaT2us8BTkMM6AwlhaadUyHcC/36y +ct3EEGHIU/GwsLKZOljktNhgUgAC87SSy43YQgGwmYqUVPQKoPYFJRLpS/LQW7C9Co0javFt +c6q1nOwkLH64voCP5TZMIbwv7Jj3bSRTHAlJzXK2btTV4FThPwET8UHnsNfiVB351UEeix2K +A8O8Gbvi7V9lSCZRZ5wu2z2GVkQdejIywZkS1NNe/4KCXeJyK1q+UjT+CKKGec7TOYEdSIZR +V2xAwk/DivnQIkuagMiaLrmX8GuzmhflT4kCFQMFEEHDY1SJpLwbWDDGJwEC55AQAL8ZIx7R +IPOqwVra6aGjIn6IpFqgUzE9PO56uzr+2/DWbrKvJCjKvqOaPDxHTKO30Cwdtivibl8pwAIt +2kUAn/FVt1w9Hp+cJTAKt5tAo+NBCsGDEj/+veacGxYKcp8OEyKBc1gEdqNPiukTNsi6izvF +XjRluy/43GkJmCrpjsRwF+gNXuaHArMXwgfem2USM7ek0/80m4hX0qGk1LvocMnjxLkIaKoC +tmLO8PU2wdc4p5RrG1dGO6rtvmYZZA22OatSTwckoZm6A7VhB/R5QQxEWktNGbuCsoYjXqA8 +awOsdfNV6IZrFU7a3qiW63TzV8IDsr19R4HygPT49UUixMb2/LxpDEE5jFf8KfSPX44ZQkoP +WxccJoY8e32/McgtHay5jw2lyPSpScSaf38bNFtpPa6zwFOQwzoDCWFpp1TIdwL/frJy3cQQ +YchT8bCwspk6WOS02GBSAALztJLLjdhCAbCZipRU9Aqg9gUlEulL8tBbsL0KjSNq8W1zqrWc +7CQsfri+gI/lNkwhvC/smPdtJFMcCUnNcrZu1NXgVOE/ARPxQeew1+JUHfnVQR6LHYoDw7wZ +u+L///////////////////////////////////////////////////////////////////// +////////////////////////////iQIVAwUQQcNjVImkvBtYMMYnAQLnkBAAvxkjHtEg86rB +WtrpoaMifoikWqBTMT087nq7Ov7b8NZusq8kKMq+o5o8PEdMo7fQLB22K+JuXynAAi3aRQCf +8VW3XD0en5wlMAq3m0Cj40EKwYMSP/695pwbFgpynw4TIoFzWAR2o0+K6RM2yLqLO8VeNGW7 +L/jcaQmYKumOxHAX6A1e5ocCsxfCB96bZRIzt6TT/zSbiFfSoaTUu+hwyePEuQhoqgK2Ys7w +9TbB1zinlGsbV0Y7qu2+ZhlkDbY5q1JPByShmboDtWEH9HlBDERaS00Zu4KyhiNeoDxrA6x1 +81XohmsVTtreqJbrdPNXwgOyvX1HgfKA9Pj1RSLExvb8vGkMQTmMV/wp9I9fjhlCSg9bFxwm +hjx7fb8xyC0drLmPDaXI9KlJxJp/fxs0W2k9rrPAU5DDOgMJYWmnVMh3Av9+snLdxBBhyFPx +sLCymTpY5LTYYFIAAvO0ksuN2EIBsJmKlFT0CqD2BSUS6f////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////+JAhUDBRBBw2NUiaS8G1gwxicBAueQEAC/GSMe0SDzqsFa2umh +oyJ+iKRaoFMxPTzuers6/tvw1m6yryQoyr6jmjw8R0yjt9AsHbYr4m5fKcACLdpFAJ/xVbdc +PR6fnCUwCrebQKPjQQrBgxI//r3mnBsWCnKfDhMigXNYBHajT4rpEzbIuos7xV40Zbsv+Nxp +CZgq6Y7EcBfoDV7mhwKzF8IH3ptlEjO3pNP/NJuIV9KhpNS76HDJ48S5CGiqArZizvD1NsHX +OKeUaxtXRjuq7b5mGWQNtjmrUk8HJKGZugO1YQf0eUEMRFpLTRm7grKGI16gPGsDrHXzVeiG +axVO2t6olut081fCA7K9fUeB8oD0+PVFIsTG9vy8aQxBOYxX/Cn0j1+OGUJKD1sXHCaGPHt9 +vzHILR2suY8Npcj0//////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////////////////4kCFQMFEEHEK3dWL3RV6ir3SwECB3IQALkhb/fDBBhTKimR+5Qzcj7z +zSZ2TfMNBwq+Wr6p5X7mU/1w2SHYNmOLGDs4Eaqto5CTJ2jS4U4B/Clow0LOTViY7pg3Yrzf +gQPT+omA1aW+if7UNCmEYvUBSLSJXOWUAdMfsurWPuRbbnjczurpevoZCvl1RwN0wPRehfAC +Fyw0FlJoae+lBGUGgpF9kJyZ1RdguNd0AneCCr+XsbeGJ8sxpxYyLci3A2Am5pioxDbz/l6f +dKV/RwjdmOjdlD35k8vUCoGZQTjbB4hLpWkfZecmF0TXPqT41+UZoRfx4dsG4146qEsGGDQM +KGFXd8Rm5yNvbmgso+gDI4QKLukLCkq2ZoZ0mYb5QeMbAcQ9iUMnspFaIn7K9yyBqNCs55Qu +3fKfKbMnBnS4GoYojU6179wlFkKJV6cO92NCkNvv2Do40ucvGqpfaa4nDvQf35dkj6xukN9R +13d1RmAnmBxdx8e+mpThUB9lwwhoIRUwKFaSFGoVncMaIfqw5tj9DBm8wAGN9lmJakM12C8v +VA2zKH0L7KPxb2u036yBPxPzFJfUATmioUhKHBky9xhGfBUSS8DHr4hBygvCYLcyaYdh9cyV +juTdM8IYdVxFaaWhzxhNdK1G/iZGxFq/CPS7MgfCrQhwg57/rpWonaM3pRc8dGYp4VC5c96X +5yPOIk5Yxr6wiQIVAwUQQc9h1wRVjUj9NCi0AQKfvQ/+LCqte1ajVbLVGp5u+Q5bAxTO3nb2 +s88MYOmprZ0sDXxNT/vPrNCIx0sjbPo/tPdBrVMrVPA/VaTCMRpuWVLcBlqOouk6OfXMMRbu +7uk46p0dJyIrbgdQOesruIMtdG5KBeaBzfFG6lPdiG2y6a+38BOwgyqvwjUzeIyucqq6gq+9 +5ufi58MzBPi1tK776loTdNYWOJBJhf+ue37UNHirtThYzEpk+NdA/k7VVp2919DfrDdQsyok +vyCp2OzryP1IgMeJjtGZ1+yB+4cWXMcJ3idLcNmnPmE4xvI33r5myKULD71ooU7cIFa8MbE8 +ABGRMSKOZhOTdsF0oBzkPDcVljOjDCy2b498ThCYNKG0SFKhPXjB6yaWNhL4hkSIZLYq1emp +aNNZcgQ/TVWmJOL2kMEtQiW6yk85gOS4+l6JpDQDK7/w5NlvFbL8tO4W/qZiMpc8N4Guklmb +HaNiQROGnyxmas9ZhAV5ZaYALcKiKfI6OAmmlvOb3xEybrdtYLFuQtPdBnwjHo/5o88EmwV7 +YRf2FF7cwD0zy100sBchZxBL+wm3UB/+a6Nlk+DFkGOqp+JsFTaK2o4iUnX5IiPpNPC/5chF +FqQ5YDCgFo9X4DY1A9bXmLXvw05kH8DF4VhbxZn6SslH5MjA2jETuEbB/9zXYjFM+ysQ9D5z +CxToMf+JAhUDBRBB1tAyl1IjhnySjoUBAh9ND/9xWGS8AgG0faic0vzr1XvmCGGjrTW+I3YL +r7WhJ5oU/RKSVgpOKfoHj2vqRdvVKCRuzi3+dYhro4vaeLn8i/oNBTbG2D4Xjv941PS7N01g +FT61oJwTpdkr9EZkPEsbT1fm6qYgLe0+Z1vf8QKfJapO5Bs05NWm9AgTm2R7oNB2fBndkbLI +TiDFtKWlMc1VyZWMN0e7zWsN4Y47K41HBBm2cpXO2mL81vi2/hygHXxk3g2U7psKlyVacGV3 +b/fs2zaIxeLOA3uC87LfEGqZhr3v+G4GvJ3puwbi8PompeIdRGm0HmIwwK49ltF+69Ej1/Lk +OD5uXCMGnOoNOdAGaiIEWjmEQeTB9ONMYZbqqfAsMtcf6UEE+tVd+fj89PCbpMU+YJX7qFlv +hOCiI9GIhPftKyOsoSpzLXAj+kuBMSq09qAb77kHiMOzuRNlvpkBjkWKl2nHWUzSi/8Uc+4+ +7yajKQDTs/eqvMYfsb/MBVf4f9Te07Jep2VP9SWVht/Tniza+VErrwNJejDVci035uEV7A+o +M4w2ACee5kFeXjZj4/FOwUmCyvx1RslBpK3aWwy2auvhJV4By8/WUs2UtnGapb+xF7TvviOD +87XSiTFYEu8P3X5q5CgqKXCbHpkncyXek8rqpqONXj6/P5H4JFAUGkhPZ3okSsgCV72PJ7/S +TIkCFQMFEEHW0DKXUiOGfJKOhQECH00P/3FYZLwCAbR9qJzS/OvVe+YIYaOtNb4jdguvtaEn +mhT9EpJWCk4p+gePa+pF29UoJG7OLf51iGuji9p4ufyL+g0FNsbYPheO/3jU9Ls3TWAVPrWg +nBOl2Sv0RmQ8SxtPV+bqpiAt7T5nW9/xAp8lqk7kGzTk1ab0CBObZHug0HZ8Gd2RsshOIMW0 +paUxzVXJlYw3R7vNaw3hjjsrjUcEGbZylc7aYvzW+Lb+HKAdfGTeDZTumwr///////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +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 +RqLP93kxsWzK1ynuWv2Wz6J+SXkWWz/70YydXrLqLmv5su2yQg51d35j7kDjBU1Vj9RriNza +dmB7PGSB48RYcjswsZw3CFNojrfK0gl4eRlUiEkHyWX60gb68eO/vVzz2lisoorD2IkCHAQQ +AQIABgUCQvF1agAKCRAIeL6M95ASakJSD/9LRPB2WZpt1rKSD8quwjUO84ie/WaLFGS4Cme8 +NI65GfglW6AyGjnsry3LyubZeGHytzD4Q9EcmVY3RSTwD5h+Ppya4drbiDEI/77OUuBj8qER +nyJpNGhSn7ug+w1Iu7nkPkGU7aEQtTnKvsNksXDQb2OiH4hboHsbS/aeCYEHPdnmElKSmK4u +RPgka4WworGk8nbD2IpRwhlSugmSOWCHcDgItnKe75+erf////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////4kC +HAQQAQIABgUCRHORmwAKCRC/tlXydUU8WCHtEACkvnfnBsV2yC9xElTs98h5fbiFgYu8v7R6 +toPlg2NEXUckfl7K/uVf/qq379A5uQeYdCf1gDAXBXjdjHDKrkx0my3GOMCU1W9A04GaD2j/ +2ogNqbE8yRF+PNsrS3wZvOqrnT0YPT9yICuBaPusv5JEtpniqStu8MlUWx8kWAUmq0V3Yq4y +3w+m0EXuS6on5kfFqjv4DIskYwsxjUsY3I3f4ZdnGGWuQZYYHdXw6DOVx0dPk4eczBb5VJ6O +h9GK8a/0yHoNzlg3hsyi0kadHHVNiwDb4qnKXJmGg33i1Qa1bpgD5v2SIV5N+DmaX9H3PML4 +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 +AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3 +ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj +pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6 +/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3 +AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2 +Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJma +oqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6 +/9oADAMBAAIRAxEAPwD2aiq1/fQabZyXdy+2KMZJrhr74s2UZK2Vm8pHRm4FaQpTqfCiZTjH +c9CoryC4+KesTNiGGKFfzqez8V+Jr9fMSUlQc5Xj+tb/AFSoleVkR7aL2PWKK8w/4THWrf78 +oLDqCKng+Jd1EQLm1SQdyvBpPCVVsrh7WJ6RRXOaF4107XJxbIrxTkZ2t/jXRA5rnlFxdpI0 +TT1QtFFFSMKKKKACiiigAooooA5j4huV8I3WD1wD+teHV9A+JdPh1TSjZ3BcRytyUIBGATxk +H0rgn8AaQp4mvfxlT/4ivQwuIhSg1I56tOUndHni/eFel+DdR00aasc8yxOg5z34qtB4F0B3 +ZJLu+Rh0+df/AIirLeB9GhOFv70Y/wBpD/7JV1q9GrGzbJhTnB3MfXri3nv5XtceVk4I6Hmu +fmfk12snhTTEG3+0LzHXqn/xFM/4QrS5FDfar0g9/MT/AOIq4YqlFJJidKbdzn/CM/leIoXL +BQOpJx6V69Hr+kJHiTVLQMOo85cj9a88uPB2mWlu08b3MjqyjbKylTlgOgUetbuk6ZFakSKp +XK4+UcfjXFiakak+aJtTi4qzOpj1/Rpm2x6rZs3p565/nV9WV1DIwZT0IOQa54wI6nG1h7c1 +hahpskk5mtZXtXXIV7VzGcfUdfxrnsaXPQKK4WDxTqumOqXY+2RdMyAK/wCDDj8x+NdTpWu2 +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 +lNQ6q3ZHfdW/APsFH6fcoWO7ppGEzUHEee7thGHVM3A8GmxHRGU4s43QsAD/fpHhn6e52HKl +2seDy39+2WVG9/Guxgmmn0IpBfVSl4GIjAQwEQIATAUCRoEzEUUdAEkgZGlkbid0IHJlYWxs +eSBkbyBhIGdvb2QgY2hlY2sgYWJvdXQgdGhlIGF1dGhlbnRpY2l0eSBvZiB0aGlzIGtleS4A +CgkQ/LC7XF8fv3DPZQCfZ9qhpJmT1IPzF/Ag4/LdDS+w/qMAn2HYZ037ah7ddgS/gzLausNL +EqMsiJMEMBECAFMFAkaPRU9MHQBJIGRvbid0IHJlbWVtYmVyIHdoeSBJIHNpZ25lZCBpdCBv +cmlnaW5hbGx5LCBzbyBJJ20gZml4aW5nIHRoYXQgZXJyb3Igbm93LgAKCRBVjgmCgtIkHMWI +AJ0QIVAUAGN4y7U2wf/I2o0/yCwKowCgiBXY7Or3H+l1zRFuQLYoeYV76fSInAQQAQIABgUC +UC5vlQAKCRBC5j+f0+THVJ/CA/wJBe0Og0AfQf/uHKm8J44he6vmcrstjEos/JmtoFfFQ+Vt +8Yij6L4XMGDZ0eBAkF9D9ND3d7fc3B8IVZJ2uU4lx3qTNqiSCTBBhzGPqSr/ekTeRoIf42f5 +Yyx8ln4MLMZCAO4MHmHRiNwNm1EEJXhQU2+G4eY3Xk+oXwLXtJw2v4iiBDARAgBiBQJCP/Jk +Wx0ASSBmb3Jnb3QgdGhhdCBJIGFjdHVhbGx5IGhhdmUgbm8gd2F5IG9mIHZlcmlmeWluZyB0 +aGF0IHRoYXQga2V5IHJlYWxseSBiZWxvbmdzIHRvIHBncC5jb20ACgkQMBkOjB8o2K5CMQCf +RFPNmaGsRmYp2CbgpRMLvhlJPHcAn1iMZQKf+lrIyauTce8ucFlGbgx5iKIEMBECAGIFElIv +4mRbHQBJIGZvcmdvdCB0aGF0IEkgYWN0dWFsbHkgaGF2ZSBubyB3YXkgb2YgdmVyaWZ5aW5n +IHRoYXQgdGhhdCBrZXkgcmVhbGx5IGJlbG9uZ3MgdG8gcGdwLmNvbQAKCRAwGQ6MHyjYrkIx +AJ9EU82ZoaxGZinYJuClEwu+GUk8dwCfWIxlAp/6WsjJq5Nx7y5wWUZuDHmJARsEEAECAAYF +AlGDXMEACgkQWxImvOXpKyrWSQf4nTn1E0TJN+LD8dDt2C4BEBi22TDX36Zm3oqsH3UbwjE+ +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 +/gJ6imAz/EhFB1JGLsx5Uf9A9vkAecL1JWgoc6ccIouOrBK95d40DLL51zeZF6G8d3kdRveD +h02bcZJVDNeJk38+B3QyjJmfoeVLAK0MEIeBQS9ej9oK+OBksmeumcT1VpAaaiQfJa3GU76w +i2ey8sRqRv6QAFee2PIwD2rhJWQkCpXgNuevYaRFl6dTrBfOizIqxEoLnYHVMBnJ95tqaSyy +INdGXnNrFKHSNa+AHzz7/YamXgDpN1hEKfh/Y8D9ZyvVGbnfPTjIIyLYamdtCoPR2YaKJGCF +VYkCHAQQAQIABgUCQcKD8AAKCRCq4+bOZqFEaDKtEADINSsH2euET5sYGISy/GXl3nL3PHE2 +MAUK5y2Lch9XtNDGQm0YSLPNa7pER/3UZJfn4TINcesu5LFUOIG4o7No3MolN97///////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////4kCHAQQAQIABgUCQkQM+wAKCRDcO5tBqsVPo19CEAC39tmTO0cmvcJn1vXBC/fEUkJC +9kC5bV+tGU3aKf42gr0VurJASi70v5rpKsJ+GC3uTbrmVX8QVrYRi4mpAPgC+gO52Wz2WUJ1 +AmbLW9c3o/+weOeAg3gUT0R9ukrGWg8LBbQXL3KT7DjWwDuqFD8VJ8Zdiux1cnV4uLzDjGzb +go5hZqEUla6jQuqNlJvABmel8P5G5YTt6sL+iIEDMABvNlQZx3JbhgYegY9Bv6rrGWzcVU/b +hJLQLiD/JGAxjz9XjU4JVnn9UtVffkQgAhZuWvH41pi1cvUGCUbVM1gskL2lkmnDnG+jnVRk +sVGw08NZrP7H/FglGJKOOa9OSSoW/10iGAJBJ1PYGivjcKBq4MlIs4F9zb4lvldjTwCgwbSu +ZG66sna43NSavCNZO2YEZMqjAG2bMxlOLqGD/nC+RGfovKTcMSzxNH9IbMDx7XVmWg5T149T +kRpBCj989A+G2N90TJEEM5afmBX1r/PDhaqXT4s4WrIVVA5Rldde6khZubuJkV/yQrgPkIHk +x8q4HBlF3gBjn5HaEJHBYarlNTphTp54GHyGL7OMqEdRFlBqlqnhtc3G8fYGnuyDcy1LsldT +Z3OTzNQsXknU4d8Y+Fqd3GGcsDO+QP6j4h+34N1Tuua4GL2PKvmGe3T51+7t98IqFDs+7btT +5miLdwV9jIkCHAQQAQIABgUCQkQM+wAKCRDcO5tBqsVPo19CEAC39tmTO0cmvcJn1vXBC/fE +UkJC9kC5bV+tGU3aKf42gr0VurJASi70v5rpKsJ+GC3uTbrmVX8QVrYRi4mpAPgC+gO52Wz2 +WUJ1AmbLW9c3o/+weOeAg3gUT0R9ukrGWg8LBbQXL3KT7DjWwDuqFD8VJ8Zdiux1cnV4uLzD +jGzbgo5hZqEUla6jQuqNlJvABmel8P5G5YTt6sL+iIEDMABvNlQZx3JbhgYegY9Bv6rrGWzc +VU/bhJLQLiD/JGAxjz9XjU4JVnn9UtVffkQgAhZuWvH41pi1cvUGCUbVM1gskL2lkmnDnG+j +nVRksVGw08P///////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/////////////4kCHAQQAQIABgUCQkQM+wAKCRDcO5tBqsVPo19CEAC39tmTO0cmvcJn1vXB +C/fEUkJC9kC5bV+tGU3aKf42gr0VurJASi70v5rpKsJ+GC3uTbrmVX8QVrYRi4mpAPgC+gO5 +2Wz2WUJ1AmbLW9c3o/+weOeAg3gUT0R9ukrGWg8LBbQXL3KT7DjWwDuqFD8VN8Zdiux1YnVo +uKzDnGzbgo5xdqEUha6jQuqNhIvAFnel8P5G5YTt6sL+iIEDMABvNlQZx3JbhgYegY9Bv6rr +GWzcVU/bhJLQLiD/JGAxjz9XjU4JVnn9UtVffkQgAhZuWvH41pi1cvUGCUbVM1gskL2lkmnD +nG+jnVRksVGw08NZrP7H/FglGJKOOa9OSSoW/10iGAJBJ1PYGivjcKBq4MlIs4F9zb4lvldj +TwCgwbSuZG66sna43NSavCNZO2YEZMqjAG2bMxlOLqGD/nC+RGfovKTcMSzxNH9IbMDx7XVm +Wg5T149TkRpBCj989A+G2N90TJEEM5afmBX1r/PDhaqXT4s4WrIVVA5Rldde6khZubuJkV/y +QrgPkIHkx8q4HBlF3gBjn5HaEJHBYarlNTphTp54GHyGL7OMqEdRFlBqlqnhtc3G8fYGnuyD +cy1LsldTZ3OTzNQsXknU4d8Y+Fqd3GGcsDO+QP6j4h+34N1Tuua4GL2PKvmGe3T51+7t98Iq +FDs+7btT5miLdwV9jIkCHAQQAQIABgUCQptfAQAKCRDO6l9ytVN84dROD/43u3Rdhef04MtS +VaPPdTu6uNLvnaaNCg2aFcjVgr9pENd/bmvLtyvcVIadO0kio4pVDYKke9xcs5ANQ+ymJfU0 +VcqD58cYFnQ+Hf9519N3nsoZLs5ZW7OUYZnJQbAWBjKlNo+eyty6EilXus5pms7DpKlEqit2 +SCf2NZK3LLnUYnKDwZwx47g4NGkd2cOTSA8k5CgB+iC2e4OU1ET+SXv7E3PNzqu4G4EsmKmb +CuYytbL+3csNQOI0eILMeRf7KUf1W7BRNl3xpiaKAsX4Z24DTA7SfVidaN2MZJPQxO/fGEV2 +EdBdLplSfMFW4EKPU4/tZwAjEHMoovXNIt3VmK1dWko4K2ajq6xVTgZ1hDU+pmvV2MU8elR7 +0jsvTppoRkYny3C+Ewdwk0bHWPK2FnkbimhDM4XYSwo8v6F82r7sOyBfEwIC0Td48z5Y/l96 +log0YnauK6lrrh/FPV9IN+eZRqG6NxT2RsXEFyh8ki3g6ZzC+uyjCYC4nrMdJ4gSXX+lALDx +xI9IVBEKIiCJoRy3BwmcQAtSOUvgwO3Z+QoGeMXCWwFBSuYNsY9ITHSyy3mPMfM/LVcN3Y52 +XBLFBB/G9QU0ZM82Y87DDthmNhIkFLIBJxqiO8wXk6N8sFdGQl0IAqLHwcENYP7OBF4AST3l ++M+Tts8NYz60odnfDiwV/IkCHAQQAQIABgUCRHORoAAKCRC/tlXydUU8WKuND/0YiGiuXLVj +YjFHB/HsCS/QpYFojwJv59xmtbv+Uu30gXbRkT56tqRHKyZM2mSPYEqHsVkVhRJUK43c+1U8 +oKdFl+LQmuVPMPAgt58r8zVTArfP8fHRsJUIKod0Y9kGaq5pZklZxkL1pw+WdgLfuN10XHFt +flZNbrlv+c0nOSqEFKZ03oenlGrasr/13QEBew6QsmulTB6KHshMPVuR1W999QrR8ocRrH6s +58VmMxsGlAq1hmGv7PzQNtMk7JGl5D0wH/G3KsmhqGvGafLXbMVZAHJ/WHxQzDwfKyVcFeDi +p3SeaKKDU3lNFn4YkW+hCtq9umM2xj0+QubUp4zMdgaPpJe1W5rKuYJxeCdeXKxiLRO6t5tt +HBVDY0mTk47MuyUoERdXWZXzGrG3ahD7VM2QngdiMUyT3CPUWCFBVfiEe4+BVDXVyzHL9UKZ +Q3xp5fVWN93LHRlyJOeWM0MLduEoxT1PkGU4G06BC3YqQsCVYVsu1FKtOcVATI2asMIUyJL4 +thXS9mBW/tIcGjCcHiI7aFqqWqbFStttdZTCgXKjfgoa0I9E+vVjURubDoR7dcU2nNt0C1fW +XKeJSEmHcrSHakm6RnV65+s+DG5KxuwpquXbxlrFXBb7D2Lo8WNEM3569bR4nzlwvDKHF1Gc +gmuKlj/IOre+47NPvwpeyA2p84kCHAQQAQIABgUCRrFdDAAKCRATgrFP3Os7f20yD/4vgN+M +C9TU70tY3PYQXXP4ZXgCwWmgY/EkBCZtJXLD5a8V/NDbqywPUg7E8gN8I4yAQwiePK1DaY34 +KHubQbn/KrTh5HxO6/KtcMyAecCkZebuD/rnUsnEvQvg2VZaQtUD8HnYp0WKZPwYvc9xigkD +T8tnVxC+V7GsBhyY7mYJfPJf7B7l+dQR61g8typvpVJOQ+xvXlQ0+4deNwygB0DctEb6vDBk +54q9WbfQJNN/YbG/B5HOJG2FHGfJFLm6PA9bnzFzNDNrTySEbk2aDyyliBkp9kSDzFmLyHqD +3+WbWQ1kwMYfOs/K2jpSbkKTeIhQTHN6rUcd4uI4bS1oJ+NIE3lk/zUwJLB+kOD0o/Ory6Z7 +L7SF8gTpsZqhbcaV3k2DovIBaaiSRi7kScpWadvXnAUZob2x5cg1BQCQE1epbaOFKlZTedtC +mR187utuIX+Yo1vwssQ6VYZUD5daxdKuiJeHz9XqYxjJJC2eHjEwwr0UOmE0bMAAjgJn/N+T +h6BsFOmehINo+afEslhKUTz3X7U8URucQ44ZZhaTZuuYuSrxc2XJmFtOCaNnb/2munDwxPGL +Crq5soZH3vek4mGVB9F/BdJS9B8XqgFgJsNnbSAG/v2Y8JGVr6tp6BBbD12T+4e0irG0CYXK +77Uj7oQl9fRjJCIFVmgKOx/EgIwkQokCHAQQAQIABgUCSgQB6QAKCRA9H1hZiW7q7rT2EADQ +/ayO7yeSZHoU49hW1A/QYw2c6qBwNe5sa6VS7hSHKSTNFmR5q9IBhaOPbD/f/4WvL3yEON1z +8T2pAl5fjUsDH8Yg7k3Y6mTdaLNtlOH5AYByYA7D799iE0twbhwGwMtAEYS+IgvKwWqOk1gf +AxpcZa0/fqDv1wIE0sDJgMQsTQ0HMv3DMwpR6mX0io0T9o5SvpTlklkgyiLFSYxHqAczw9qi +i3krXMdsy6DrmTqOd6N/Dk1Zt/bUEkBJzXhxlE++0RuMFLm8jUpOoCSR+y1F9Wq+5nMIoghA +kH/JKYEWDR+YQyifAH/UbVQAmnXowrucNpMHK2/HEnOpr+jhe7wzsytL3p3Jc8+F/vtyaI6n +pESFw/S+32z+/u/ROQ4upAPPSqVectlp1ONgcIejbwl7/eOcUEVnOOqBvOOPB34tXSEwUx2R +zBjh/lLJpg6F0oZN49oBzH/5HyYaLz2gn5eaQJJoSrIrm5Ewmt+3jEHgIgpsQjXBotfL0c7H +jcMSn2VP9LHldjcKM4kAKC3Rq7N15JMySYPVg8CFltiPvl2o4fjJ4R2e4SPr5fp+Hl3O1Hew +hVnP5oZOMwRvyKYajhJGWIeJPacX/vEpMImlwbSaLWN1CFcOtk+3Qra/7RoUWrRPUB1/7aXo +rRk+diP9PcwwHO03uXHhTSrK8+Wg8fHfYIkCHAQQAQIABgUCSoG0ZwAKCRDHDUFs2H2y7KIf +D/4149TxLcJ8BDEdaJgcG78uNYs4+IeXxSUL05fUhCCSbicy8cNaQB2MHWloGwFrLpTAuLww +4FH6qIyNfvgmLozDhFu10UGVZNfML0SmCB2RJAeJ9vOc3o3AwCgr98uMzYLrPx9LiA4L+9ia +tbj6oZsDQVKt/zwRuPOCXh50DLZlLFjTBSnDLQxECCldEnojvsMZkjJ5QPn5G5Nn3uG2WKvH +C9WIgIJifafq469qzYRKVtfZdVuxWUNtrm1XhPNLFSKOiiuzUyc/16S3v5QAuOcHquoRvTt6 +yHA7BCwUlh6ykAj0Gad6aKJ/y2jFELRGErek7UDmuQnGthhohmbsRXjffrAzkPzRhOnKkHRB +S13ibfb1aIWIElMdrPeB0T2O3FMPZ+evI//VuV57pmjnqfnIok8Nyf8wiKhuPKPPLrohwwCt +3MK/ggzVTF3ZCrqJjaQCksB6Y4RNxPYCmL9t/Y9hIqFOdYoilOA37eJRwrUKVq8tNUYUA/pB +wu6Ln26O/E3wA2DySm/t4kszHber8RZAO3evm0q68rcPzuBXlzoP8cpZm+MMFdICkx81jlaW +JEq0hcCK0jicppfiE75YkeA+QWs+IUNYvFpqZ59H4HkoBZXqlPZeL87REiNms+obQlrCioe4 +AZMlSamNadDUsSLjA77+KE4dz+cFNN48Bx8L6okCHAQQAQIABgUCSqPcTwAKCRB04FmmNBWC +5RvOD/43x8QXDiM8mc9ZhwiZ7sECxxnYT8rkr7qgDY/Rj4FivFiBRjm64/YZI+AIQLbrJHuj +4v4f4CMQGt5bxRTcdAQcpvIftYilKZcceqK6mJNESjQi9v8lbdK4FUUgRveEC949CcgiB0gR +WnC8On/lPlOL4435Xdy3Sp7ZIt5cNzUn7Y2nWxF7Vra3Sc7UHqb2msJWHvI573+Pra7nl2hk +njKrlhe/Ab7JP38K/Btb6WP5TKW0zChTvjyVUvFsX9r1Ja/8izg8Y01HEwlARoluVxoiZ8pf +kQwSAMzcdNWmTaXVfx1J1nHE+IcQbjNK3nQTWLjm2m/dD7bKYxLZnHGaiXbLFGrU/MeT8xhn +jlkdckefSeIim0f6xMIJQOlioCCe9uhievlOCva5onhkM5EE/YZHFmwXyxB8zItTNc+kxnJ0 +0W1pmwAjFBHFRW5PNeD6XE2rxaaCbvU+FpaLLuI7ocEaklzcgnu3WEjopzjkTZ+MSOQoM+m9 +du5GBhQh0UnFiqrHLxdYpxliNZxvc/wXsD9bGmlEb6hOhaSkEV+8Az61YGxZg4HgtQ9Nwu9k +AnND1F+jvokLyhEMrHOSuDzu9nNtpGMWJeU3L87QRyEN2FqdPVbNY1rug7yG95LIo5oafY7F +iNITP1RQ1Tn1m0LLRZhYFlcq3dg5ZXu9Zmo7olRJW4kCHAQQAQIABgUCSwZO4gAKCRDWCwxm +Nt5Q7h6zD/473ovISqEhDGWrxGYck1BevXIpSRu/1GdSo4fZpsm6Qf6DIOxrWY88WARKC4ia +bVUKKDNLfazcYqdYrjWZYfPE6HNok0BTIMATSLmiGe5ZKv5tyAN7YZD4WlOooM3NVqN34j7d +p/LC7IK6dFk0xinCZgpBEULzKq4W+NFxvKiCI0lf+IAg43n7VSfJVOUXebXq5N1lMqbSDJjv +FzRPt7e2rHQdJCHMN/gF3rxqB14m880VpzXrBfqaKrGE1YnXxYYaqUxGBLg57HsxIa0s3Bfs +2qhvbIz1Hco3eI/weueL8WESRi+mDFlRjS8VWimsXdjVAK4PgdlQAJLYEYYjX860Eg4mR4aJ +C5wahoZ6pSSvCFVDXbTHYWr4eyktH4b5f0vmigibwwAC9/5ARpQxOGxOCygkPoBtLyXhY14g +MJjkbyzlz2YHGdHkUcOW+Z5keibQeIOvMlIBkAN4RJ034soXXrpHGCRVqkJVpKS7t49vPDk3 +3Y+ZZyKK8FstvRkNFoJwYLyWDX18zKaehDMmP3qe87NgnXktR1FMtMQ2ERjq7BZx7i4Gkcd8 +V1B9SNvuYltkqyxkotbqYnDV+MsKJ3XZ6lU0VyxPVqYrPCOCE2ArAyHckw1L2G+cBbVYwvEP +lo32QOnC5xJkZxQtgnBs4i1Jiz78lYImdzf/pFVqldv00IkCHAQQAQIABgUCS+IpkQAKCRCv +VaJhx8ds4+ZlEACBueXqw8b5HQX/oyaK09bjiSK5wOk8mNuRDcPXEBudWWY2J/CmfcBqhHRq +TT5r9dHITnlR9hvieoqzQr0yBtujZldaFn+UmjBYU5Z3mtNUpHvp6htiZQakgVfZJGY2Qukm +S9R/p5YRFeyrk32P/Z1Cgo0blO9gmFAyZePdzx3u1l79iFLdUknwYVFit6xQNpBLLBRd7Pvv +KJq3sxTNQFncutclNyjoI7cmnHC12A2NkLf37lPRRBL31ixc2iGYI3mZBomDPmpkPyKWq68A +96Iz1YFH1OT/Hnnyz/dJI7+3NkSjcoYYp+Rqi+4Z28xXPyTeB2oWIkOaKk/HtS0STO/uTpIU +7Wu/rU/Bka50ybcuymj+OrMzby42BEMIlWwfleRZIkdprur0wwEhsfNIkwNp3b0wPk0Grcve +U9hAo7UjX/AuiNFEINjRJTiHMh+0LBGVEyukLnGn0ZiOFhPKrgqBBs/aF7AqsOkBz8p/bI5H +OVRkG6ohjqR5azuzrFIKrVjcs70PiLc/o20lepuYqeFisBH3RzSFw3ftI0JOY7mvr4mgZsHu +OJezlfBPNiixO0TwIZJoMSr7JR7SekkqdUu+Hp1MsjbsK0acLyiW4xdgM0td8nptM4LxHlFa +wDxJlbmX9df4YAsvmKKA0WJOwAFfLwWmKt4R6kTLt2Z917qLkokCHAQQAQIABgUCTAwMSAAK +CRB85bB6eyCEZRrWD/sGXg2gUxhgX3dDXNy9VAqwAJbU2Rbk8mW6o6La/puSQV0rcyEQF58O +mfBiPPx4D8trdR5LnLnk68kelQBZSjXFC+Lu//ZYl2DiMPzfmGan3iHGsGfcvMMXpfKwAAnv +30X3FHowx8/k7ep/atxWZ1JZLI5CyWa4Ohpidg49OA+As/PHe8mIMxAl81w0RoJ4vm/F0QhH +cESDtbznREU7z58+6WGLvuRcoJQ1Z/h4qly+qRi4/biiga+8rpiiZimU2Q9zgxUy/U2yAFYv +9upptO+y8F8pB+nYGDcCQuSAu4s8g7OJe9XhMVkgV1tTbIxf3ylqhG9/6BhmMLqHCiTLjY1E +CdyybNnftlhPbh1ecARpqIFzMrExQShliH9LyDyWFpwkaO7DxFW3wLyzcyibKPL9ofE5ism2 +97fLyg5hnCZTHynCsGKT/gjXLZFi8U+b8o91eYl6/bkU+qLSigYadzaPlC4JEpvAcg09MxL6 +LMTe88T1TtSOEhP0c+u0iABWqaXcOI2drLj1/lxRzgq6EeBMdDpHiJZS5hZcdHAhfoZ+B/1N +LzwvW4hEGXjRlZrVTlfVQyCzMvNHVS7gCsfETv9YSGbWYrVHClCCssqp8incEOjFRbceQHzT +oVmTD4A+w8nF/HP3NSCNEUYKj7r6ZmDfq8r513vD5wOLtKXtsaLxTYkCHAQQAQIABgUCTDpJ +YgAKCRC2xKe+vEfeEcRiEACtFpA8lUp8NrPGUt1g5o/xpsj7HpwsaCwj831N5MiZPWXR+yD5 +/wr/TU+X/bAysu+kIPHbfW3AA7RHiaQJsTA+kTnfFQTe39zCl/BYhWW4SNNZdEWrCX26wC6q +/yYcEbBxsl2DB8X1fjK4CX/4mMqrGBZsbuaE7hWKWZuVGEQV0Aa5eXInWH7a/MXWi97JXTDw +tQacglLLZxz8p4QNCfYOkLVVlfkIc+2QoZ3QaDdfQQWgX/OvQjY1sHcMirpYW8tMg9L0NTQC +/sM8QayTaq5vHHw9tHGcu+yfLWXvKrncwd1O93Eky44SB1sGGzMzPdiGZCcOvKrK4XKqNM/I +F4nRCUyyEuG24thEscGqRjQGfU4+5pKG72wUYIZjcgMKesP2AtqE6LJ51XLq5gZOcjFi9yn2 +RbthFAPK//Tf9qO4IN0F2Hp/IBHF/a5iISxS7no72WuXx39gp1m7uMIWCqFuJljdtJP5cXLe +MqcguqUlhtKQ/1oOYw16mTDpq4ikdfNULkxHeZ6pNaiZz8e1DQ76vWDe0mYHFTZt6p3ax7tT +0wtuSIUEmRnRQwBOZQZh38+Ca4BLJH/YwJpcmuGKTHSxwQdC0eaezY+7sJ9HFgaWGdtqCmng +4irVi76ngDig3ydQwMEkuwik8TffsSZZJEAfRCwG+vLUFwPk1viRcRZen4kCHAQQAQIABgUC +TIYODgAKCRCJCSF9w07sV8MPEAC8mHkHIrnkmcGnataQloRDPZ1UxA2wzH9eAQL/HSj83Cde +1IDMqNrL6rUg0K6UrMHGp30gTRfjGQp/nYBsvumjoSjgjHak9OJ5rnT/xfTD1XqBRmawwswX +p+S7v0cKmba0OJKnkGcJXfKhD0kNlrPCqJMD6pgt2egxIpbcJiAfFSkKdR95LDW7tR9tHlCa +F/a00S0Zjvp8IzHO6+BG5NHa78somq6GnBMJEO9iJwn8b0S0nk/D0P23LZ+p82Vp26CYJNk3 +OsHOwoG8kzYVDb0heW1ppaVL9uxYmLAkm7TA9zQxjzmpY8aGl4AGQXrY+0mgVmSyjHCr5v2R +T7zfDB/rN6gN2egdveZJ5hyIUruu7BxGNsCxKFdsBNYzLSrQ7USyfySNi7mjWnT+35fZuwXA ++QvT1AMHoNFvgz2lPq2GEZfdRnUwcdednL8IojvcCFsh5eTykjNpcSMrIvPLeWVsUFI6EdYP +5PxpXgl5bwgYQrksfutJ+t/Qa0NWAnmZ6/4qNYTg/wuXBTGEN4cVIrrVTO01OyOlxp0JVlK0 +Sd6n4ZIiYA6KwNeWjDVTJU97baLOvYVBr10kIi5u27fbuSx9L/uhYmd7vS0FTtcZSwOoX+ao +C2p3AFxCw8sJ/jU3wvJfH/TOR548pU5vVfDxh7cCU1Gy+XFOJt+YEF26svQ5PYkCHAQQAQIA +BgUCTJDa3QAKCRAP4+rmMEXWVSw5D/sETYIzgcV+UxPG5Jd0J3fQnLrBiNW8gqZpzapCeNOY +nC6uHOrIV2SmmyTdP52dn4rjNRdnPpHJfr5cRXhamtYm7IivEbO+arSENDnNaGDBidUCofBn +1mrAGsNPbEz0qiK2chWXSds0C5ytoeACOh4R/SR33XQyCB+MbnXpxoAJE2fYRv+1BejKfwLd +8Yo5ooMjlg+NJEPsf3ZjaSEs3H0mIV9SWLzWZjk56L3zN0s53Jv2+E0579NnTLHN/YDppM1T +qgPbVp9pASs0AAv5tP/2YtmMY7PueEDi4qwwQmG7vr0v1PwD6Tdp+E9hhvQ3gS1x0YF4zaYK +sNei1Y5LWO0rxcWvPyApKL3ctS6LHbv9NC6iySBXMsuihwq3ZNj5wsjfrb2joSW/oZIlNRBW +8xRw0bRVtdca0xBbWQJKX1iIb6COYNQazXzWzsVP1WIXKt4g9t/LkPlb3g1NwL9HqHiXuoTB +M8rZfaPZkYZOYydxZAmLvxWqpIFHcueFuU6xCbWcGPu+fQToXINNjDpVE1LuGvaU7ZI7o7P5 +AGzFY+JhebytCSSIn2uLRYifIx/xrGe3RGJKCrJBbopWqiLrVPISRlDUnb+2uXhl3Zbdi1uo +fSC9iAmM8mb8r3eOzFmcJ8aNoqxz5fZyooQ0l0Odnj8XzPQ72z9ism5C9kiEoAHpJ4kCHAQQ +AQIABgUCTOUqeAAKCRDWYVmagKlsSHanEACPejawbKD/QXuo+N1va5BPQnRFHblitFxFQhb8 +HrldxdWsYfFyeLi1MVq1twmoY7F60BTFnSomyRwB9dxHmlFdiYd2n1uhvmaV9Hs0KOxUevJJ +1aBNTvdKTmX7k62Wx7JIpUHNo1axBF5gv1k20Qg8DmmKmvF0jjQaQLrpv4n5dggoFRMfUH9h +8HLFN+aHyuWpO2GCCIiBtuMIxt5yVyq4z/Fr+kySfKCWWoL4qxLQKWUOG+4ZWXT5v9uWkBeo +hyaveafMH44Nnmk21/RR+ONAInN1yMI+pYWMsyKFXTcBC2ZKawKL4Mrs8GjgtJ4TPHUGGyw9 +EeVUH/H1yQSwdbK6ossX1xTisn6OSJDxb+aupF/sQ8uYbZmLlY2y7oQTijHRbs2diR5hFhFY +tN1105826liPioqFvm0yPIOgxRD1wvJ9wf25sdk++pkI92GZcb3iSo/zGg8hW5rBD+dGootu +h6nQz6J1fDVKvnzz/FlJ9OA4YFV6jtM46pnhRHW7Ph+4ciS+Oc7j+MKLJk4Z1rE/j1MPLuRi +5Yzge3Nn0QsooMfUXgI4XwndrBoyP9jkUs8+RHq6qaw2njKCeT0uN9YOaVckSuLrEVlsqQ12 +lHruFF5pOMrkV+wG1bTFk7lDdkG1+Io4RX5aOGcH6zbeUf7uvbb1kyRKybvddSq2eaNKEYkC +HAQQAQIABgUCTOes1gAKCRA2Gffm8mK6rNHND/9oCDrxOcataHeY8baKqo5JXcDRX+XmIsYP ++Tbet0CJqlvMDY1oHytN37A7jEc2gQ07fxkGXU9tEbWJlYzUsvZaWcecvZ3T1SM03PY8NRBY +gDJ1s5ae7HA05O48CLIxASvcIlpIWUdH57CNCsfsu2DcCWnjiyKz+XFFKarNks0rGGhll8oz +OqUvsg90IKOSEdHM8yOzSazHE2/TIOYlSQWrEmXkTsyaE+YcUHa/t2/CUnOGemWeT7MMo952 +BL9LE5JIOBnfdwFbAB8sxurIklzZV+Zewoyrsx9AKslc0ysIWuQ7Si1Igo5BebUASJxSk8FE +/+R0ijIelG99JYFGBcOKrO+KGhVMtYQrNezLMeuy/QMe95szjxJIsBKsppUM4AgKNu4hpZzd +DWfhdxsIKORL3NLjybRo2zFfy3mB2ha59Xj4zqAgWUHewln7b21outV2uRYfsXELpFZHXIah +cUPmzLS99NkGDZRUbmderML5H2Ivh5olB7oL+Sefx1Q6iurkue6dVcPYOHHYY6cKvjtctGAD +UOe2XG7A9uVzflV3ehHGWsOHFgYUf3onanb0F9NCeQyytkbbec/CALQTpfwrMXtd7oPumbvL +5Gn6W6jWjmEE7qJC4Phymm6vE7akxk4t/Ec+m68h4WHTYz7H16nRbz2hwjUa73XgCHnTDJhP +KYkCHAQQAQIABgUCTOes5QAKCRA39NXVa2rxYmTqEACeOSJ58Qfsgj3IT6UiCQhpLW+Usx74 +b9Z9zNJYr/YSee5lfGyv2+ocWQbhGjExnf+4bO8orxtXdf2uizFY45fAQ472ucMN6MUPEP6a +88cmks7UdSk6klCQ2hUMRCbL4MbumIVv6BjQNeEI53HHL9d9JsReGVGPukEnXN1htnm5IXhK +3HVVRmh7xhkKXGsxXCocpSzsLXfYxT+bLVE8atqhgTZxb59AJfY7ej3ODZXHHv1Mr9i1akKx +hyMZ6zpza/TLZRZP+6P45rfUBxuY+Re9ZKYbHfwwyijUuuIin6wFAeomlPRmElDG26LDfQ1t +PMP7cmoeQV1ne8YpSfhLdzQiV47u57hn9O4Q7x/IPQQA3UEU2naEY/+ZvQVGhsFfbOwSwo2a +OuaQQ5Mc8VhdORiJt8h4dCRKZWYY5nZ6xpaQnoZuoBGH1AT8g8GEHeczLXGBsQ6oKd6g5Diu +2LDgIHWLVkLMCSAYKprmMAyB8plpW/rjrgDfcBI9I1uG7ef0HzL6rKKvLF9FiX1k0rAXM6Kz +TtRz3q6R6TbTERuyf/SK4lMVp15eo2/hrZNyMqHg+PUH8QY1fzyoAVZRnm6kC592302h0sy5 +GN3SeVYiVmqou8uSo+2gb+i/QrKzGzk0kL73kHGvvIn/OCr//aZFetrgrZFAn1h0+D72y2dT +RvFOOIkCHAQQAQIABgUCTOetAwAKCRAuNO9thRYmnmgWD/9rEVOJ/yWyVISZyJUCzr9w4l95 +G2C08lhXc8+uIRq+52mlSayIsQBFwJwX93hkSgNp3BVkXotAmY2kc/uGH8OS1Cxy0xCpNhVW +5QWUfgXpLV4C6IDOLxK8mrr9VLXnHhDJ23F26hvViU0/Ln5tKKrGMKV1+xM5j2uEG7hhWM5e +QaHff/noiZlTkNlDnXSicObZtmdAMcQf/Tjz1N/PUAamd2YbqDOj1u/4vinGzyE+3qfC1XI/ +SMtNOLbMZPMzM3wg3OUfpNj/alHtLjfdvihBL38aG4WuVa4SovEOc5pVM94GGjryFAK3gyWE +JUUYz1E3AXAdq093+BUAtJBhgiPB9hchaY5ZfbSfq25IJtugH4GMR0jZYhKjJTwKWYw4t7fg +dkya3EGwPVRcdIfbntVFDeeOWfzDsKcFyR/tjivG+ZKEsWcVhD/tkUihDKFCOA5/dmzpm4FJ +IlW4Ib5jN+iLDEbrpb8BSX0cVDb5SpaJFj3mVG0lxk9Id0CJgnFHYp5KSl66vSeD3ucxJykl +YnBMGGF6p9xw+iwsXmVlSsde6YkYiAxUg1Axki8nE0UtuWPsU3lp7bxjCzQnI+/5XMEd1Udm +V9vTVYyIFTMdF1OuDYfEgKc1Z7buICRr3xSBH2ibRbJqBgXHJkOYwqctam47v/VaQeGlVUpK +kb4Defq4O4kCHAQQAQIABgUCTPA6zQAKCRBxIX0c/q5Wbi8+D/wIVbJY7JIzlPNxiruw2iDx +ErNi6hpwMshF/YWt7mPvVOmoGFBYfmiyEUJgdwWtaOWtQBbXJb4UxnCVEcCVjiZuNeDw7Zcf +XpXEchP4Pv22Chtc+HmxUYIwAJ1I50vudr5EBl0+mhR0U8A+0U0T3mLLnjgHqMOb/UjBGgmn +zuS+zD1wJs6wj2T1R7W42aVjQAK1p0CujVlVOvBljec4XkK4duVLYLSyTCFO60/m5fBwjYOy +IGhf8JkQEu9yzJpEoGeHAFz2PidZFYiHHjR9eu3cX8mV7zuk4vi/ZTcppzae3UEolRbIEf9o +wG+EFVhY1yhJgrN0zHsl3+9piAb+1VTKfZtPN01wtig+F5AD89h28YusSRyU8PqCFVC52Hex +SaE0Ll/50k7UI+agljj/ZhFFU1BuQMwoLKu4gCM1q7W7RADzotkwUscnEja35WfEeePZchJJ +zVmDHzdoW3Qnk7/kq2ZhMrg9u1x9rknVo2yBJqWuASST/DI0txvELQeI5yuhFfM0ixJi/TRj +r4zF6r9jdiFTFQz7rs+WMuCI17zh/4TFdhbdf9KSXhF+anSG0HYqaFjouQaT1q4ejkijoJwR +P5Ujv7kxISkrgJ6NgT4ISO27x4XGJxX4qahvxmMwPlIcaeF2ILIMNEb3OlExkQCT6Rf3aoeM +90pe71kVNl3eXIkCHAQQAQIABgUCTQ1UywAKCRDF0jGh4JNOmOC0EACKm9odCjLN7Nj5srD/ +DKCZh1V+t6dUxAZM2QcOAsPVXndFWSorcXvtJsYIppTGkcZU1rfSZThSuxCMVLKVwanx67em +q1oymAwvz869BJY4h/OCeWEx2wvovnSzsMr/W9DD0Psla1xHAWoyK97Q62cHeSRyu7F7aD1K +wMQB9ToOb00+UZsFu3x2LzJVqqkxWJU6Lpif1lR1iqiKJjPChCRHhydYEVE3/oAKAilhKEhx +1ydmQnK/z7D3KGRoq9Vp0VZhRvgGKb+EVf8nmT/6cU1LFDtswbzdFSzbCNJg3rTT/M5CQM5D +cnBg5GGUucYW/m2cofmv8HFgtWzIvfe9KRgtDrqad3wlhy7VoP3gDmIVhg0pC6Kj5EgxOnA2 +fd86jiY++71BFPVXdJhbwJhVuFzdHypGLau+pOuXu6+nWhReWmJB8JeQAY7nBmg6mQwvfPFB +0ayq0hNhHAkUEqHN3cbatRwE4Y97kJf/QHNXAw5lrRW8BiVUSDiymzgc2r8aSlbJWeFx2hqt +ir43cY3HLFwMR7yf3/P0kzBFYea8h2ANzzlxP7SoBQmaqBY7SwwBdRTIVBHGL5ucCNEd3eiE +um2Ln4o/YvdSLXKbGrNl3hFnz1RuEXVAjo2DbBDywoWtkomvQdGtaFVxgBQq14up+zs3xrct +8UknR8jmQmZ4SrBFnYkCHAQQAQIABgUCTQ50awAKCRCTZPt/6Jb0eCe0EAC5OjNcMaNSFJpB +Ud0gCGbHu/73Ur1jl6FFWuQI2CaElMdXL8QVL8o/6f+UtXg5J1nd+4qRzWg0cvAUa9Rv/xOU +PVcqLytpz/JOwuBEhpYsM7fx/gIiYrHd/wXI26/F77Xrx/XHpX6vtAgBnIVCnY62tXSK5C4q +vPbupG3FbgUWwLIpuij4Mjbp3jjGeJwc49G8c7YVYtVK99ESgYuY2gtCgaFdd38NzRcOZGan +9zKnky5gc6IMxeAzDxqeSqvZBXQRbO6+PMvnlGlD769HHf1hdVpzc7OXZTot7ozL6bCZs00b +DT4FY5VvOCGwhocb0e/ccJn+RIakMstIJtt2uqmMh/nnk3Cz0JRrnYU/vFEGezzVDiJe63GP +byWZVcj8A9jRzN4eulhhLyvHolRdu1I8zbIS6inTHAqPgm8uwywo0l/a/LXQMPXRhQiakdgW +BW5c/TwSvXdGH9c1uu/ZrjaWHPXtShL4GO5Er1mIXtu9rLUyHVgu5D8O3bd0gCqNe+wF28aC +amu9cdRC2tGZ8QnF0Nn2dV/2SO2kFTuRqXD9o31qH4enm2iTTgkF/2O9Uo+UD7wcsmafQPdb +XW/pZxr98I7XSVMXeQAayvxUWwHuOB9ORwegmZPE9GjjE/BYSTpsRa9bcMVayjQcYA+vnFlA +zN/lUvmc1kBa0PuhCScI9IkCHAQQAQIABgUCTTCHpwAKCRBougpH57HcJMfVD/4j/dLTTibi +ZVeHPucXvJ2QS6X/MRUtTeluVm8YSO2IXy9G1ne/eccFCi4lzzuV5ZTSfWg1AIoTdCOaxsas +J/RpKJpaC3LDeh0A3ENeARPPAxCPwaIiacxv5ptrz3cpmckk7/B5QMjZqxro5h6uuTy3qCjm +A8KNf+CXTudr7H318UF/7KJdCY/tXhm4vbTSxusp+c1tubqgXFE4pUBqzZODH3CjnmPRFc1G +/rmwyHeFRy2PKOg46EQALTB/H0SYLXqzUk3DQn99M5JZEi/BJlOFpN61ACfO6I+furX5DMpF +3izS6pW+3ae87TQNF56xCv/UD9vahPiaLr0aFL900txs0AxnwfoPXRjO0vvdjRtNQa3nFLCJ +wZXPR9eevySQGmFuyIGK3AkPpyLwmz2QAnTFxcArn753mdQ/31uhUL+2BCSKVyKAZ7ly8jZU +ZUoQe9bsME0og3iWTNmaPLjBxScPBxXiES9y+SvUL2qCm3tdzKJL0L6g2MPzCUSd+Cl5FKq5 +DGMvJ+QwBss+ef45RHZGblHSblH7vj2GgpC9OxcBE8ZbZnAGcId7+S+jXf9ifzMzbTxkZKc9 +QwspqUZozI/d0fVnCkEcHzUMaGd3AQXbR18FYJlBvtRHSCkBJx4iWJpCWn7ByW6Y4OV39Xuu +wWgpMPG8SDfSa7a3jM8YE3VxSIkCHAQQAQIABgUCTUJuhwAKCRDeP5fn7iJpXHKDD/9WvSnH +wH5VvjVdhU5YET54HfA0JYAeJbZwR6jat8hr4KmsWl/ikOwy69iLDw8XR3wMCKBR5Lg+E83T +EYSQhfJN3xbGBnnzbF/xENCDf3l39isucduyz56OWTTzMXHk05gP4vGYuCARmmUKq78jjBRs +ryYvHna2fnorgyLgBI2BEamvmt5y0aUMJ9o3CZid0oZERXOJzyUD/6vdoL9YrqvLUI4xHkAt +W0+Wk75whL36z6kv7gwnUfXscFVKukY0ZUFAcgDbvXuLFEVOa7VtEVBwE3pmh3tRSppxx8jA +7dXGWNUJtOV3iPYwmPTJfTHEhZ3yPQv8qxoYzPm8dr+59KAdPrbBMlSEzQYIz2LimuoBT4yT +trmfC5fpPfYU7BEkurrzepyVcxiObNY0593/mjpGxrUofWSQBFtW4kZJvR/6SLR+ql3FZQhP +6iTBY1aE94p0O93nXBQULobMBeW+Q20FMfP/kHuQ597IdgSIJklWI+I76DHc94b73vumKJUl +e0zMfI+oPxW0iwfQE3919qNOqgSADLqzrqB9lE4rqw3jlIwop1/lthm9u6k4S1E+N3SwiJY1 +POLxz9kNpQ7XfABsRoYGbEBg+0ek0UOAXos8Gm8waCPovU6DvetVqBQj7wuF87+94fd+UbLV +ZL5L+W0tInaWbym0YhmQhAXWaBSD8IkCHAQQAQIABgUCTVUwogAKCRC880RXMAFtU4NXD/90 +VjGlL6UeAfCcUkVE8XIeY0uhZxWZM0TtnkpxuslIWDqWc/L1t9uyY5U3i7q0uTHubNrQo6gk +4VdbERYcJRN9xBy66lV+qsqZ5pvLktVrW8rFJBJObEuaG51ZAvw5PwL0kDLCaq4QX4AD5dDZ +RStko73azrEFg/QTG8IMeBM+khpmnTHWaIXR08Rp30pQ5ETVzc8Izc/Hyh8UJ86bviKLQ/i7 +A7npdetDcF6rDDWA6qdK3BMDZWQz97FazPFn0T+CkmLQ9pQ1x6xbAeVpVDa2gbA9DRKXAiaa +NeZtA9wFp8oiMkDAS8hPDYka48ARuuY4mIcBGKYDrBfyUOkMwU79CsXBPGUDItS/42ks2GJU +F3lFFCEGxNCtbd8irhXXgo2lb5OoQY1gssn/ejCWW1PyK0sdklbKH5sCC4Yhlj36JSLbUnCd +zHzq9vKhgbfs9eTkfmb/FOv7f4qRQNRhVWG8KcRXo38odgy0LqeSAUktHEFEfk9Wc8EXQ/Nf +1Uq9VgmT8OC9Rv2iv+/PiAixkXLQzGfLbaIFIvlTXpQXURxPN2hbmB6Y2gKVOJ2LyZU/rRDb +q3WM3cz9jHKLeC4apC21eV1xAfmGbM/BcUFo0y0mpLeMoucsVfcR+QBWt1FUiSRtgzLSfDkm +mqWUQwkeIm+UyXqrZtta/XN76FT2ABXy6okCHAQQAQIABgUCTVUw5AAKCRC6dnbW9Cu6oJPD +D/9HHdCYZ0YtpCefOQF1ky2yl2gBucXE16asOe1x97w07319mN5RJEfAHteDDwZ8lJTfMR+m +T5rzUKV9TDPDRW5akD6pP9VfJKl3BNRax1yD7JlbnNI99d52NHL88CCk2H8HO/qh5yGow9K9 +X7zwKpFMI9iMp6ncGhsLbYXfUnDrAT5MhXciKn2m1aGKhBOQQNWkXiU+irzMtsLudUIfafkM +mnBdMr1+fNkkFTe3bLd4+W+Fb2ra2GmNgiJJ7FXMedamO9kWvyfDADZ4RcCrRAPFJ4FmjBoX +PgfcDO8MkEflZUfZWLvOy0yqzDTIYDIs0GctNz8WdqBSDyl4gM5et2jEefV9Ff1Vl69lqjgG +4iYANYmxrjgsapkXZnYMaZM/n1BZjqXkzqjG4iGH1knlDP59f++tTR2nkiCHXlQyUdKoX/rA +cra7HqktfW8FRbv6Lw8Yjqx7a+/7k6U4GdItoVvB8wZL9WUDLZ1St9Lchc6i3C9zHGHof022 +l6eoxMpo/xEVIc59wx549i6jxhX9o1dblP0utV1hnFtqLVJQpsovbL6KvZ47L1tjKBwTmiTp +AdsxM/WPobePNl9iy8EIJR9QgU5IvYXMhzbAttIL7BsokW3/x2dBtqo2eYk5CxVc4ioKhvBC +QkdUj9BaqaZt9LYb6qBFSJxyqTPKRGMhqxflE4kCHAQQAQIABgUCTY/TpgAKCRDsO1uSsBP9 +YHmfEACGUF3hcjoC8tSfFOEg6kaxcLv0M5Rh4OKKEo4mHdQa2rJKXk8jtdlbsxnmOUeFjWx+ +m8j8RsaamO5fVx1bM7gB+9hlcL8uZodxindaNfSZ74Y/3QbwHAHF9TFECgfORycGdg24LNDr +n+SkhHOqVa3J8A70vuw4Z9nRvjZFU82suJF/IuJH+8HoFxJlcD5dz+yhpG5Q2MQ6rjPBmaUr +wVduTPzX9OTtsf5tKjdXJYPic8KT/iY2Blki1O9qhGCMiCZ5aHkuUFr+oNa0eJKeMfqbW9Ha +nGI9RL1UX+DC3xBnTCJwtbjXcTeAGeurGbi8iZsEGRduSWE4dX+0VqXqs/dy8986x08b30df +A4goHATBYQzzR5yxfNEyfBp8inHX8ZmHLOM7JAvN0PWj30ZGX5YGMId0bgqQmrTm4VONOpK+ +RNIFBIc6IEfn71EohuOuUujSJ12f1JCPsxdkMEEs3Juu9jsDm1KN8x1JK2Ubomvf9fgUleJP +HezLStylN3KzLJQc4Q2WBSm3B56QDDpPMifqJcfdRZI/HHkpBF1F0GcN+VfAyQ+XSBtu42Lz +pEF/HJa4H4Ow4wsAy00K+lCn4PbCP3zCYJpTOfyf7+CQYI0BH/4JYOOSwf1hpACUMl0ANXWz +uO1cN55EBSodlSxcTJUzf1p+1nB7D7O/lb+O7FrcZokCHAQQAQIABgUCTeMlRwAKCRC79Wdr +Fd38WF4REACQ3XUa0kZ7HcMslAGOZc2f8LV3vSn2o0o8qTwSc/xsNhdzTi2d7FkUoTk7djC8 +vjtOYbXHZEpKS7MkK0nj+Spkf/PZJbqwqc4HlkrWSKYguz/2bJ5VpgRQW2ufq8D0kfuWgxZN +j8RU+ZQGOKIw/RIcQd/FCishV+Cdi0/xEOJJO718B/5pALyuaBu3K0KfabRxdaz8LFzK2lWp +n48UP4qDTS1g92LKvb7XaR9e+Ycj2Esg07B5nkugGYcGKUf60+Knl+Q9M4KeShmzi2xYiSJB ++tFXHa/3tEIcHqRP0C+1rOoVkxyf9XsUfDGGVaEmjaAKBJBzAX4F/vS6VZd3BE/DhZbtcKTh +FYI4DiZt5Mmu4I9Mq7d2hWJjGkWQ3g6KBbwpVUutlQ18YoYH1ZsiEva896MgIv+VG7a/2OoO +joHg5/UgZBokthS/2XAWgGbe/OcB6NKCcLEqW7/7H90ndTtscLUX/zF3YHCIZwm4xtJ+hxGu +jzBacLIFwiIXvaRpQrfn5NEOGEb0IFpIEKMAXV6BK81js7ujMj376er9eMJYnNp6fau30Kdl +WCi26cldZPJ3zeY1s1kmPmAkiyWo1w07T54GU6G7sJEAyksFp0bhAwTN+dhfdjTPwcoA+XnL +GqxrBLOT2ZSaUtGLA7hxqNg0bb+xQISnu+QyAEaSmp757YkCHAQQAQIABgUCTf6qzAAKCRAY +uG+OyFz9vyOyD/wJ178q/WzhAtCUs7I0UESH6KJPe9fItvP9kjHTVnQqqy1GEI9KYyntQiiQ +3kOjc4CxUDu6P/5gTiw7e8/Wo1Vp5KDxTfSPfblJO+R70z0G0QHviQvA2Vcz6h+28rx+UQ7B +hfQKTYchyD5BwKyIHdOwA3unQ9IPIWCr7JcXp7NXOKElTC+G8ploEMdRBekb56L4HqJJ/yp7 +eqCQ2maDed1P7ac5HOBl2ojWl1t/bj5X2SKWT3k77o4a1MOF6Qi5rViVB/XCP1VtDa/Ronfx +WG6k+6jojVVPOKbKWqDAvhaieMToD50Nv95FAKu4RgXCoRw4rNowZRR4sCdyLB2dMf7+fXBY +PJc7ih2R4BbgZeyNwtyu4gXN1zE4KmBdd3+fzY3kJFNpBZe5zbGwHpNqx4hoaNSuhe0ECF+y +g8+68eHgISjGn87JIzZUVPGGHnPIWcK4Qz32ruZdnkZUzsHa0cZxXmrjIv5PYUmEKAVWH4Z1 +Aopb0EFRJnfuizPnmf4cG1IB2snG97gywRCdkb8c2Kkf04X/uyuFKv0UP3j/Kb1GEBhCK3QG +jbXPoZiEc66aK5nZN+RV0oK7tM1PQRpANp+u/hfnXXQ3uSK/XFrrThrr9PfCvmRJAb0OXRCO +F7r5TWm6QnwuC54hd+aL1Jq2YZZJrNiQ3eI1SScepetI/zts84kCHAQQAQIABgUCTiBMVAAK +CRDGtSm6kRYhjL75EACJRnalvz8Ei9nAj4N4p6avTHQdTT2QZJDJMnGJi/5OqGCnz2qy3flq +tFbFD1Nqkv/k3QxfeeQt4tlYCgGLAOnfTzZ6Ecei4bdKzlA9gieCg+ctQ0KpJQCx3BbB1Qwf +uhRHjAr0RWjxqrMMjiZDRxrpkcq7SyZ5JWOn43E51D3M3bzy9oKGC1XOD/ZCVUgPhg+jcX/n +A5VcAC3v/UrhwgIo2aDgy+Icr0+7nvpM/d1emSp8+vMvZ8IR24yX/4KXgFQjLq0pS0ABFmlW +r5Axb9J77JBTrPb6Bz25fi5XWISzR9Rd26ycjWxPo+/tSh8BjxgvV/IBAkX1kMDvCvga5Hdz +7Anv6Vgej+QXhRtNFsv9kd4mT9dipJ/3OmYYW0s5/xXS3+N4xV2doBMkDZPZ6weLBAJyVAd7 +0vfRx+/wL7r5r0qYRVWo5DNpayyQUfSwXsIIY+xC5lG3HcoF9WRRMneoIWEJpsM+rvVjdkgF +27ep5IlsXQCAm0k63b47lHFCNdH+IKjb6tnFa8lDxPZkKaon9WA93BN1reeaa9/Jkj0+j9Ux +lffYCAuWfJGG/UbXVdujJ7vetb1ypQezK5AViypekS9cMhN2ZHJ/YjIQxDabdB0tjibG6AML +l4aPnq+sYLVfuvAerjmIYuAUXp/h2nOt9YFVgxik2PrM63B8/lKRI4kCHAQQAQIABgUCTlZd +DAAKCRCFnDptopMh13j2D/9Hbfg76oYapsZ8qwVA+9d+KdVQ6+Kyxxv2uJycgvRvzzWOEGPm +5hHzlV8zGOTiJyxlgGC+6QA52XuKeVaLWS0A4Rl0J3I8xQG8UjDLjShwOl8mM2fiGDC5Q7Oz +eEWKo4lLDFgAc8RUyJNWsEbAR+bFUdG1WtKgertf4l0JTLXZCxcI7r7vqQY52gtYov8Tb68Q +1hO/V0oGi2QPSaWs2wqEuOPMd1Z7A9hQMleW5flu9E9NR358/HGdZbMfNfl3cMpAy0pQOpQR +qKd2Ph8V0W5ifu1fXXyif18CzWcPHyEeOaE/WRgAL7DZmdTIog6BFpZbENJp6er5/na6awZJ +MMD8eeOq0ZD9vWEey1kKV4S0mrCcJX+XwmW7f40fIfyvNx8vZgUVvU05vx/FNmLl0eDq32A1 +Od8tr1WgenrTxfQDlUtyR/eghL/5w1G6HgNXzjH8rVANTJaG59OI8P5ZbVujbqpNxmIvmg56 +TW7oX1ucTUkCAv2kRdMJNtbkY7W4zpVoeqG/3S9tE97tYcZGwGfWGyVct1I7VAAtfYmRmPws +C4D/l7hLo7C2T8edurBhbpM3S7tD3mrUCEhVExXryyKSg+MIk5E3ljTM3ZE0sLtGqA7dVlGE +MtSgtu3BbXqnKXFSUGaXizVkzK2RUl8eIwmljqE42cA1cw2v13sfJ5i1tIkCHAQQAQIABgUC +TqwEoQAKCRDcaQ1XhbtIj2ylEACAvwMnWq2QQht6dYreFyarsTFK/dwVk1+hjpMHxI3w2MHD +9pOxvg+wI2uycDr7HPJ3FJz3E/k2YscwrGmfe5a7Ec0DRgi+TStfzTyytjTOrD2I9dSK3Oh1 +nwkbi3BbemCBe1gy+tMkLGEQcDmSXUdJwhRPKA8PvSHf/f1kwMCnGyFSJbmKysAJHjPDcFdF +6dOXKJzaoI1qJsgk9Xih7MPQGl9vx0e7lkB81ofUzEpD/5jAERtDojNP/Gm3qnrZqdou4Tqn +LHMR/k0ssU/o4KGHq7IM51nu6ODtMQNKG7VwLToSepMeOMts/0BWpr7w5H9TO8peqkM/S6Qo +bv2hvkn9iJJuIFmzH96aEKZlzKE6ZHuHYDR2sj7d6mjy6I+7WG1G757ifXjkBbX2TbQ0D4fW +gjHnzsgFrydJXSnubectyleG1BLJAWBrQrDRgSea+kfw5TjaYa0A3nx3AALGsTavWMvUTART +J5kOw53tLKm7mgr7NCjeTYLiTsGGNoPFWV//eXG6PjV6uYv5nYRnLcYTbqSz0o2pWxcqkWUf +3cjBuMjHbspvJvlFw3WZIHhlF/epqwxCakF6GsJd45OY+Wu+YJsKa9tz+eVoIQpqOCdcazv8 +XGbQSd7eV+qduIwB5xuf9/r1NQpd3/Eedqs+FL2QMNCNgv/LDB0TCm1OlEy6JokCHAQQAQIA +BgUCTsB/ngAKCRCQFB7i2ry4k7urEACIPOyJZcJluM2Qw7PmOM+lMmfSR5e777uXdvv16/xb +ifYKjen6izhphnMUY7oBPC/1K618UVAmOzy3JtyJ3FEkFrMwMQdsYMzUll/MN+KCIMwa8iO2 +TPF3b9vpFIn+sISl1nflCwOlW3tO9jEJDCrueo8v8SFGvoGfjb/0tcs6Q5Sz/JL6G2yKWLXm +Jg4bzHpbOd/CSWqkc5U9WUVhJ07javisjqBYPNy1j2n0AfWtPzH1Ha74jK5io9xNJfdCqxpq +TtSEo5+wV6b7B/0i4LjdMico6O5gTiHT3J3UclRVie6pQ25vjU+QjDUf7JO57j5fK9DF0phO +MPEYuKqfA4GvaqoJBBV7neS5uNtWtzEQOHGsZYMP1Te+GPDOG5CDslHT+JfSKEPm/Nd7fgrC +P9fzSz8sslg3pI5znzKGSJoF1qUws0W56Yt4KPEI7vUAYF8C/9EfM+/2PnzAxqyVHG0PWtqP +27RCbee6nFXBL95G0kw4bFzI4Zhe36OuZK5kxKKlOOiDgTjH3T9gNqpAdiyVBlRoaQxkrWJI +Tn+gAERUFnn3lI5P4azjad9Ld3jfSsnjlc8unQnRmw2qymKsZ0aHpi0ssJuj5fWjXqsHzPlp +qqTJG9qTHgVK4icHpDoj7Qo5xA/DBsD2k2ouf6fqx02cgWFz/xdSS90q3oBaG+xKgYkCHAQQ +AQIABgUCTs7VXQAKCRAE7wIvDBi5zATCEACTpufKpJAbchpP4UsSB4WNRss9rBYoAqAPknJ3 +fP3J7AEAOFXRWETnFfWbSqO2PG0gj6FtkSg1H5X2kP/qsnjfEjjRfp+tAYyH8umRqbZijk0L +3JUvf1lxfL4cSTTpKeBxP1CUqfdgRTQyOnXIrvcNy+CCo3N6ZpLmz9nzsvkSRauItGWOtENf +jxpI0oL+sbepnI5zqBhhvaaAZ9Bd1ZxWwN4F+W1Quae/9wOHI1YFGE33sRd89KaLpa2sNKaK +HvXphS0K2TwHWt0rE5+5xhIk70Q14bNW5YXHQ3eeN+5+NplL0J9K5jQhP2skg0RHdlmHIvTD +LRatqK4Y5EjEPMP1wSLliIo7+vsaSIDMpSEi9JGiieALhqTDOACE4/9UXf8zUiUzxiOqq3BG +gsZIxIWQbVd0KX87oXc7q1KViQnFeBtkfgExtr1RY3U1qP9VTOk6l0KrkXBKnzrfFQ1NeMVh +kxloQA2jQb/Q/mFH9lrmv1mTNDLSVPrbo8eWeUsMx7L8fJlqP8yw0BpjAMUv66LUTsa47ZD4 +pe1alRt+AJ/kNnnPW99JFsemuGr0uSNx2TRB4TSczkPqnl4FLoIrIv67SgekK3/3TaFNxYOO +TAZ6TLvfQKFmnlkqZtee9a2oMsavPDDSTD2Ze3QgK+bGrGVND6hiocetM+UuRN5E900sfIkC +HAQQAQIABgUCTs9XkAAKCRAMm5uLP24UILq0D/wIAayhxHzgqjFvbfafsJM/ms4wLwUJh2PN +3ukgTWfe2i2/yoCXuhXoPEHYcXxeqaB49uFVLhda/k4MKcoipjo1GLNMfZ2Atb14l3ecdQIK +e2oGc0MCiopprohpNgBYwDcfJsQi+rPFDvDzDHOz8FwQ4ywVgWJVliAmdqevF9miWpd0dRbu +jIX4owO0QDNVSVZ4z48ldlybAYOrFItdcDNx4o95Gpxh6F65U3laKSSDY4jkzeCvcYXJhLkL +lpPiO7/e20eqCyH58C3IL1m/67yZ8v6bhtahMMuyad6VHQsXN6kr5OmTM7iDHKCge46bWRDK +9x6jSwSzsEI1XfuafmToy4+gzRdK4IqMohY2198cy+Ny6c86fB8X45GDs+RDFj9bf3bImc3M +6UAtA46S3XElPiRPNhaVoZa7vA0nclmDEFpelRs8zhmfO4dB30hrG6SIx8BFuFcY6FQEmq60 +/gzCz4L3a3LP3V7hXHDrW6/Wjz8Uy7OSmMlfcPMSMDYvDkL84EVeQhOOb1mZrROzjJ1QHpEB +bWLJ7mh3hhI+lFIHpcEII+FLxqQYn8ygekCsZu8bIirSKE/IRYmXpvsGc1U/YFS9/QFabN7O +lkeYMlUOuXQ2K3Z/uEHNV3MgGiRUERzmeMe2dR+T0+4jTBK3MW5tX/9YAKPLAJrgDPVrItfx +X4kCHAQQAQIABgUCT4LjPAAKCRBkmO6VkbEf6PPQD/98cFsYdcllkZq45IQ4YgyfLBjOSKD4 +/DPdzazledHBapHwjPnIGCipzsE22N/JbJMY69i/bln/AYzS2hZXYgwbEQ39zd+HdX3IlXY7 +xvckelJ7NE6c8zZaCnkN7Ng7J3zekxa7/6GTpKX3WJBABrn3wmBzLu0fIgIajms5inUUiOCQ +vWwgtyO9NUURzZu/+PrJ1c8hcXjtjPPxvc9M71z+i3nAfLlVUJnJYVZvH0LUzm5S6ilLhrPv +90Qu07yIlGMF7D0ZwZPT/2OO+0qSIp6GCvKxFApq8GIfdffnJFo+XcyG/0VjG3n1tge+DrYN +vR1mYuFOXCRQlTvxEJBc5yJErfETLAY2jgdxnrw6XrANWq5n2FJhoHWnqwAsia6/YbvvrgNt +DPZI4z8M1Mikd/ynJfEfT56RPvOz2E1C6Fbj2ukxvVlfB08cqBb7DK1ECpW4Fhpm2GJKLvaX +zPm0Rny+nj83h0OTvVvksR27sOLkw+GJQMV9iX3O6OkVNmm+OJQ5kxJL/NqfCqiSkGg3ME5c +ACtCGR56H1deGyCTNr6lUch5lq+N6STuWifRtQuL9jSFjvRR2u5mPTJVP1nFoxdqjaHN4cDs +WifZ7x41oRUJLj8F41vypcnW7/4rFJKB30e1bMVBgnMasDJBnnUW+j2URU0Qe5Ff2lvQCLcU +MPNfn4kCHAQQAQIABgUCT6AKWwAKCRC7NqAoAHiN1HDsD/4jUr7kiBAdjViK1IRStoghmkhO +3JJZ24Gzi4OURjJg4XZXHINVVQa905KoA6kiicxfy6CF3BncLtHwm54ZS9ZBFAeNTHOYGGae +3LB/hKcJvC8LUh4TP/KhTRHtvE7osbe7LSclIztZPhsxCSb1VEZ3SSA8TLGvREXBVz0HuO/v +9QptgI2QYEtkiStFriT2VUgHFGFA+BYIwG+hvH89dgvRzfT7HLdOJ+9QxuWj+Hb5EAo6VEMH +O+anmvNlMr+R9mqxdobXd3Sh4I8Q1xVpHQFSXsCpH0IBA3iqYFIh+dbMTj+HDMfiWq4Q9fbZ +rHu7nE33HztSpGjWFNUFkJPrxmJS/pd1zNip/fAbrMz/ls63zwocXXEw2KQsRyb/oNH3ULv2 +WqJL4PsC3i4rnB4ITbNyCI5j4FuWgm9S2BnVWDEW9fWtjx0XiX/fAHzBH/1WrYI5mfOYuU+s +t0Gl2x2ja5G4EE3Z5ojl1RaXUxswoFR5/axWj1r/+7DIOk91svJ0Bppknh6C5ZTGCsr9CFep +iKN4TU6C0ohCK3zjCRKoyNG5PG7IKLLAeTrCWBB+xnIq8jad2osfdIWs9hBZE6tIxu7qqX2S +bzu8uRe/Bx5ArjXfh1tcwrlD8jfUPS7u+7E3o2kF39FkYEVt3SbgIeCBS/3g+uDyEBFpubyY +gGCgdE63pIkCHAQQAQIABgUCT7fLdwAKCRBixKdRBOlYNudED/9tM6N4qQG8OewLyQz+ExND +D8p0wOrHxIgEs0qUI+rx/j+SXYIpDk/PqXIM5ofET6pbTOW/Wlu2BH17TchHBkWLBNtl6wQM +Krk49j1zkSQs8kwztj2ASvCGqs02vz2JGMt8aaLeOJYZBvbSdD6YP5/ad2C7RvlajLKPgAO7 +rhipp448W9ssCLlNDKO0FDF5SZCrrS8fpd5dhl//1UszoLHhiynJykZX2QXCfO9dFTPoh2fS ++sDuvaiP19B6l7SDUnZypbhTnJ893kMFlw3TsU3A0D9gNFrMPOeufRq+OP8sqQTNzoE+CZ/0 ++rNZAgnUtKT8nmU6cJpe5o6YQ8HJS4f1JXXGlK3Tw5vvx3A6jtCTeZzkT3tf3DZmoiB1OFu/ +ewqz4tJdFaz9Jn7Vu93X0XZdeztX9+XEJXiW12JiQDGLlhTYDVGuhWW8VZfm7lVDPyk12nDL +qI3ix5J2EdC83G+IjsARGf7y4v4qXI06WPSFWMHkgenKOyr0RqIu9A9+ffLeXuBHVkEomCk7 +NvrxotGmFNiRxLinSPjTveyqWhuBW0pUtH8CqyGe2bEjqyWj8HZiAWF/g+brAecRHmPq0yr4 +YBUbHz3bTu6msZ0VyHq+g+MsO+JkocAmXTnsWmOltmvqozuDVXDLzxwEoCCHCrMCDGEUAXmv +bhlzFiDugyDKXYkCHAQQAQIABgUCT81HhAAKCRBV4tQRB5YuAir4EACWJBCyWNutLCZmBMLZ +Q1FIx8Cpx11uTfeXJuK6RRAKtDmgS4Ow7QJTws1PAkAa0i59mNYWmwaTa68Uohw9b23ZWCWv +BPmLZXi2UcSIa+sQxEfssXeREoZ8iALQBO69YGytV6a2u/3Wy5lv8Tukz4fyBB1SDQoMAn0K +K+KKbDr0ngPIcDxVUjeTm01hYqpBiAAtiFBaXPt99u/A64tCNnMcGO+llz9dsSrThSzNYAir +QJAoplQQDneCo9uALq1eDvz+MY5ZLqwypYoR9QbxjtZivHR5sCRnawLXbzJytmtxf2uzQajP +tDCzxkhsQEyMLuy9nT5hVcqZo5Un+H2LecCCYQOeelT2IRmnOmBYL9ckoapiTZf/Qb9is/J2 +j6VSaH27/RqdVSK0mMSfxMj5kdGrncU4ol9bYzwitSSaABNi0Y3afX5NEmw7rAQbR0HiuQCN +m4QAfbUqrqBY+kCn01ZBqLHZXQi5I4RfeWU3UprGVoOjuRUE5RtviGhwJuOp5RsVojTa8vD7 +aMXhVaj89VwplgLzIVtLtVhZUKPfI1ha/ggwVvDz96mYPIxcI0yU19KXlPkiSeGY4TgIWgbr +D12x02OtHZe45s8gV+r6z8K7CymBpe/DyFaoso4FM6fCKz4e6bw6pAt93/5xLbfbk8lOWIP5 +VjMi3uCra4byuncauokCHAQQAQIABgUCUCXsAQAKCRBNtTz+gqRnKIm5EACKWpQyzJn1x4pM +EiDBW4EOhlh4ZV+tA+DPlCIAiPmHQCTbpqpIQnENU32LpdIARr0hG7V5Qjg4Ml8C6wYBcr4l +nFkIdlNIXuIvmE5rHN4OioWUw/CzUSlT6Pr/jVmDjhBLeNYXGQ6Sl1Zz2Lfj0e2ZBn90V79e +ON6/nkYjafg9k6UA2J8wHnTYPU3Xq3sXZu4a4pfOabvYKDmZ9DM9DXmau3rsoxUvEkkM+/4N +lAVetBKdIhPmQgx0L8V1l++GbsA/BaaTGUcEb7dL6Jza3gLn52Ajh7YkBPEdcntfURtXQ6mE +IPzid7TrfpcnemXGILM28mZlep9etFpdmws3MOAUdJPNTgfdF/TmoytDHv5Osxg2HQKOdcIG +NjlxjtD2NUwJSFRPG8zFGijfftGBTmg54MhFJIH8gAYUwWKpdc3TTb0C+m3qcAxm6SbTpU7V +zQLtzZhTeVpXMAitN3nyewjy/oCZxlLYFilxXWAd16rf2XdgwagWG0HcKEcu+ENsRkcc3qYa +AhxrWBuwjOQNUaNHJDHMTIhhd6micAtBW8VYQjpCvNwiZqm2PriVZGON6kjLok3ynFUilv37 +3E88lS0mWqz+oSyvhsl97ZL9S6YJGHXHAYxcf5ONMb984udzY+fPsA7UO5T3cvKqlI1rQWnj +wW0J2zqSmIjmlGxPG04ltIkCHAQQAQIABgUCUCqKogAKCRAu6g2Zz1YtSMqfD/4rANWWFw+Q +6257A3UHSIG49lQEgQXNng+WUWF2/NaPy6Gvw0mcAcFsve6rTy+ItatV6cvnCqMYRP4wpVPO +nZ82x/EB2h6S9out7jyysdsq5SqcoSMzXiegeNOkLLCjjTdxuP4SoZDHI7hESaeUYcM2l/Ls +kuqFMwerUHpI/NObX8JL15rsbiz8M3AGVxe49GCk844tD1eToz7dil5gC0XmZ96iQfiVzr3o +mJDiKVp7NeS5ea1Ph3p1ix2jzHve4Fl2fEIQ+c28lGupJSp8DFpD6N4e/10YSmiVVAVYr2aj +epqWf0iy1vPbnu5ZbrV+LAl/VDguDn9h3zOqvfclblo+QY4IgMZzTr6V203JWXHLXmUaHGSI +kdAWg/+274tU68t0/+9+HmjcPRtd42nY6CrnZEQDVnRnpOtPdVxapxxy/mgb3PiwZVWYF0yx +siBZa3UBIxZXDXuvM8sSvPhPUbvmFpuer82m2EyvpCqfuso9uZN8854kWoVxbITJI/DaxCXB +An5B2cXvHB4jw4fAGaHq1wkQeIfFk+iop7q7OucAKM6/Mbi0dxvHK2OGLqXlviWVquliu1MU +1dUvMqpLMB54L8zCRiniqUfv4RPGl0W62n3/FxY1NFX2R+eT8EaTMj72raJN8NZox/m/wL9o +dIzBkRWEcmIooF18tQ9yTiJe94kCHAQQAQIABgUCUD/BpAAKCRBYweiR8NgoitWeD/9Ue7Tt +CNLlJcvul/NRWFQGH7JmSbIqVygbtL96vh1+JIB+pVMobO/q/V1uGO+ZgHFNHJjSV5IxPYi6 +TtXL3fEQDKmtza3f9Ogit8NhbqLnoPjdQ6278PjWPUyQ4t+rgFPly29Thkbnkvauk1yxQXZ7 +FlcuP8USj/rDZepr2hvZI3MoTA2p59IAEzsHwu3GlUo0OzwQttVibWYEXRqjP5SoQVGhENPo +829Mlxm3jjwiv+9w2Strmjvy60cpE7N0tQNh2lwM/1nhGZ/5vl6xjeDzufEgUgBhIhI5BDGz +W2d8TCrkb/nA++BctNDJRxP5hrFmFAS+3Osb1IEcyRhpO3WlPt7GlqXAYpDgeMTlI7mfSTG0 +W9BMhOjFscEBKzd288uzv7rbC7ss7D9KiVVp7exIyKegb3tNylfnT000ChMIMJFa8OOA4TQF +MAGqm4tzQDu02gE05AIh5DTNSs/h5QUlKBVAOnuqCsu3fBeL7CWenAmK+bnH9GKiIclXWOiO +d5dH4yEG/K4I4qCB054yRXV3084zdf6ZvEBabpZ4ZzgAL9xjgtajoTj7g7hlFwCkLQwIa/yh ++EkRlsHg/APHF7PZizHqEE36aJ3z3F+3gw29KH+G7GwyFIavk4YJrF+WsP9Cdvbw2rG9ZJVs +0LkJLF7OAMobpKUrWS+XalzS/gRNKIkCHAQQAQIABgUCUD/CMwAKCRD94qPLgAzfxjrLD/wK +dTb2QNMbZxKg34iDpKcc+h6/74XI8cHTsK+zfzsX6IklxA6ceVqPSoW5lObNKB9+wDC2AGit +ZE1niHT35UHwTBWRqhNOOeMY+q7ICCB6tm+SoQgr9tw3FNdDK4Lk09CHmEuKAINfEFuQnOAp +wHlp6jjhnNSabVWwRWlV7w8G2CZ8xbfmLWO6FZciaI0TaMiTYuhT8dD38L9mpspVOQ85uygE +wA2992qx2D5rz7eBwRT/aYTk0Sfb74LNsJ+zJsf19vh2TdS+MkcUp1Wu3eSXw9kA1Mf2zvSX +W1JYgkABkr/uSOzjqXjUV/CbGYCEnewEFYA7gZu2Ye8pvXzA0qFk1wGOFl1VZoJ4pXnawrGa +tTri8c8H+rxSQAlCbq56NURdVPuzqhJF0IZKzkF2R/XVES7we26SySaY/RAmFQb0QW1PQMGq +dp0U4wQR8UIPgODORCpHXyRYbiizq5bo2eas+YCYTAecvXT69c4SJM1sy2XZyMU3byMrfvCk +gaoZs7l2DQpHqYMmRUe4a9ILPkpIMB+xZMDsq1AthpsnLpEHSf5dw1sdzw/I4A9BM5fcpgr2 +BYQBVXhekqBW9bxTToj5SgtVR051RUW5HRJwbtOWOTXQvlb1p9OvY3hnIwjcBvrTYc7v2y+Q +BVUn+7U6+R+8+C2yV6NODKVcR6F4JKJug4kCHAQQAQIABgUCUFmD8QAKCRDr0RS4I0FR4a2D +D/4zEKstJrt98EDxKmCVQE66nKr8cesCbYLMPwxkhXT7STY94LtnKO2Hlp0V74pjPoA2INjk +0+ts5Jk70pULTOBfFMfjDkYfL40/Xdvs97tcZshTzN7FvOQX3mhN5KNz35y7ru9ILDANEAQH +qTDpRsQ/UMx2hFr/q0J3lBk7wYWjpFgq1MldEAfHcJSBcu7txrwdF6cRckAq62py9iSzt2Gd +LzGujx9ekNhGpLen4nmEruTBt+s4v2dy69eZjfRhIwdJpXtlIaA5i1ogMhjqwAEmjpBq0w54 +4ZQzjVBAJ9GbZgrgGXh+xmVenDIGNhxI/lpgU4JGawc5DPyxjWYzLcuEMQ9kEa5fgj38ko+k ++anNv26t4QeZTDHA52Xrl4l+wglO8MhGLaCNSgwOmaquRJjErWzWDTuj6ScBJ9GFZMaTB3y5 +6c9yBhsZRz95YTruZerp228azKtkqTPVgYFTY+mg7mwcsp1vATIr6eHKqeosNEoseUIil/NK +nL25sI8qs80IvlermlfJa3NZAMZlUTkBSOUPBMXfJEagSHNVWaHKZK1sgSElIhFwjwlXPZ2a +50GZdVskBtI2wnCRGhJY5u2LpdMpR71SudZY1dkb55kn/hJcdvtFsTmk6Z2pt0RYvD43m+Jk +5z6MFKxTQNm9ht+0KVILMOkzvdMgL8/MyRXGI4kCHAQQAQIABgUCUHge7gAKCRAsdgqbdk2a +bO66EAC8hO4UoENXQSqUZUg1hmIiF6FGKDAGpflWR1pgC532WK3hgGHiiGHXdegqhgtTJzhW +wqg2h8mJAlMmUJMuB7bIUAEHv7ym0rWW3bJf6Ch5BrXXhdDDukxK8dodG5nZkVn6wgrup3Jk +X21HckRQr4QrKuU1IpmBdAVY+/dgosJBhxKkJjzJG6RWb46icN3DI5sy3uLxiP6UfxI/sr2A +AiOkN+38nruCj+5BEKUzRYKItXO/a1eCtEu8AYcNst/VIiQ2XXyLdhPpM7Hh39mBBx5imgHA +yhUwkCtycWJL4Zd9ZKNzAMVM3S2obtJt7ZkTTUDQCPu/1/ZaTeG2KpXtLMEw+pX+QwOqsmXl +ibh165XppXNZUNLYoxDx0or4hjIjxu30ZcVHWfGk+rBgB0ELEjU62wBPD04ngW52ejRsxf4H +zHNgIZKSv0ycjWOXu3e7sSWtUOB3MfGKDAMNQqX3R9vHDLy8tkoHmBs/PfGV+iCJ/FW3K6Wg +f1lsjVNBdhB9yd9wae/6jiyS7gSBR7qSgVDF/cta1b0pDGijK82CNXdFyitV8KH2DjJRL0Gm +H67yFIWd32fbxNLPJ4IVi39dMGYUlXg4JNOhWhquU4DT4LiNkegE2Fu2jWCUEzgp1EUxrZhU +x0gthLyzxZBhMKQTGiRAfc3qVfJycEiNa8VPOwLt/4kCHAQQAQIABgUCUIqZlwAKCRC8pbsQ +KCJa82KgD/9uXkadirGlVt3tns4jR3UHjSG+Bh7id4pTcOvYAcfrEaU8UecXNYV7Decc3Tkx +N8qmhOguhpEzm8MbV9Y0xEQUBAK/w0qAgHCxm73Oe7sPicIpznzbQJJUI0+H7J/aY7lL+E36 +eSxtKaoYgW9w5MN0lYL/T9JbkKkZeDT5GK2aUCPXEzvPI44GrPRBrwk6W2SN6C2WUPGkfzAI +VorxjdBpc1RLlN6V471Xn0lsJEg/nLM9yYbe0/QZtnPrh7gJdle02pq8Na4sj5CwecqnWcwl +/NvpoND6Z9Gi8LYHBvMVJeWzpPjdzdm9r1tH55UfLPKfHD0nMaofQ9TogMOwkJgIhvQdiaft +vwwFKm0NyGObK9Pww5pjc8mm5EVr0IThhnYyq4TFyr2pIjBctnWOLHGzgb/GcpXTRm7ysQ4z +Un6XK06lMkiEjILsGcDYfOl88QVd/sxwPBQtTwMfpM4YcPfKxZAmGEyhVI3SoniWm1My4cMQ +GJJEM7gd+4R9JaRvLq/jE1vFvcItxgxagjA6xZiC+VmXD/bfYBzKniC2JOx/LtI6tcjhSRLy +G74ztu88fICqwRKD2++z5dy+jEJ/hfxUAUlhIKKTMIMaZpr3LbQaIFg+mqDEONcRRItNlSaQ +FRqakNFulzZqLcN5VnxtAyuyfFgQkqMgRO/xRTRa8bzTwIkCHAQQAQIABgUCUJzjAQAKCRD1 +fE3RpH1Rt/ERD/9pPimOpx3OB47NXmjZiOtO9AAzRWc/G0j7/a5UhcZXtoztUoUHt/3nz11m +UPWEvYizKO4Ew/bF8WcRulxCuLgN+dyNg0737Z+O2ZNkULXNZzcJxvWa9gKKdUFsfvxi2DH1 +pg4Tq/kmnUMwFy1Wf7JDtri4sQZS+Nr3ay1ncp4dtmF1SKQ51QA591BEGcc5YBR/e2eSXHS0 +qC8II4WFwFdLE1Y+AlyLTcErPmUlfHcgFEdFzBnhBAwbCT3HgJQYn0pEsN5/gvb2TjNDNkVA +0mJWb41m47BL7yqU6K6viJ/N+KwC1H/mOaXh7abb5MBxK1CRtspiqMducqOHX63Nsu6SqZxS +xWTyohLnHEehUrk1sM+hI8speRII7+te8lkK6VeVFsoan1K7ltZ3GGbPcEtqxKn00/R30wVY +OsfyrpXgEVP5WvI4hwOe8q0D9FYRisPEHVW4i6FAjBxXrFTINOX0X0kMOVX7izqbFDYfQygE +4YDgdZAwSriit2TtflROQ3dnBE7S4TjdxtslR713EZ814U1h8z4MYYpI1met8OEEoEzA9kXh +wFUNM3LVhv7Nh42ZVqGEiVdm42fwP/TNtKTQ/59oaGknKSqkFLW7684+43mTWwuRMjRV7lnV ++KhZDJDm0VpTWaDxYPGa3SQ0A2tHJatFY+aRyLsCIhl505n694kCHAQQAQIABgUCUKnizAAK +CRA+1+PYOo5DNpN4D/4qDs6WpzfBeSbBN6WQr1NE6LitdWMXJYj2w/g9fikCGB/L+kgx/Y8u +fH4XQkT/wGKX6DsldfHMcfaiLuulWbAN6VZLgXpC3qWgU7GuZyLn9i/mijwVjfxKjy/fIrK2 +22h3S1RuyAzvTdo3T/uHhw5JHFYjCh4bLfQE5fLLyYQFtccyzuARijn02/jRXrfHhRvA2mg5 +ECYQI8QJiXXpr0H9t+bgnVTEA9MswLIXfZg8pLV+Cb795zxHnEP4/5KTqEMas3p49XcpR2SU +Zn8mkx/uYmNLuzNVLxNC9YqHqnrsh4I5k5dx7hFD2+86rSKbL+icnHfw4lfvZyp+3bsFjEFI +WiaQ/hT6czhhDLmhoDeuDGmO7wpcZuxubA97qojvwAlBJ/8PywTlEqtFrn4gCoS+iSVxiR/K +meo0ABenI8Vft/hA06hC8VVciF3k/oKLpLj716kr3zeqZEnre46kqlIW5jd7IIofoWZQ4GRC +Ds39xpwoBInDX3nQihMKIM8F0xH8J0qZL2ue8KIXJbww9h/2DL5zb42WOZYkXQSFiDXj+9lm +ZRy01Q0R21WjRytzzhjyv8rwTNwbpHR4OgQ3cD9TS0yL5ApjY4kRq7yiGxSjCcgXTqjcLl9X +tHK0It4pFe3ClbRIr2pFYHXaCKYvr7rcLWqlNd1qKOITvtyyavPPMokCHAQQAQIABgUCUMPw +HAAKCRDGxdMPfC88uYcxEACQd/My8tpo2s9zzqqyFYwwc1ipGB60ezGPPLlfCKUrgWX/8bVG +YJpsehGmbBLn6WvbxrQovl2HtCF+9QEg7aFtaLM8AR4gHO5bnbpWyPgdKohFHgLeLfNSGA5v +xPyh6/BKoecIsYNp8Axj2TN6PHCRJnLmQ6dWYXPl9RHfOmeYCMBRkOgqtlCPQsgOJm7Q/O20 +V466BhiBz4C5cBbhSwZz78HzHAYoABTBWDNTn1jngXMzuQ7b/Q/5g58BfLm57093zVUqJ8yp +MAix1FVzVguavhQ3JUNTj+65DQbUz+uIn6w0tlNJv7jiex18Gr05I1OHYjEUaS7Ub3tvzS6i +nSIRfcNir9YvHZhpvIFySwPlRxc2DcqQpkPLUEqP+vM5SngZ+dPNNuWL1Wizf6KQwYNVCQz+ +zaua5EkZcSh9bciTISwBYZX7EzqPccFWHpNZKjsubvScQ3wh+PU7lofzF/gQKvQYxgFNdxdC +YWaO7D/Xz4JE3iGMEhT0bsmypTJ1364D2lKyhZynWeXGdnOjC9/KdMX5l/GvnOcBg4GkRZVv +mFHt/oqt9S59NLRpXpDmQQqV5IudfrtDjdOntcpuCgTJyrc3zVRoEAJjXp8oKMfhFrnDFyV2 +mJI9bs91UtBSjB4oZGoAW5yhYNWXn6gboijd+zOYZaRXqrFM1fvlhSfyF4kCHAQQAQIABgUC +UMQ8swAKCRB7OkqVr2NnVcSaEACiTl0+6o8v1+6zIOiNEYOswS1bwUvmewnOFmph6h/yDsF5 +1eY/nfI8p2OE1S+oQ8FH7y/0KmqRkoHpBtDBzljWeMVrdwWsM7V0VlCkvrur8ZwGIVnrV2aL +T4i52hr7Z0Cpm2Cmz9zUeoOjHP8ouz6758rhHdYnA+8ZxUV5lgYqXKNUptj96m0tlbpDjZEJ +3QsBMJRg053QA0cglKWuZNDLYpKnVqW7A2GoXkBWGrwSeOG/vU0Lqvp6TiG0JfalflF9LrLU +8F25QD21DqBs03yHaL3bMms0NIL5y38JnmXmOoOW0kT7BlTRBF/JQiU3NdEhRCCFapbvD0NE +67hmZJfbtPnUxw1FTxvwXu1nObi13K5HOsRGq/95IUtVEGuWQgBiyBw4Y05QrSiHACHLI0Se +qGRFvKZCPDDS8a+mdoxiAvGVjv2fEGtHPWTAGptK6DyE3LDuIypCFS/enggk0ewYZVOx0sQj +rpolO1XsTIMUz7UlyYMqd+hEJWT4tkAyicC0dTOBW2k5rMJBhPAighpdDcQBTPtiy6Do/ECF +JA3HN1KAbZOQxFFR7+WD4MU4QcEMun/DE4p+zJ0Ye+SQqglhov+CHjGbdRbCysonbZxzJ9/p +WZSlDOng2i8YpdFiWwARM8xTG6KPp9OGM5Ad0rc8rJYc1RrIpql/AcO+nvXJRokCHAQQAQIA +BgUCUMYlrgAKCRDOQW4fPEJbGwW9EADQ6wEm/NY76/S7XcdBg+zepcnOeg9Xtwkcern2fpnx +iRFsyV7ukyfAaOr5Eb7N5PtkciIW1DAReKnr8idGHNVlXkrxcwqFMnH8IoEhDkk0Y1q3Mt7m +5d9LbnLPlGq9CpQrAVp2IeLE6ZU64eFMQkTJBvgsJrpkHw0oclolg4/LrFC6C3mQlzE6hR2T +67OC3MYj3S94XlO1JOYRCuiWMFuATWbITlBlJVhjLfpAzQnFr/OMmF9XHXssm+0tuK9qpd4X +jlqVw/BBTpuv4b4Uy539wUWkBnlcNtW4/6U6kLXsF9g42Z1vwUnopqjC9ZbCJeYcppeAAkHM +nxFrcnN7kMCgon/mBt5ZnADzU49W5jQfFe9J/GwjnLNklTP2PhBxfDcaZ0+uVgYB/ABXvktz +geMy+6VZ8ifAIHtA2TCGfyNVvxCzf9n6G0voopcHkxq+eEKkijhWxF++Pxfyc7Aw3c2TfP7u +vqTrWIr3UegB/ElQCScFD2EN+sfCNnfN8q0tqtMCzD4GowNjbVfAAzNd5bjKXyoDy1tkigzU +5na6LmuX6PV5xgti04Bw1lcdNJpoCsO8GrgSMfBX83Cx946aMBsE9Xwx1CcMEGX7dndZJ+ez +c4AVqjm5yGkq3YE3Gl1Zk9gXCA7bCT4a+jGdFubfl+WhHMnTL6qYae42bidewrky2YkCHAQQ +AQIABgUCUP3tTAAKCRAGmdKigxpB1czQD/9XoGiHl2kzoJEKD4mMGQv76Xiwj/png2BwFQur +Leka2STdiSH6oLKqiC/UR+xbPEv5lzNoLvydAkWJbMymMid5sCIys0h2X6dSlKtUvOjEn40w +O/LDQMYKJID06vJq30492043Yd4C4NHCtRDstKWvixTmE2/EKVlrWKwSGPeNbLZGIb0wdeRy +l0WUGBfVHZW4jrm1Y+INgGTYWnY2mNZTFqcNQXmsX1xEOzFgWVMokE9OTt0BZHGE+xO6Zsd5 +GKvGLCU+Xicv4VN0vxK84U8yRhu0FATviAa5ItxWk9yRpW9pa+MUYvCytWcCUiRRUVzcCP6c +rZLrSM5nVUgNTCrctC7VZ/dPioHYtuaFzvX1bk/9+hKmRxp8nLy+U/o8F1dBFd+ebMlyqIj8 +O+oCFlw/ZpQ+l/Py+eJXVGqICpxynmBRYeYeBdb9hbpf5Ut5pNjdCBFYEZzOuD6Plzx263FT +CkslViivQF1Tsef4HjKsn8Qb9CGLDlyBYAxIOvKhsB53U9Z8yNzGWwpJBe1t0+mgm6RQaeiF +0OzJMzW6L5TKtp9yXb6G+F/YlMkwG9/Es/TtQDgx22ExBfop2YHLA72XReDF4epuxbbAi+fR +luxJYvizSkEJt0Cvctt4n8f+n/EA3dMIMLz5Mti+epcdYiaufTr4xzy8YSxs0CfJaIugE4kC +HAQQAQIABgUCUQEFRAAKCRCqOe8VomBW8plkEAChLF7dOTcGU262sElP6yras3zWIT0bsL83 +I7U/xxb2um5oFN64K1FEm6bHtpOkUcBd+Kiwm6udferbvxvUTNl8t6WwHCDTJ7CzkNKsQuQO +IHtAXFMVLFHjfTONMGio90PzvgUBSG3P5H1bXXIJ67JxT5hL/j7V3udPtSWaPSsLKAI9H1SR +kR+/EWIXE5uiYGTi5wVIgfTL9PkbEZ2GBpjj+wDKlgMTWgwj7VY/cZhKuaXWcJigsmSQEtrW +QwSZ6SARIW5Li+F99rXyDgc8YLA/WrRqRA/MEbRzcfQPLcEBJe5u606lWLiwXgVH3LKF7qET ++k2MF1nEAdW/4MYGOLGbnqP+2toAn9VtqhxdogP4cH201zB920itJPzEactR+4515nOwUNXD +hyez+/OYiecIXzEtj6Vii57OxNphO9o00b3B4pzY7ecrvXbpYA6q9ilWOnzpS6X5iO3Z9b3c +SlBMXvooBZHWJoalBP66ZgJPT6HFUS3gdBZU26/QYH5i0YPGXZWnbUjOqKq501f/HWh/hxMz +eoMvaNeWW8D8wzmbUVAlthinYPjVMoDva+4raiHhjPcvYMEiloqDzK/eCIewhAtS3xBZqmIi +rR0LMJP2bbCsbT7OjKu+kS8n3FcOYO/ZnrTPvzAccygcqH8Ix+Bg4ZsEz5tO0wuIEO7nHih2 +lIkCHAQQAQIABgUCUTfUwwAKCRAV4DVDSd3cAwxtD/9YdmL+m9Q0UGQvU+NVMJ5hJhJJXwRR +bVTcvfBkBcI3zGneUcMPcmw2gpKHOXeXSXcaqJ6KXB1Vq3q7IC/cMbsZ+rss+Ttz7uQ0CZfc +xj3cXqNKnx/M7pFp82P3obYCgNhVrPkOdaS58mKaK4rIjg2BI3afORLpziZeGY5By2T97D7D +seXMxMHNxK9LwtYwk3zWleqtXgzgB28ZAyihuQo6h9nyya4ewJAQufrwmGYyc6ceSHnNo81a +YPIwQMvC8g1fOTXynpJjXRTWYvNAnDCdhiv8v5PezuzmRC3X6xhAomU+iHpoi2LIBCLDC6mT +R46keU48xERJDTDufBm6hsXcjcqDc9QAgyL+F40+2xZr8ImVikQ0SKb432ijWMffZstGkNpJ +JMr9h1dZg5JmXk3g9RGKm4nrmfo2KTYO/64DFnCNLM+2jUB4GN+hwHFUupbnSc7w8YAhhRJS +omRG/XYBo8X6GeUFs3H4ZN0Fll2z/dqYbmXVK+BOJFugjTisxuMf+oBE77z3EOVOumAZB9tE +Nm9qVX05IGUh5/Zg+M6HO8SnQ7k9SiA2kuZ+mZZbYOm5jhzLPnJnmgm3ZqyeL8TWoVga17vu +/dBVHVcupQtEsK+ZCu819mUwbHRgwPyqAaELAA6NIltwk+OYhC45cA4xET9W6rexmXkRZ/q+ +CC4Bd4kCHAQQAQIABgUCUUtl4QAKCRChmZ0ne0n573sJD/9QrhEBMoL0QD3mEptKHZRGkOV2 +cYIcep7SYA3k4KVYyKHEgFFjYHHlpNpuMha8F7PMI6WhHutm6UCF6EPS0anfiue+iWDxtvpO +vdbXQ3jeJ9fKwuAlNQVmf3NZacdcR0Ty4h/MdKdTCTEKQ7gO8x0+kObsjYrz/yuU/SLZUP4h +5j+9utEwbLpWh3/66KsbKkXN+3t5B185uPJF01JZS0gvLNTs1xcak24+G0a6kouP3o8hbCak +n+37QjKPyZC8sTxUSDwxCxzN5JVhccrIzaKNUce4NYvDJg9kbsrHR2ZPAZphoQPiBifxMMlA +rsMRWeayK9ZW01r/R8kwRr1uXmgyu3C796MYkJ6Jb+v8r2GqTB0qOQZ+9n+pL6vc1pNqVEiA +pHu6a5cLof61wDh+NpPGjVnc5ZIZ9qrckl1cz0tOA/5ShjlV6oSw11qtCFjFFjIDqsfERcbW +HtASfmdlrZMwKtblWWUGSZxYebZgnmGp3XPiI1nnDEtbZOsRfv3M0qPFhgyLX69k5O1FVKCU +TGwUWMZQ7DGE4CfekP7F1pu9CV8eaTRdNW432Ie+K781rvntXoVDNxJVE6YRkhVZ+8fEG/r9 +x73Dxn21iwMTw7rcMaSRUKZ41BnFsS3wQDhyEFlJ5zM/dd9E7BUqNyIirf3zbG/cTdPAQY2W +lUWqAemstIkCHAQQAQIABgUCUWmpgQAKCRBqoEDdUNdlQWfhD/0RsyQaq2xtogUZ1OVRF9VD +AYqX3H1uwHnMkJDsNAbebrBEXcSwyam627LeWqFqKZ69ipOGpYa1Ub3X3zWp61TRXqpvZUVl +bgIBgVCDRuirjkeGuxR3xcgYkgOuap8aN8/1j3tmH2yGVvmrJUQWj/dBH5LDXBVw/2xhWyRN +lHqjvh+vqujrX/X/iAVwmhSykgTuA4UM1R6yrMyrMItFQmGgMBr1Ww9ZdQgA6gRqsAdR51sR +q8smFjRl6BQhsqzYZgBNl0vrcVHoCmxSgEDaoHJ8HrGt6dnsG4tqaR8CrDEKIgIcpxMJoxyQ +PEqsecLhtoHHpxqkIXW/XvFP7Ow0nsdfAFXr0hVkrYzvltiI29ksY0n/JPfZbsQnCJ0VBkn4 +wBTDZnmuvRPKYXCOYY8SPO1x8IsBmRpCjeOhicgj+K/zBlEz6MVq63Flg34liJtiRVZ97oAR +/2E0k7f62J6p3b5WCMnf8Gs8atb3Ego8z/9kPQjG1wwmd0lDvQ5zPelk4E8LpcxbH/9A04Nm +tZnIL+e/Aq4bJSpJjy6vQI99R8hWv5jy/4wzRaug5+ueuPzmN1SBLoOJgvFlZoc9Wb9xEnsu +au61JEkDRpurm8f6gkLmjUR8pF6Wbl313g3G8kmZy0kabCMzrhD44z+GhHCPy/Tm4WRwDJwe +LJXOLqsqzC7ZWokCHAQQAQIABgUCUaEEHwAKCRDmtFbK8VRH1UA9D/9NiByQmvHWVC0r3PJo +Jdk92KElfNkVttqSZyTwhT6I05N5F5mOc3883lgZSxjVruaurXVnWg3xfzFrG/uXOAc6RiG4 +i4cFpbqtvQlA+KNZF9EQY1eIOUD4YuLqJQuRFnpzuvcr/jTYw9yVGDlHAXR5UQaDfwqdFSEG +zJ0ZX6Hf0QpHSBXGuXFiVoTBzzrWluXN0MPRp3mYKW9rsxlVkvfmAcnMsrOUqiHzVf+c5PuN +SN13JNNo9KMpXgVWT7c1gqXJc2GRq4rZiR3dKaQg7/h27xDnrmuc9ckQSQoZusT7zwOZLE8f +XQYCpVmrKSrMkpw6XCveY46P1VX1zGtAgIbLg3PGGB/pSM6xDXXUyYtI8C8IyTsHhIKX+Fjp +pulXy7QKDvHhkg9dP6nZpl3KZbRtHaRORbEpCwUVVsVD4+GVMQayHWqS29XIygKURC/BHKbw +L3V+qu8y8PDK7NIG1XVvRxeLoLH0j+pNsMLm7/rIaOwnG3eIARxGmhAHJg+ZdjyQ3Sq+ig1F +guozsciQJR83ph2fpmkw2gv0Z+4Vsa9JvNXv3qmjv+Vmh04/W3Jthea/tx+Coqpw2WCHGtO/ +CTlaqzwmymjx3JL+c4wzitTjIZM9+KYgA98++2fixeQ5Lz1xqUj9eNv+jKJLQq7psghpyr4G +53rfb1O7y/W7EdzUaIkCHAQQAQIABgUCUaEyLgAKCRDPnwj/Arhomk2vEACzUjjHd7B3UGuy +86ppcIXKHdCna+ocF96YXhNlzXYe99TvWPJ79oIBcWl7CqsdiCiW6ZpPF390YS7vf/On34BA +RS+xff/+pMukwP1wyNVLEb7gLrLix87Ut1z/PUd3zhH6jOEP77fooqLu68ZgFVh5f7jZk5eZ +dA6qWnoAnUAPgHlq2D9olrk3btIbwscP+6hqxGV3o5lhL3ZkWnuGcRrADcrzft7K1Tr310xv +tbi9INsow/D40w06waSyl2bPxiPtpfPZ6vo7oCQkALx0TQhuEq7kp7/s95oBXOWIN+NmprnS +eNv3EQrAGGE5Xk5pfdlzq9jqbD9j4R9jHf6lNvte+gRyn4O9kVDP1rAeIW+LzmjOqmXnchPE +QEfcOnQPUiUD+h6FsvZbZAQLejsebMtTTHuFzfxQkBkmx0x0qTqJpzDu+S2BIfI2x1RKq8Lt +4WFqEHL5UM5nU2bvEND/ePfukNHUrimQw3PH+Kz04Dc3te8D4Gxs6scQFxCySZw1hH/fzC2X +VrSu8bW1DcSYOS3jjfzHVWnIBV4IcK2IjNUVQFsQpf3xsX647Vmn1bYSnRQob2ln6Lxary6n +fyGT0dqLMoBIWmqaP4SnSzU3AlokjcU/G6RjwFf3IgqgzLF3cMCNciA8mNrlxqmsyv4XGlgj +2DnGE8QHREObCkezF2hBnIkCHAQQAQIABgUCUdrrgAAKCRADJIQ+Ymt0AK6ND/91EYaqjTIt +1P//UpvFaCwust8t0epEEiiskB7jrSfeIBdBlx1wGbURW4CMFHM3fejJFx0X4wdploO29SJ/ +UnpOaL5+SreIFYENerTDq6tL1xBcZ37SdemRBUwulpGBLxHZeiaspaTejLBF8KMtUJavJpr0 +gG+pKtCbbsAwgemjN1KaB/6U/no/DjFobDYDW/0NxwevavFW+Ne3H2fcpcgBSyNNzLst8niY +n4klt4Fy9W+KBOH1UCmJr9s8EliOWuDcKNdKfRC5evM0aDrcX9DW6ZCrOQvOsG2HroFjBIMP +2L09RM7djC2SKSv4Yd7OLygkL4bu1Nktr6sSgBsdzvVq9K7oQItWU/Vw0JG+yfn0Zl4hR1h/ +9UfFqTowSv2uLbj5l4BE5on+oJnivpwyjIZc8nLbMP4N+rGjUonwTXVyeIKh+YI3MID4skPt +C6C6Rg9OoT4SDNknCRwomd35RNRw3/93rMLqNDC3GAEsBD9tkxbVCeS4nYO3BnFe5DgB10+X +aUKGV1jcr32AWU2jVlsKqvFwa2sCvYuFPbNRAf2VMmsQ7ZuT6p7LEX1WyxY3gAb/3pKN5SeM +Esx37/A0Rznh0nMPXZvnxcxQyFPgzfVBSi9d66knovSSzNXF5gn9M4q1u+klbbN9RRmxUn83 +ethCpVm+2BFFDbL0iYc8M6fOMokCHAQQAQIABgUCUewKZQAKCRBXwTRmKdbsA/91EADDMrPm +5lSFmDxwuU8F92kzr6MpI2M9SeEhs7AGSpCgvZca5DYte7cekdEDMYqJg6CeOcVi5gp3eeq0 +u4PNqU5Azah/L/3pY4r91PsRtV5bap5wkxkTnSwd/HgNvpZ69Y3+d3s8kH/d4C8rhqiZ419+ +23LxhDI3EXBdlBX/TW5OHnqfRlQ/uvnN4RKsz6ypQHgqRq+9nKG0O59lXm0pDrlRPSQkIIIt +fkFTt9c7QfjqvCJCRDy/jHbovlahbHx1axLC59l2dAQrwltIY3M5x0XaFG65ArTXmJJscAqt +dTv6W91OzRVNigDqTsdyW4GuHReKZjhSPfPGq0uzufm3ZWhhGxXMm0ztopTDRh3BoMbRVcnL +p2Ng3YvJYLPnQoCv83RuzJ6Lv9HZ4k1Opb6XlWY/FO5DOOSz4n7/lQyt8UWSY1efMsHI8edR +QeIuiYGqBtJ+XFHQWHKr+L+Eps+4xJy7hfR6bsFszNX/ChQC/5StfltfIFW63usHhc7V6zU3 +Egp7m7Z/wPyCfniqqIfBVutOv+msjJiRtFtlStnNsRNhhwEzb1ZpltArE33oWrlRIv+b7Smk +8RdO661HOnY4ISkNvFkXex6tVQUB2yTlhIARhgWi/9dcb6e9QAiV5fgUduy+It2aTYfBwd3k +O+te6pART2NdVw3IaVBZvf+KTPqUJ4kCHAQQAQIABgUCUfkf1wAKCRDYhUvRE9CGkxpnD/4t +sBCqsEZiRq4oFe52ALrLoUJABoFtm9E67G9N/Xkjz/fLrMVpj84A2lNicy19WJhnE0Qo3z3A +XxGe9MdYwHfC7BupNEI4PA/08IdQ92DN4nzPHnUljJwSlTexqvWlscyL9yMvCPRIXGfcKGrI +Aef6ohVLeCGaFAn8StjLAr2tIqWZbkhPnRenFRW4EQxc2builOlE+RIVJRSpRe5FGmWaTvTA +AYDJmCsbne5i1z43oWlBPuX4LDdljinW7S4M7MA1NQ7QjrT5/zYj3Eq4pr2fiEjkAZFbxA8C +XPyOEGMmL+7M+hSMBK8T9WwESaT4/J+eE34pHz2goqTHM9gGMXv89Qt4s6uHQZPdo+mrqyrQ +IkYrqKBTVgiVsWqKe8AHhFTsTZ2Lz+uoMxj7MR5INhxbhMlk81oIrs//JXlAC+IL+hmieuby +rMB9feLqapCD1UmscJg9niZyAqxSjpjTJJrAFRbPV8q+gaQg8rVKhvKNIIuwAQ10jtgpvx49 +23vrpQLj8PumNMGJuKQ/voFGVvTz+41/0uAi+aWB0cF0q1lYrDCDwvQEHqMeHQW12zIVt/oO +W1smvCyMuUCewBCqfeTUD8CccsrBlyXK9pS51xbdO/hIglpYjvKe5kebKqgXsapkspiC6wlM +xVlfNhdzHyV+zjCs1qa/4VE3VBdrFhv7TokCHAQQAQIABgUCUfqnIAAKCRB33f3JEbe8y0eQ +D/9uPRsBhitYQIFNvQg9EUxMKAesZrK8Dswa8arBrhVzX+4WK+cDwCSDGlq+2dzVd932Kb/4 +3mKGY0lQ7PLwT1xBxtXuQnXD1SeUqNRvEc9CJnlKnbakHhxijfQygWt+L+1LXQtAx0hDsFJn +FWi5LFKwQu96F3dfBZHiWp7OwUdsBooVqczXWPgRjONQK5Q17ovjGAh3VBjyhV+BcmtlzbMZ +v3atdsxi4WeHzfHxMyGZmdabkdCRo/Sm1Bfi62bG9/MX2poUZvStATsMfBd8BHpmlKo58RI1 +1sSC2dwiPCd6oXMq2rRO6x5XzAhpuNzHZpzmjBoiNvLQaG+GXQLu3yQkK03rMFd+8eJSRj3Z +0LxtnNQoF9pgHqyaAGZzgvJzAJ+TV540r+zT48ySLDKZigymfyq9a5eqHE/jnA7CJuD7biSY +j30Ah8HCfQSXM0h+MRcoGl2VfOo3Bp0c2xox98/QkqvfvAzVtazdS3HnSd5PJsjX6otNJ16D +hVZHkokJhEmXfeiteiPv50A9xdiBDFUTtJ9ZP5aAquY6q14JKqyjtSTYtX8U0f1rWwyTc+sK +BytGba/6Zi51661PE1iV3jwsHcRlG0LvtHJPVK6jwU+wFwFEDGQLdWeaOwsbR8teGBLD5w6J +bj0/ClnFOa8X9gGRSUbZ8ZmczL78qEqh0BmRa4kCHAQQAQIABgUCUgX+zwAKCRA8/nqw4w2a +lU7sEACwT4tdtt7t3BcFrYAang1F4yu8P6fz9nB66yDIShR0+/tylFIAuPKNNTJZQW7wZMAb +F3nQJb6Ox05kbmq+owc4R9JEdrCFvFQ5+XYGvBAoe6IhDyW+tSbcm1WLzwq0LOR522efHb1g +iQPY4VFRo7qHFPGMWXd80mBqq4jemNBTMQkap3hgZLwU9Es1cZC4Bh7cLIvQJwpnIJNxz1V7 +p9ykKIvRNsrqh3pCuunDm7l49EKoHq8RCo+XffcljCbClcC3oJzECpIXPe++o47FmaHEVCli +kpWLVbVvK4AL6/AoBJSOxmUemTE1U/1ysBX5kGRYQsIYfmdvBVCH+fh8FMqT0nVtHtCxgkBB +uBWA2uZnYB7wQ0H/GGoLJNEODXZwsqgR/1M7eoahWqJWEuMsBOZZqhHgT82GBiYj9YoXGDui +6MwLsrLTs5/VhzGMETrCIPYv7Kce9JyTKeX/CclbrhTOFdiqYej6XO0+Zq9fvX0k9vw/5jqC +SEMhZong1OCAv36VbmEASGoWd3iV1MTJycyUybfnGpm9R04cN+Y7Q9QTkxGXqEJup117+dh5 +QoaJAt1x5Ea5jZiABpVYBBlGTDwsXtpdxosxnjdAHBypflH8yRyfBbiZ3hmg3LIqalhfknA7 +gM1vE3uSJ925xHU+Q/yd0JSw8C9gZreHCQRCFO6OrYkCHAQQAQIABgUCUg2QAwAKCRDPbMMy +773WwX+mD/0VnQBX9HwCAFq3pPUEewqP5Q5Z8Q6Gh7wz8/KO23B4uYUjq4ObF7UqdFNnctmr +V62SYTO2q+JvMUHv9fwlTC/WaZctmHmDPQHxpPFCSYkwTjGev6FbOCR+7fk88ick/fD+56Bb +9FFH2Df7icsG5SsytWvWdatoh3olDdzX6LxTRiWpPUhSjGuS6SiiJnGcQS/mCi/OeXW00ET8 +ohFlWfcsYn6YzrnYUocnXF6FrzjRCsNJ9gp27//wUnt7YzVa8jFfMaZJUGkCb2KePx6s1FB4 +bMpVLGqRY5Gl5HM4/2w7Um9eudrx7avRaFPjYdIqTXgglEzOze5r3hs2pVpIjV041CbJ3fyJ +tXYetmmQgF/2N4URBQ3m1qwv6VKzouOcCioY5JWlPvF9nnv+y4l55P+w2cAt3ItrPG3lI8DT +2IRRMyzYuT2g+nFBw4//w6SenP7xKfdApbGl4wUVHcwvq/2702TNxmqlV+bQQ6SjW4mXMnTB +YERps+3o5vjxeYyaAlBrE+acmHUlHXl/Cd+VhYX4DHZJIiAuZyjMU6X9PdctlMiUPhE8AStT +OOK9kPcnSiHtpUKw5ZSP6tOEw/5fOE6za3NC29fGgH0uMvFI6fyBE9mQrxNDNcQrjoP9vgQh +glBj9WTHgyEgrTJZO50S+kcoOBox1tLWZmxe/uik/8ZVXIkCHAQQAQIABgUCUhAgfwAKCRD5 +pF59UuMFRWzWD/4vXz9O5M5fVx71OXF+eXwgszGhk12ha3bEwXD8MEgAHtdU0dSbiWPps/LS +K0h9GCYee4FIqcPtGgNYVWvQkxb0sjJLpQudSkllj33K8WlvzNPJpwYmsSnCSjJL5gArR7rO +pkguV+qNMZX+ZLOlFPH8rZDGBLBbRZ0PZS46cMOQlHeQJ+2eivRCcxx09tSUuh9QVo4axC4l +f7j/BJXBsx1o9ugYNQfONdZAgk/uSJEjVTe3igiSWg8BGIcG7F6SFX078mg4KZ31JMImPnKt +wIbpfLISgbZ4F4b5/GniUXI9aOWOL/dQe/3FfIaDi2u8z9v8lQfGuKMSSmkrXaZCr0kfBk3P +dnoYrPmWxM91TM2UCrOzImaWTrLDoIdrYRGsYbQqDG434KIjRucnaqnHpfwe8J7roTCQb2JC +HrnETRTPQp4BmLeXzXVGRVtWdeIc7q3tBTp0ZfiC4HAXnIQJ0MCXGaD0oNejUOPBCSIWbYsC ++ilSbevFNhuLzJ+fmbH9FzoIDdvU9vfSKNJmbJ24tJ5E8R6ljOGBCWSwvZcx908drP5kfwIW +EYeie2v7/kS6PxUGg/dxfzjYa8OSDNVxRLllbJd6VGMnOvQzS/42zKQojfpInTY0nEVfs1CN +cTogYwTfE/oRo9p4InFCDVIGCG0VltywXtvv/6UHzEm4EXDBV4kCHAQQAQIABgUCUhAqPAAK +CRDmeBX59clwr9McD/0b6XY9Y73Ik3HuFCjqBYSTlhkDd4es+azoXTVDQzNkF/FrGu53x34y +9vlPKgGPWNZgxoPPB6JLO9utifoz3QsIBYJ/Az1v3/JpPwPkkWldotkECA6wQ/jd2d7MDp2B +yGlEf8wYgnARu23FPItgINx9oNWz0vXEt2ZqRi76MrkOcQi+ZJcpI5S2uDJQfQvH6ks6lcmO +UZxwWtnR8fnDbbO/qo4rzUjHa06r9vQ7sbbBntdmy1TpjsyLWdgCZIj5y7mE1vMREAhze6X0 +u48cfKOgU7Fo03yuOwHi5rxQ1K+G/yHHM+a4pE1fHwK7Ea18gXrbObJf2BzwqYhGjq8prEOJ +NhMeua9D7RBX9kyQE5170MAUWpuj9Jq4B/dqhKXxRGmQJSVASb6Ep5T/3cmpG5meBGFrXTLG +pkTXciHvdhA3GRt/TuSG3WQ/nSFSuUAQxsIYf2W1cq1sikVq2tSYYf3+rwlpfkY6WZsr0ija +EXjokx9bTWiqH6yoywe0XhYAnkoLmwPLPRFcSjEtGlYCnHsTv7PdvGu5uyrp1ebKfuRWBTRB +Ys0kI8rBfEqe6XC5gaVzqv6X75gG74cd02xTG6E9MdZWU1GX9nF3ZkIIAH151hVocN2n+7Wq +66BRdMMwGdZGp03++XZxR3TdLUk6ixcyi5XiVoMU6gGNqxixzXVbFIkCHAQQAQIABgUCUhPi +pAAKCRA4273IYJJpPuPvEAC0iEZU+jGJx6KOuHdVY8Sf5+27lfVuGLf8KU00bjhflVBm81it +lEsjiEr3RisolIJqmWG4Wnf6gQ/dYnL5SOZOMfMnRkgedzyDbL5/fm1KGLYtLDpo000HSxj6 +7irsa1wGMckUPSzulNAy1wV8VxW5GkuheccDHIcAU+UMnIBfOjd4LSAw3mTGF/ZOM3wDgOhx +ABV9NRxfMvdiPS9HcvesmRfSV3246e/D1ddidwr4FfJmq6jGET/IU9DmiUQae25Jw44Yvk0y +BKVvMOYQoiwpWk61H/GqR3RWNPglgWBA422GkkmMcJtwz7dPGOo/i4cHKoKYV45ntloJmxxQ +92jjPOz3taPiAiHV3SxIawjrRMV7fNdpbhZ7vuEcZ0CvVfpYL0nMRjoRz8zDnjdZ4bKH3xZn +m1EbWlha9IUyKg31DR7bDaIXDPVe5/dNJFuQnYf4TYZhXSLi1CWACYetepzxEYDGrJ1Fvgfq +LnUBWjGeVLeml5n/HSaJ/5GT43TxZcpTwbJXB3YVZg0+U46NBizJ67GJSt3WwzSjiz5CRAxs +NxxrWyC1PCwhx4ugjbLM4cm+sxqHml9LjpvCY3lENhwlx7dzJqkzSSuR3koWKTRpb3haQbDy +TvpksqSIShnxJ/jsDfCXlv1Kt/KijsypQYjhmfVp+7oIyGxqPgO6mnjmCYkCHAQQAQIABgUC +UiBkZAAKCRBxADEoSUgVV1tEEACyccyW0OPFVfpqjxBefC4/8h7c1/Kyzt9Z8/zqwUBzrX26 +C3DQ1rzBPzJVXzXL2JmfUzmed4sfMQwfxUv5pOcpg6abmDnh6KgRGflYWCTOE+ztPyHYsami +TmYcBmo9SulU0Df88HgLrAfRcWUSi2Q9yfaGSaLyODhLzEqqkwxXPoGFfFTgJlpAhkDB2f5N +JUy8aMO4bF17wRFh6DF2eG/IDuuqbCXL3G8c9JB5+1vMZB5BvM5ZISZPf3A3YEzefi+CMLv1 +1dPf9tjU11t8l03NljOvOthXzLu3yHlvCeR19k3+fqJtRlDz3Oc+ZfSRrUl6zPIuBkMyhYBo +AlKYFearsM5WijPiIC2uu3jgceFk94JP2ipi07zFDRKFDCw7Gb9BU2WxgYde2/2QFs7ER3Uh +BXm63wjUSYgk/+wjHV85w42jaOrmQn/iZaU7LY0/Puetj+CXp2Fz1M4WrYl+zo3dTEtIaikl +67fpdvrBvg2A0F+nWTMZwjfUYAcuUdaodmVC8lnJ0qdy35C9g/s42cMr7GkvQ6XNXa6Y1brD +EeKcl9v8yacir7htTCSc82VrkYLfvEbvSzhjPqrd3z+OI/XETOEdYY0EqjvyRi9/4YJCRcg1 +NXl2SCdJS1FNVTDEPlqCl0UQNJir6gAiBmwTyoQhF0YW2xkNiqxDfuKv5tXCNIkCHAQQAQIA +BgUCUiCiWwAKCRCi3FajfHGK7ubcD/4z3sRaqxSzBjWNSNJZeK46q520CGT8X86EvOl6DHyK +ed/PCOW7i6ugneWXb+tLii1MdwKvJt4SR49ENdvSG7yf/sG3vrdXpk+9vnfvSjCZA6Y6o6kF +sp+pTKaGicGIdajHGckTK6gGhgYmeC7MHI8tpFFPN5xyIleL88K+b4ifhH0NZy646O5NAUiL +IROZqHOjj0hAJvFwoTVfY6eQVu9EnZG0aj9aGw6Hi9/6wim2qxWO56wVnOx8PqG6gZPPDFND +92fRXMmZB2ptwpc6mn/zs2WaUZkEzmC5LrKUChf/ttJ6HDLkqYMUzNI2zzkkJn/3EzE8IJXg +SRQsl+G69+XY3KR8qwCPNhCIjhhdWn39kyvD9IXsf32Hut/5vKkcmqmhq5xhmFNdpHgD42Ur +CxQMj1GUVYQYUNpgWGSJGlJVkVIjYyKW1Nzb60SlXqa3Etl6kzBSTCz7xlN7WE7BWNuNXfSr +sHz0LIqoid+ykrKqxsTNQBxXc7MXEBLWcJ1xGUG6Pr5gyuO4o6rw9vvMNxsNG00BlL4x/tDO +D17hFKFEHX8+1mG5ITuUt3WCUIpGbNa1xiuDL9xqts+VFFhKbw1cxtSl2MZCtNpmgb/3eomJ +bRyGCusIr1h9DIR0BCVlItXeLUGgmcs3QWKT2mxNCIzDu5gsiJdpqaQkyeWsaZ6EoIkCHAQQ +AQIABgUCUifNWQAKCRA7lRdqJTy03m7zD/9LtStDfuewe+D70o7KFaHxUspBesgM1sWkNjGb +KytsdONiJYijb8VhPOIYaGNw7crusL5F6nag4TXbekxr7hFYLYHOEMj4QWAa+JgNfSMHd1sH +hPBRgl3mOPJdtEXOqGEkovFXLcDMivzXP2mLADCFehFbE1JEs31Vag6NkFTj6Xlsj9TLW4Cx +CLKY1Ju7do7to/Hkd1IUjQMqJDRN6zl1wLybT9SgsJNlyxqB5mcNwPVQkcA2AXZHahJjBTZj +DTuri+7uWhx44TlsoxMwpSz69aobOHGcrGSQBNNuMpsCfz3TYNWdE13zxjfhLRB5BtX9y14x +EFThEo8VWrnD50JkhO6b0eljfYe04ZF+gOA2RP38d07K73KAUGBzpUwOtKg+ThMBQ57EGD+A +qh+IH5bfMabHLoRolhHBvYdm1fdt6ys3S5w0/hDdnhGPPMRdxJwjbcG3R4DXzkhQz40K5yma +NWcEhRBQggykVZdcoOPyg/cQeaXO0nFBcV6vxRcFU8NT5NxFADw6ClJ0PFscLvVWST2k2f+7 +qR0rBFn1Q2I+mKvpEQjw+LxxxuMhAWW0xLweRiaGUQVo6QYGY2W+P9APbzcFF+1xIzqW1sB6 +0r4CnYB/oKgs3OB1i8OxOcd2V8NsiMTv8fvh7GNoZUN4FBOCCBp8nnCME4FoGMrD3r88TIkC +HAQQAQIABgUCUifRqwAKCRBwW8lMsAxJDFplD/9ZLWpV5Nx6I9jmt0syABaXCGseGoEN/uIF +1jQIcr4CzrExR4b4ebmz3aMcPw71H8ywKCSBx9YT5bY1ayKh78LIh7yC+IdcBcXgJc+cYUkd +qvYjo4j7ihbWWQax46K0wJl2A5nwInaCNYHqW8OXt8HzQkyfNydDeB+PnIrO3XZX8Wkssao0 +9EfR4gD12bCD9/kJuwDqmoiKJd7jxUA0EcdmC3gLL1tzaadhljFU73pwZqBO1Ph/M6c+Y9pl +q37ETyPLzr1MFPKhYi1xvT3R9ecbE/lHWr4YI99RgPlvvuvFOgbKM+Jj377mg0GxC1VB/RGD +XMHihiT+uL3j7LL+gN70PEw0gAqHNNJpM5S+nxMmPq7Y91pYUCBHc5AZJhJtpDOqC3Kj/taA +OpHZoSHQYDxornanXXu1NHsTcrdLYimEm0cREJQ/nFu6b6Euy2KGW2T2yUlLJKH3NJQ+J6M4 +mWK9a5JB8BhsJyaR+teYp+01/lT06/P/oJLr0+FPnoVpfeLro/SxBIb3JpURpeSm68e0qiVc +Q/ZiGNbBa8Hq9/tfsQI16Ja2FpixM9sJQpysKs63D6GH5YqN48f+QB/DZG5s2mRsxZ2f3Znl +aYUJz0FSPVYteZXtFo5fNHqXfnXu5F1NZ2J+Ebbkt+A+zx8opUEkoD1ettehcSKtqZydCEB3 +94kCHAQQAQIABgUCUi3DtgAKCRD3L+qAyjDJzQsfEACw63aHXyA+ao9e0nnp+9tGVg0Q2t4G +C1AiksoqyUExgjaoBdgvKwAdITgajA1a1LUTHFiC9uK5rT/s+c31F37YCa7wt8ShRyncsSDg +FDtdBRklIxcKJMSunHa4Z6P7zh+2H4w4iRWeXJl3MG2KAgBaWwvHuX8AV9VC52Axzx2PBxTs +J8yA1ClEsqKx9hNg3GswH5Q6v6sdfidQbu3jn6IDY+9vF+gOYZRKLF9OFCKme5Lx/ACRK0oU +esGUOuWPhGoeWGjawDt/Fub83QfavCDJ1H3GhIOyJULZO5R5hg7pj/HlcU6fDfAgauTwPYzk +rOD86LoS/vJsFzvKhsb43YN8Zt6TURWk4Caiaq2K0330eDkRZbHPuDMCpyVQIITehAd/tk1r +wgNX0ZPVjRtQoxaTa2GUOo21oVi8abVFNMCwoCV/qptz5kCt1dM3+wUHO8YyWuGnKs81fpV3 +yaFDAARbY0yeSsRSAHL0qaBAe6rtG71eI4fN7u/vbxR1SF9fBWAzAmeacrPDAJ1ePhgQpA8U +RxHBo0dBuayMlEIugrrypu871c+ffZ01blibE72eKyPw3FKmjOMst3IMX8b/E6f4A+CkS04O +uGcLXdJbcFcsLNu//ZxjpPTuM9o1J/4QusB5CxHmg+DR+U4A0KiY/LM9dJ6Y4WbAebA0FSU2 +FdOscIkCHAQQAQgABgUCQ9o+qwAKCRDt2ZWk5pdwHEaUD/9cqNmJHapQS/9wrOUx7MhOflSU +Q3h6OWFPzJuRX+iuWxBRRBRFbPlusInlPcMrP0KPHW/ybFHdnNCbBC/2LX/qNt8EmsUq8nfo +2iQpzm9AfKkjKlIQzXxerRmL2SXrOi2duVFnMChH1FTJAWc3mTFQxaxehtGelM+9C24jbXnM +yTF+jTnbjTEPX2Edj9zhxLuS7hO9tf+1P17mBtb5tlqah9aDV3p6e0N6byvMZL7YqE4bOxzb +xnCTaXriIMnkSjOcQRrN9KEgVUy54nB6HPTFqJIoS6YHyo7EilVBJ1Iv3GkrRIghDNtksr3p +3drpkN+j2Ux7pyPUFFNWeS2CakW0NFFsPn2J7lJR/WOt0BVlv6FgV57/RgA/Bhaxi+R/66s3 +QfTNZWbI8Q1tr0TP3t8U/r46hCVcukR1f93B7rpyrQcowHnfZ9PA/Zu8ZxNnhWlJwwgykY2i +Jm6x7xZtQo4InLX0o5Nu+YXzaudlhWhO6UDBm4G+eH0vIYhx3ue3eeMRkhbDrPCu/l6m2+Sz +3zVEKFdn2dKbRNhuldwa5JGb6K3Fh+1pvjTNND30tmHm7gNft6dq1M9zeYlA+DLKJQ2vREJc +fVfd2vUFei/L2T9MPlETon5x5/1269C+Ecig/Ruz4mNjF8wME9vNFfNULSPsSBI/22AbCZEY +c1H67OHQ6okCHAQQAQgABgUCSg4eNgAKCRDBVsqV8AjDOrhDD/kB3XazIorV6kxvbID7QIUx +YEREiqya5FAAtiMp05fLJjnsyJcVH9Tex7PuXc0NsuvA39vAUpYUvxR43MKxZZTvsR3vLOrz +AW5uCuk9sjXBYItiSqmlz1D1Syzu+unH/I3Ke/O4zncSoUmd/yGOxfb57EsPCD43aXg38fjP +ZLq/kGQOPs2R8tOc28sII5AffBYGOzTsnzCg35ffqcWRAovGnrbI65XeiI5CRJQ/OtP3S/0z +MP39uQMCDsaK/ZdsQDaOO6iX/IAGWsEgGrJ0GIBbKqfiz3Irk4LHnZOikuCtxxi+E6WKrXtw +HSjAKBJLjvjeKbD/1RzNkzhrRgfSlDbsPJ0r/y+8vab3B1GzJ7C5g83MFAvXiRMrT84GbZF1 +vXSc+DwYmEMXGG3HePLRXpqI9/vo7eVKiw5vn95StuoQlceCp7A/zfn78/GHBrd65OzYVmPi +HrFdB3YbwyEUrh+WXYI9YaFIXc7mVXxoNlLuErMlai0kmUK7B8lSezvJodUvaXhP7oKeTyou +i2CH9yQ3uTe2ZDHx0hmRBkCvpFuFBS8Enbu8Fn7n/QwbC3PIiO6BUVDJdxM5WpET5ma6phfo +1f64ZBXjen6NZCkTZ2MS51yTJsgqp8U4I5AgDHtD/s2rwwo4bTwAUn87B3rE4TZSQV4k1NLa +JLDsKaM4Emsg04kCHAQQAQgABgUCS4lpvAAKCRDthSARn+lZzKFREACw3tGMYDc/LrkDRyuG +ifVv815WBPeV8TdQ/jDKSxYw4pSN9n1sCOnGwDTbFjRstLkyULnbZ/rWBy56RewLtNjavXJh +OIMtuuchggo++/kh5Tcuyj1nZ7GpaLQgtBS7y2IAZQRxFxHLzLNQLW0JKPM8EzJ0daqI3bh9 +tfAXS+3LPFYFDqADTEakGO4DjXEz82k4hmP5mQTzK9B74hww2zcR+QZs9hU24O4zi3q8cVc0 +sWbdr8ijzrDZVYvGGCOmrYUgja9/wGHBRKT2xH4MnFZvE31FIeYz6U2/JruppN7Iqp/217SL +/82De+LeHAYfsZKIcG5wLRLY73Sb98sP1Wq/cERuDi3H8THsLFiqsq0Ti/6gQCEy7lj6sA6v +Q0wjrKMxUDA2BrxQb/jT3UgtiY0xRHzB5ad3S60AxkGzLzAHdn0biCE4C/AxGCygKcVY/Ne6 +Co0jFNxIc1EuHnC5YSaUcYcfyDyDlEzSyR3GAB0+N7Ea6G+SxdNOMq/u3/lDC9Y3Z/qWg7A0 +kGhG91vUXhDjhtXG0VDe5y+yVWLHr/ymfg4jYXJSJg/F3PCl8JJgiudSiOEUnKgP6/6HyQ6b +xvGISr1dK7f/ZSK+GwlJkaWsp4/XNXLe4w+KDmUuCCVPSBen9zYfCFQMCqzBgCWzQ8I923c9 +JfNCQEZNF/BWmYLbPIkCHAQQAQgABgUCUPEFawAKCRBSPdXTTo5I2a4bD/9c37vhdK50jonO +UZ473LRF7qeC6zJhQ4s05wxKxL/OkAnP2kewHqo2WUchJmwyfcTg1oVFl1d5mPiw4Qzw0RDI +ZwhuHI/EOLyHXx9oFMqMwypBds+7acSuNXr0QAfIcfLd17l1cm9FvZj+/4ohGNna67m3ZPQO +JRb6a/8qOBg6wD6JI+do0mAcS35+jEMjJffzrWqUTj2eczXCY+OO4LSGXQ/n6ppvzbm5Ip5v +GXCLNBZDD6f+Hgq/bjtOhbmz9MhJGsOUqDQIJrP3sTtmEMs+QeXMGCJiJkSAS2LEVhZ6ExuN +k8Prv/FPEIxguOHT7nuRXNjxz8M7k/yfbghmG+EIr/AduunJClYBXFMPnp4P2a/mHUV6l3EG +ErUL/R2XhJJ+DnUwBQV9uxdPHcpe3qbpYPekKQUg2J+Ra/BoHLEtunDtTuBPBks+Xof8sOMi +fiLgFdPoOZcLkcI10G8RKWeNirN6JlmdtScOrBpROIbffMTTweUYnZlzw5uJgDg251AqPnYD +3DFo1vhVJxI+/XrrbGA2orobGxKGidD3FJTaND5eqLIIP2xPJZqQEqaUkmnqRUUck00cX4Vx +qnCrruJ6yxqlYOgCoq/sxHT0PCXq19HeC6kUgoCyHP6HLSDirZZsFsFw0V5Y3ekrBh6aOQ5l +ac/aTgpLMzc4ohjxJ3crTYkCHAQQAQoABgUCShGSaQAKCRB0h6SV1bzzY/35EACjOIka8+pN +jZaqy89+S6hnwoOhNQElO+GgoS76ZKIp81mmMWUxLs4lZX7KFRB4mneQmovuj5VUK/UGOPGJ +lpSMnB2BHI/qkSAZwYRuBLd6LzfqoMQSD2JhhkAFrUtUQI9obH7cw3gibFRoyDHSUx3HXwKG +EiEXQEPtvu57asHJ/vbp8uc9qnM6Q6SzPHmlBQ5HDGT59kNXGo5w0IUrNg9LlmeTuqBonqBq +30lmcsHsXT5M3ThRhcG4oiS7tZH+dy8/sS3sfSuwGcnh297yRBP1UzkbabWtmqJs41uDRXwC +qIOceGo+XoDbZIb/3d39x3YZAeDMtPFlmlmuaVZRumDzNwlicAH6lj7KUTWMLRkHqdyQ/HDP +RktQhAUjaodL2eSqXPY0HupDrGCDfIHGIzj4eo3LZNZ1kSoFkwW+04x8T2Pfk03jLOxIYjG1 +BEQ2rcfyDEouvLR5Jw6SXjYVq/Kg82dR7lJM9UC/4B9JrC96m7DV5w7CRT5FfhWq/U87RutR +RHn03P/OG2QUCvFqSHVad+WqJLaCta8MEl9ZO80Zb2XzkwfryFZslYvL0a7YNM2J/+fvrtei +My3Q/+euu58FbgG1FFMZ6m0BQ7ucRUFuov1co8saZMfXsUC+YSh2C2pTIVavH/+wh7QJ3qSW +t5Rf5yfePdvlpLk37GnEnAIBG4kCHAQQAQoABgUCTg9x2AAKCRAmkChboNlrLNTGEACqn+fz +XAkQwGDtaUAYyb4aHY82AyUk0d66L5ybOjac706DNttm3jEZAHcVjR6bR0KZABV2mYfKJ4bU +RjhQ0c2OrTCQ0Ska030GV8vYi4buflIYIHuzHTrB1CuGCrmLL97Z7HO6tUSpFyl0kHB+BXTb +ZeYQ6ylA/bYy/y0EKZAErwxH8GExOHIKaso9o69k8PVIFXG1qKiRvZwzNKMXtlSp3IN8nlVN +MeQiIK0ArCusSNv7wWV7m1n65XHomRcirupnGuBnocsRufxopjqkNSyye4xkzOmxxN3pYgQm +P8RhvFM5UcCsLHs+pQAw/P+HxK06GIF61bneOvhG0ERvPfB1JI5aek5MMBeZ9Z4qUx50QStN +rGcb8AqTOvri8j1pg2QVgrwWNrYsrGwpprB68lDeaxqGMWOgOaaj8bz9Z/tkt7oCNkdo10Gk +OcTYW/sHSe/XSUMd5E+64HJ3yEirwYalSmdxuZP203y8u4qkmp9OvzN192M+Sd1ZdFiyqzjN +5KTYNq2lvu+zo6f81hBCQ5KzgjLtUzGalYv3DaGy2VDrP0SGBB+GLsxFSyCtk5HwwFOno3Xy +dPO2IzSszQilNQoCHTE++iouvVCgnNsdPm53ofCSw71yAFHdGfhXMqd5iZ8HLp8K0NMBBndd +lmqsvMyulSrIgY9xSi30pf0eq/jz6okCHAQQAQoABgUCTysDywAKCRC//I3TwG3WsERBD/9K +SUhedkObgkkM3VMvdFCHEFrHPmeQmFPwGpOPs3t3Tlmb42khCPKlw1SVcIHBO6QS8cxaERpU +hyIaS4NjFNohKq8kCklg5fLVwq63S+UUkOYyVw4xIkGtSaaVA2nAo2TrhvXH2OLTGz9515k3 +Id5ZAYS8gMQEeFjzQ1yqHBDKjklV/HPsQ2SAwACVX/bsRE45K/m/kPnzAftLbPUmGNLSMbtE +DVnc8sBNgjnfBpOeJrmF9nVHN/BDQgnDxXwbrfiYECHvCNYUoWgp/8rAmFV6s3zSjoHQkm6Q +cg7q0TsBzKvRYz76kj3TMd3iFxfR7FbfQuXEIZ2aRp0qEABGO4mTZ9hDufXA7m2DGY91YtMy +mxnM6FLzBnYS6VuWfmkNneW47ikSoT/y/nFAEEzWme7Zk6xx2Y4JYkNcjzzs4Ozky2LeJ+D6 +YB0Hi+zZkWhJ+tnpzg8ln7wZsddHBqIHbyL5AXuynTDqSkq4TtkwhXAUblmrDD/RwJgxqO2V +ghfgrCkdqBBS82nCyuetP1xLJqqTtqPSVqWHf1pgJPz4GVoGYokQGWP0Mgut4Mp0Wv6FVSnn +HU2JHRfy/9fhq39hYy2wg10ok2r3sHRf7VH3dU+SP9uI73G8VApB/Cg511SkONeOZoR295Ke +yaKzWqEeI66ZcWN3Vf7BJ6XQxkq3MWWqF4kCHAQQAQoABgUCUDeq7wAKCRB7H9SitKLBWBI6 +EACp7xQzJQOWA5e/JFQpd+CPIvzPrycuwK3E8Om7RgkIPrW5Zwq6oDNrhNGQTulQpGGghH9a +mM0ehqlnX1OeH9psoljEbNiaOawbhqbN3kOIVi4mAguzrJwc89j4dcvztDwo5PDky2DZFAE5 +zbU8mXRbZIbBSUPTgnmuOeKfVZfsp/H0xyK8dQKf5MnHqASbyBLbUDNwHUMnKiEALn3cpoy/ +EFyL7ekcBeiosDcxPic6laGcus3lFgaWCeArM+vZh3A3Ogz4qMltyQqG+etS8uL/uYd5+KfC +gtSDvtOH5jQ8WhNmOfwloLKbcYAaX0S0aG2TgNwYp1NzyNwNrbGgdpevL1v9nunEvDkv8cMa +REPkd/doJP+k9bu4J8WRQMJ4H8wWf44gOMBmACWD2J+ZdGGvFoiAnxHJTJr/RFbmPZhznyZ1 +Ok+hcaMxShQw9Cx1reZ8VNg4V7dBfaVOF0rXwtY3G18KT+CU1kjPb04W4v5bWURlI3LDUvmb +7uywtBI0aX5kcjUJigQcXDzSYCt9CpjSvc0VBGFG0BcaVNLIVSW4jwOpVgmRyP/bHrMaxeWO ++SillTNrr4A+Hi5UQrWRSdtSE17G9/3n4DmS+uRG6O2SmnZHFXhHmOm67Xwl1pFf2showZH0 +OpcqGam08kASAGUxl7IOmhOlXLGU2+cSe/QitYkCHAQQAQoABgUCUaocYwAKCRBWhCfpSAGW +pHFZD/kBQZiUUZiPR73lncl/0l/EAGNr+M028hWkTsnopGti6rz8C192zB4NgGtjKwObXKvM +t3ekoswPEdfk53PexjjJox5BWfZ3lTkpFR3Je4Gei5ZR2E2rF/IVu6SV1AQrfd5TAZkfuk64 +SnlnkSktDVnXVaqLZPnRcvZ1SMBpCTEQvXnskkI9U2Cd2xuL+IetSn2yE9gGiCGo/0MXdlwV +nXhynVU8SBUROMsKtAfTZmdZI+4n7cb0sQWdmNiexLyfbFvxzlEUQiyBb6Dj05hephXgvKwE +zKDM0Z5/4z8j1k3SZ6J5PlWg2bxov2h5ewOwGdGljc2cw8R0WT7FRMiisLd6RaCWlxOSkAuj +2OVrz/xxM9k27cCVDFR8GD5T9oblBTWGcUx00faIsCgmfpYC6dILvkFiztu+Iye+g5RxvksT +4C+QtsqBpKqdBqKV1df/HQ3rhhHFB9G5aO40N1UsjPRUz/k0AUjL/5qtWyEjs9GfhF9SALKa +Dj+sFSwT+f7gXgCrzvyBM2cqyg7LXmqHa4iVdkB3Qz5uL0hau084EhSSB5GusHgJgvA3s/0j ++X7CwZqQLqeNozHf7xcINn8/s8gy203ASNFGAm5DJE9zal1UDeIqJfXfjRqzGp1uM6dTfGIT +Nl/GZyCQF4s5lN6eqkchIHEHAREc9p2OatCV+Y8N0IkCHAQQAQoABgUCUccbKQAKCRBo16lL +TIWsgaQVD/0XAZ5aH0Li12Uxb2kLnWVhp1XsvnF6am7sqRsRuuwvozqPYXe6lS3vD62EH4Lx +a/RKD3S72N47IC3s7vsbw0AvYoy1BIhOUN1ts+opF9+JXq+tgdca+KpF6yVH1ue3tBVhZr7h +HDdQ9zfWVkDlqT/Hyd/AFlF36Gl3UhNR9klpMz16Ft88fKQ2dHEXWqLl67JoEWv8CH591xlC +BpvdML5YCwtxCoKj9br0J4GpJR3wd5+4qKO7Sm3utxpgXRjeFIJqmxavJlTun7VKbaQZmsCQ +rEImNygrOwkn1oTPBn5Tfl+n9ninGPWhtB+farfj/k+jthvzrELPq+Ywm7LzB+5Iq+oZrz20 +oDGyHGMIZapx1LuoPSpNTXljHLnvxVH2jxmFnafPCxdhtO9jTYdBhxWt3vKAlRh52w9IfFir +SaRiMgkjcxJ5IKotB+d/vpW6eelr3GHROfVRv+H3UWtckZH5j8w7c6T7b8nnQWPPh+egf9WI +7eapx2mxJrgLBS4Wewow/N/HiQcRiki/0HpAAY39vWLUkGlChiikQ/wmz47UIB9pppmu1Alz +RfWhsPD1dhaiJDxeYCpzLjJOq+LUsXUxWuhTNWSHba7Mpt9c6hWNk3CoLmltShIOviU5TypT +fEMXFw/E8+0u8nE77hdTCix8GwEzjVGzSgQZTEMb+An53okCHAQRAQIABgUCQ5h7aQAKCRDb +wbccbjHPwhA2EACrPp1Y1n6SfZY0LbUtLGvPv2a+856sslUaqX372o7m2hYTvv3v8i6aom5s +JdN8XA9PRCaRY24JaW5QrhpBGHIPWk4rivuldZRVAWrzCkAEEhZefDiB/+y6RbVizJGXa4Of +WJhdQZ3t9U6WmTMzRv0oozdGGUNFYb3WebQNZjB+NrxDeY2lM9LG4tApUKEfUzu4ews1XeQU +p6mwLklyLYSPTiGnl9Yu5OS7JWJuoEN8WEVzBNy3PyB2v4C0DdAn/rU5HzwSzEUIxQ2WXiMU +EUBrBnt/0HrUg/KffRbJZJ2/0udtxUlLhASZYcvb5VNkE5qBAWVAJ1w+/XfykJ0IoZYQFs4e +dMh7m7FHwe0awDfVO1RXodqSzrc+/U5O+BGLkxrR4QqMT2YUK1mSJ/Qgc+wEHdQsYShadEks +eVcCcoztCjwqFGG+Ba3rrGxOJ4krgrwGxFcsBKATjSOg9Esw6rufQPJ44Kz4SQ4o5JPD/k3D +6vCXzh3ng51T8O/io3Js08EY2W40b9MWy0XQB155TU5fnbklkQ6+4K8/491CkOPIul8zlXg2 +8VTwV3LYgJvtvhRaNfgCCjm8PGS//+IInypeSKy0lbfGrMQlHRO/+m/B7VhzEX261GSHlZn7 +8b1IKHvzgeWOPprDvPKxbebWfJwsfzzECyVOA3KQGVXmhiEehokCHAQRAQIABgUCS+WWVAAK +CRAgnCoVrJrIQRS+EACYcREloDO7uDdSQN9hTChL0EVdCV+6id5rEopqMvximQbVwBhP28Yo +ZmTRVJ2ixJuEd5qkpJ6d7BVEVelppdW+0tIl8osheYULjzzY/5VPKnzXjXqwvsTIsVWnGu4C +FqNaI7VZyNEcz7A/fP5oFu6CvOc965zjwgn/9gIQNHbJpCh5ysprsOIKp2GnwvvI7UcwWUb3 +JKjC6Q3N1ighxDMC3gZ/s7YKW78aq9x5l9S4TmHb3WuRtM7hhF+YAttbtBp8HoIxZBzrhdP3 +aeUYg60WOsvGDJKENLHzWM9YinlWtbjGZG3Z8jK5spQgdQpWZjBhmcwv8KgQ5aA50w2DBU9D +11YtNvVh0FdUh1/8sPP6zmCGLdKFWA43O+SGIxHUT+UW53XtcEB93L3vWJ3TQ25N1RBhv0VE +8YSVzCv8lpaCO6hUSNkFPtiZasuhLNw/VsijI9GU68cHn5iHjQWGnrraSBQgwBHVB1rmGHn5 +66XDpWMpsqWZsxvYDCKR4yfGGBvRu/6IrtegooUwI1t0Oi0cHmLB+OHSKChB1dqLr0URzVWE +T+BwDCM2qD+KbCMOT66UZ7XMpZn5ZauhtAA0FSMQkmrauSqHymnMCAnCewvBFSudwxyNDhol +0YjXok0CghqHjOdVW2sdS9DQcUU+IlfDeCMuNvaaO2611Ws1ZnRHJYkCHAQRAQIABgUCUIWt +FwAKCRAijswSwI+RwbmdD/9a9s2EbOkP5UIqIXYAq0KjEsK1yTlCh8ElduaTe9cf9lsyLuz4 +Dd/GoEs/N/W5Cb1jP09dsiYemBQ4ladOoIWgIMHdhhZXHZ2Ol8WcpY9MFFwQQsovR48fMlXd +4rrzA/XILcUmr0z4sQfYNGlF1o1aYVDz/VC49ecL70bI5zcUt5m6aEvSksUO4wsrFazEGSf1 +ADBVn8ptCydmOSfgbGuZ6lhDSk1ow2vhi8Mk0qC+ZvlHnDgt5m97Ux/x21R0NEOQ/iSxzEXF +CTxIXbomOGU9wVkFtG/On0Q69I41V7hN74POJY+TVH73LRpccmraK10zIUpjr/r/k/YYG/o/ +s3dFaBqlMIbTuJPBoklkf57M8dKfMa5RNBOSe1zVTKYz94r55UeSkNlHHcgxC2z22DP7kapd +dU2hT+JlOpvV90FEGWo+OHvJnnr93pxV2zd9lFyLPyKK61MSuhZlEgIsO56BNvMfDTtGexGx +I5xDeY42dgvsARDN7uhR6rsWLAVvkweGZgisuj5iwfDrEM0eOSx9VCNal1gWGCbCvbKqYWRG +MxSs6/1XF0FLcWUecSILzT/+jwP60ARs8gcq74WxYMG/zwM4jXgnMMfNMRmu12LHEpIdueVs +LOe33Axh7Y0BP0Z3IiGaDVrYB1gozsNK3K/xNw/6AJORoPuftArZtq8BTIkCHAQSAQIABgUC +RT3bKAAKCRA/d1Fg7kG4LdHJD/9p8wcYvnc+ci7d274DEQOE7nBpl5vhOsZ0EeKd9Kp6m4Hm +tIuLNI75+xlk2WfOvMTj2ATdHUjJewE/xrKZfBVnFIxa/t7EtPG8OqXsh4yjl7LjgGmLLYWi +UU3FB+cD80GPu72+QWLL88sXUg/T6Ys99DCiUnYbNNtbjsFd/97cpQLioDRQ5IhDDLYfvlP6 +Bzl8TvYyGKdk9dZGoVUjQbRu/QSYE0KtbqNcCJUsAkOFVyrbgKWHzbwQasyUp68JnEWB9gNY +/2oOpydlcNGo3WBwVB5BsUw2FiA4IHVIVoqYKZd8carJ1Nv/+fnQsJaii6UtPKhtqreCIiXY +UnpVyjwgfeLnLpuaw6JnkSuMj4AfXrZhxBQajiL5OL4txMVz/j2AcyDSsuCRAJswZ2l3hok2 +IVnfOV4pD+l+tjhTYVpNBT90MCZ2U1eobaXJjMWOSc9SvynvDcqSotC7michvdbWQ9ejoe8N +6gAeoArfK/Qe3Er1k2pWGh3/cPNk+/Q+lIITY2OV9FnKQ2f1OOYHXTj+k4rDi+ojvK5VOmPD +4nWcPEYzjmzhB2V6T0GQ95f6Z11OUGmBbwxJmUB1Zn3+xHPxDsaYJ66uvxPPANePXiGV5TAa +k6ER6Tdxo7vny+h6MvjrL3MdpEgOSJCdge9Oqm+mdRYWNJQkvI4TZ0AluGARPYkCHAQSAQIA +BgUCR3OzlQAKCRC/xlKO9c4h6BqmEACWJqt9522zHVuo1ew7c7kbE3Bqr8UXG4DwvHpxRVC7 +F4I+CL3gpeno8L6HOEcGCKrAyGCy/YWktaB9sZgO/+C2y7VekDFhWD295I08SlTBLgkMTjK5 +U9li8gzqP3w+8F3GRLt1aH7rAiFYL1Fljt/lWQatJJr1HqGQfjd/mSbicRVVvDwgPtT9pLG2 +o7LkLKuaa3MZSWrckByJjVBAzqJ8ZkvZ7ccCOQI1FIEqUsdtNj8ndLA9Mf3nVi7Bv3zecmwR +CUoVEdiSxwg0/2jJky/skp7T+F4Iy6hrQj2zWgNGXDaMRtP4ftrqc41htU0RRJEzNK38ckrI +Fb4zzoLXrHyqlcO/4FEQ7RXw23G06igYHssBYPVyS0gyLlHKm0HyRW4U6KlL8+RsOckPfobo +DH+4ArSfz47URTFrxKp0Wy05h5KHXTCxTzGv+VFQD5VabuwPgOm3Tayi0jCqCwJxMbAmCtnY +UFve22cczwfUNY9HVTJlSYyRtJRJctBT7gsx+uU4PZPla49ivRUGIBdfnUMEF27Tc2l0HP4y +tF6l3tu8qdId9qn3bb9VxdzXSynpmiRIzhnHOyeTtGR+LlcAYOEeYpUeiWbAmh0nhxLgtzhB +XzxWfuoFmZ56DTIb7JPaC61oKmqCg/133Zzkyas3yspmO1v4kJ7ekXTKQGqOGGbRaIkCHAQS +AQIABgUCS1fV+wAKCRCZkx4l2R4BLIEpEACG/O1XpPVz7Xd0/x6VirTvyk3Iu/H8Fn7SaAWy +caULmktfz9vuzTwbGAhIHOdtkmxaYP7OshesrxZTBtgFtMl080ep8bdsWZHQKjYjlZq4ANpT +irwRHll42NGQAIhP7D90VqrrphlrMtO1CacbQ0zLCHZAYXP5J6XfSuOdGCF8/lt0CgTcEAHp +qfNLS8EWmZKDkGcrv6VMkASELuPicMdTdrgd3y2fNrqsCkJZahaQFabTOdPKw9KGXwuRacdr +hURtA1t3aGYEt99vNGC0C8eSPM3fqYoGa9qYO+lRMhJgLEU97naWhitZSDQEo7JfRjfmlyN+ +HwkGXdeXFRszoAYJNU23+7VVWz9EIYYlHWkyld7j7hM2of7+RaLVaPlQrRN++qL6Py4kh2Qj +oCSFi9tDiVxxU2vNP92qCnXPfILvfyn9AVeBLSIqd2sKm2WFNjjXjybkf4oaX0yRijQpl9ci +A7BFrZZoXV5i0+4iyyl4rx7FSJ8StvA7JyxKnKii3JvN8iVLZuCpXFoVmI13LNArqORcSnYI +VL8447XhHtM4CCJYaNzvO1rqXUxVTsQpTcTt84JsQksbst3IdTEyuztMw+gcebiyNIt+6BDr +enA9xOl7/jr16taM1WDnl4yNhJE4q/OfLpxaNvEP0pDwslbUuX+13uZaJxY2oF4GaiF3UokC +HAQSAQIABgUCS82o3QAKCRBuZbWTYFchyonpD/4+BND21t1ZYWKyB90bW91xxIT8/YxiF/8S +UaJmwvvlo58rA75aN2QevlUqtq8xh0DUkIynofqBdglAMNp4B+IhOWUN67JSPXVLGVmZepBx +Z/o4KMYVWe0mNScvHC/uVBEol7m3K83Jc72gw2ohazvO34Nesu9AL/sTSbkBfCrUkQN00315 +Tv0lHy16EjViMQSq4FzpGjCDECYqOzFoVqGIPRLcYNe4T6wjmtw/1PIZhGFGj1c03KUzJNHE +d+uWKm/wFxlxkK7JXhlTQ39DHTSGF3HQyJSxe+Vb1RoIdq3FEE3O4kenGEF/d7z9ybhu3cZx +iKGiEnlKKbZLb0JIXD4X0QmDnj4sSPQEDssHYydWyXvZCT342201texWCiv6mggx8nX7oViK +5wgTFKEhGJ7ex19nVqOHboGkZpNf5V+5BTkzDo3iOiGoUgcAnXLzkjdIgCjU75mFKJ2eU+rE +kE7Vdqn3Yw88PYXTSuKwUEqFDu2+z6fE5+JHxzlGIlROfnu6LjgQxW6mpeQ2xEsbrr4OSKkj +CCBEy+swARLdP/Tr4QVSx0RxsXerVbymhhgXLMokdQwJZXrojr73NJtH9aRSgqM0OkXaMaP8 +k7WBFs/CfeJsy4/Sox/eJQ+ZXkz72kL0DFKkUeWZcEjQVHnaZXdjDFtX86Wz/3bjTBqA/xLO +YYkCHAQSAQIABgUCTclA9QAKCRCcqg5j8k8ptc/3D/9Sqea6a3bpwgE5dsbjia5/4M1+03DN +Ad+RqCJnWlBKKVHEE6ehDKzLgXkHy67i53a5f+F72v5rTV/3aTjZ0hC0ShGz4oe5YoouvE+K +1MIlPKjgye6hR/6Silkln52IgMra6lBMnNbxsfg8e4GTepfMCs/3iVZJPqTKr+jFMCNQS+zL +vw70aTpLyH73ojl5MRYslPn3gO6jYzf0cX/CKTVo9gV4J8sJoZOib2v9la5anhZMLS22x2YX +BeGvir1XnJv6PZ7NHQEinH1MB7TTsOlugLO2Z8zNnFexcl5pCkBXvzpkmZ0EpJsM6F9CnQpT +HWx7BYlL8/ZQ3r+tSZt3bi+T0dNbhu0G07YLJcBI1audHeB7fwSSi9cNOFTbFvLMiy/pekPb +WiWMlWWUBZY6N9SeeMIO9c9y1/NzEbEipyvNQ7eDrSWbaKfaYNu+JGXRtbcq7lTYhUagPU8R +bvcN34zFsTJ/JEpOacDuJ2Y1bUYULbARTBBXr5v8oYGTHOaRrs/NFsoEZ7m/0WV8ByhXwo6H +jx9CGAmoa03XIUbbKBKTy091ZH8LtBDtfd8pvzEl4MmldVUH3LP2tNtkWFqH+whktBpJpsv6 +fv14OwRHL61bqeRGLzarozgDs28opkOd+T/+bmq+VEjoBHluiNyDxGfgv+1kdJW/1VYp2h+8 +FBgozYkCHAQSAQIABgUCTsrCZwAKCRDm2n02KoEYt6DqEACt352FQWPyspQvstPMFyhUa2MZ +LvajWFgTCULCqFvM2sRFDtU2b8gZLywcaIYsZ1sLgQk9rEWVBn3Y69EHtg0nm8Uajq6nST+U +T/ojCoS+EJWCu3kanyPy6u20JjYOqeRbBDXSSbKfKHQftcgJk4z5qL6dcDiXnVWeByXLFsMW +8EqZKgzyvfjmROc2JRNSJx7qN+vpMw3E0P2gt1fgZHTBFsCotwcKDKk6qc7KnLptggHf2JAS +QBIwVprbhUFFP8mDvcSeGkNGVtlrgCDMcYZs4eZO2NXEbgaJtdYv4Xd1Pa/ABqU6qgKQ4c4+ +dmEFL98AHUYqlNrTbX5g/YdpIPpvuInAGRUEI3wEpw+/2plRUVuLCtP8YSJHYIxWGfFY6iYN +Pkv8MkE6KvMUcE7MsjT5pX2dckLkdykip2YIyYis/oKufCwvRL13Cqx/bUtWTw9QTVIS9KQe +4vaeZbusRqakjuikiTfUIn/II8/NLzRrIvVn8uvXPgbeAlXe0HNiI2QXS8Pfl9qL/TNSimxK +2A80tlvXx6CF+43co5rPtZm3PjeykLvEZtTeq2GUWunLjE3pYuz/UK/t+hPWCUm7FZdBlf5w +l/SXfU6fC9heW+2MJ1bAlBPbFjh3R1tt6TYsK2f2+4acarfqqSFnTvHxTzMXbRa5p0V/0qCU +bEuNwG8R6YkCHAQSAQIABgUCT3Br+wAKCRA0qAIWH7cF5djREACG+efRwuMHEuZ9ZBcIMMUw +CtM9b/gFmsV/DpUBdEo0qCZLjFD+AoT98Al2WvfsoW82W4aPh9o1RkP5HA6GufWoB5ZDBo8E +lMSDAS+r2MUizIlI7MVD1DA4ZPAkYRk0oDvtpBIXDwgG6I8Gf+ssSvo7Bn9Z8Mibeik1KHYr +231OBcjuC+2CLeImRMZb8Nrlksade60JrBrYG42L3ZKqnVwtlFPHwiCpFaDEHjzYR5Lk0rlx +tgA4fIiMK4kEmnoTgmTP0roIFh88bsGhXiNxcYnNiGMAhRCB+DfWDD/pp0psI9LksYTbKRFZ +/NxOLtBx343mZDLKbnl1Y2JIpGydlFXL+dEP3AVtzgYom738DNj4Pf+DNAkodDLtRTd9LXse +bm/0bV20WzNpJ/LT9QCMapRfG60Z9YbcrH+NrOchyPJBHKruHkvfnZLyJLZBlLPVgyg95nWX +ZjW8LRhjmLxa0hGEz0kItwylGxZBvO3jHdmfYx6N3wAK4uXdam3gY/+nHxtDzUyDMzBqShv+ +81jrYk3Rv0Vh6F1TJuoNvwWp6DqCpd1siBXI39ipequpPVQjCfkZqMET3065S2htKjQDciwL +B0wP+/s9ulf15jxDUAK7PdTyDbCq43zBxdprM+Aoh7KQAfyW7SokZlEuCna4cq7VlTazvLcT ++fzolmqCyFpK0IkCHAQSAQIABgUCT5/OEAAKCRCav9NJG+YAIoTQD/9YyuWFCLRp2BR44K0H +KwDhMa7nuHecH0hmzM3GJzvz1eLFzfOF/N4T1jhri60JDvviIvlIM4PYCZajsonN/d6pI+zn +LQI74fEELfQA3Iem1Cdz8BnQI4HXYYqpeoG+Tm56krmkSNW+KNAa1w/Y6NoheZ5z3YNts0C2 +f0qo0FmuWwlKcVHaouqVVcCla6LuXuFT2q6IKp3EqZ3Xzx3VUo+E+BykTG39R/xDhdyi5f+5 +nTHi1fiTeR3Fvr9XOlNqhg3DibNVV1dx0cAAqj4xgBv+WCDTwuwxj2K7+6PZMG9EwKlg3RIS +zeXYq1PqAl4MZL56GdmJNkHX4+ZFpYzsVQUse37Ol3h0+oiWdj2fXHdmY7SzyufxwH63f0pJ +dadIrEFVZqy0SYKWAFGDzfmP6oYwQx8BFMGuOUh3cpFdrbjaWMgeJ0JXIen8a9rHe6pCUzpT +9JUpa3bjyNkuywRsX3N0002YufoMXBisIZN1fal6WDAe89BgA80InoiblbAykvwWz5f66Qtw +SGhd2UISuDcAxmm5eg8uk/3Rz8hDttXS6buPnthIzV9zNwxj2DY/J24dEJNjguff7Uf9Xl6N +XeEOPr5Vpmyn8uwZNx/+9T0BOE3bW0tKiwxZGObub8AHqEjCj5ZisAEu8vym7qjC7RciqxGD +EohCU8g+5/yt0cJxCYkCHAQSAQIABgUCT9wppQAKCRCo8sRx4sAmLbECD/wIFjT3p3gVmnNn +JE0Mc4CjH2vXqhfghbsBPzlqPkqE88GHdoOJqw2POdEGLJcOpncQPEYSBbskjChuyttLCr4c +QZWiXaUXvNeQ0wMMMtpZLnRa+bmAwcb7pty6R71R31Q9htOYQKdoZDc4OmiQuD7rHEsH7tIy +9tc7XMFywwKyQqySOWUwwzLwEi7qprbW8NhbV4vl1TCbuLHRcXP/XgqSVCzOhCv41vhwH5cj +MEOGhjC+DTQuTilQyqMeZMjl5CkY4CohzhS88r5LPnuxH3Ccg3Vjqkz9j61n+1M0rkTM7GsL +m22bw0B3aICuYG+Mm7x7OzYBFB6IWzEEZRaNOXNisMxmd13K6mNNV+RLr8cVOejD8kP8RcB/ +OQu5uQpm2OFgSnjtsbaV8uhexFSegUJ4nPdLevATKm1TvGpTniwPgmGcWJ8+NBoG4xkncE5q +t4/hrZxulxZGKIMNLFaR84YMulGfvemAM/Jh/27CKltSGWR+T2jsfLm5bgWG/FYINd/z9RP6 +P8mEgyoWee96uWCM8a6NLBwKHX+Z+e7Wd47hUS7bJysVEMNNlHY/rq6V9xrBuznrsCFidDao +F0hpY2AeE8FLTWVFtYrPoQ69mBpfbeiG6uHMG/0fZxz3wBWFE+M/JaDUJf8X38ZcHbSVjOk2 +XQF1rGTAo0+Lzb8w1z0zrYkCHAQSAQIABgUCUVNf1QAKCRAuhYDll01Y+fp8D/9dhmbrdFOC +41v+NoBPzg9mzcmux4jZGsGdxF7Y5VSwtc6BQKdAlI8ziXqb2MjKJ4kOraclWS+jASRM3zuZ +BS7wpU7McdWpADpTcveUSJcBXUXvdPEJFo+7oJ0zIw+J9VHhW6S2HtI8XQAkCYtseaOkkdMk +GteQfWK3vOml0sZ0dhYkWPAW4Xd4sK30VzGhntmySEEOEdtiuCGO7BNIz3vQdzcVcnjRQMlM +AAzMxXtpArrsc8/BB2eBcexhbgdcRtdcxNjAa7inRdAtvgJHOHVH4IRSXGs+OD2gpvHyLkro +5C1EwW5TETK+blzQFNvPxPJ4M+HubCJJbFrQErVVBV7AaFdPA/rb5zP/28uW+Wsl+OCGqm94 +7FaeC2Cn1/5AD3zQR8E5ANiWMyTpPveL1nnQhigrsCrLJPhQDvh36hD3e88Zo8HzYj//Xphu +qo2VrDdnT3+EsZiehduLlFfktm8qWDlSw5aO6HLWjhphH46AByWr7V88J7yIlpmmcPEVdMfj +wWpIq6yB+3/I/V3KCaVbLH8cpN9+63h9YQpZWhCrLmDrXQKy3dupu1ct4fa9UIWgaZjPfSh+ +byNfzCTR3XtySjeBucZt2TOTrgoNDOOsz/El5bptVvECGIWUJoTFbRAPh7yr4zl4Yd3PsN/y +WzRY2+qO2cbPgvXLykk5QWOO04kCHAQSAQIABgUCUdH0pAAKCRCw3cc1O4BFRjPHD/0e1r2K +quF9mJUkMOPp29FdNSUXi7LV83wXB71XMOZDahq986MIHrqCYszvKp2Yo53ODUMhJF3Wob7s +GaQXpmFqWKNX5S8p2c/p4DXoNkhfmXQIy3863T15lnfwEqvLbDYgJC3dmS+q7h56FSuXVm20 +BGmyRZ8lCIK+9o7digKV+SEIDlfpMuXbEh7iposOYRekUhQXvL0Wi1lAhZ5m/rKMvc4+9HfJ +GSKgb/H25WC0Mpmn1EQorLDB3rBYjUURwFcVP1mpOJQ113QVkgtILo2zdUASDUEIcL059z3/ +9c9ChnnHEQTRFLRpL694jFXiJoeOK2O/QzodY71q7Hz7WY7w6leqW5GPMnrgsCgQSdBlWf2W +jEOorjL7bXn06IN1TxA3t6gycH3RaJcjc5ydOwBy6+di5bxzh9sTQJm1YqLCfMSqGYXFUC+s +suVm7onQ8TZcuy+nETIbQsJO5kcCAKX6hYy7GDLsJHsLEW3r8DPcRl51sO9uA3vmEtwtrUi4 +jMu/mANK2+CnBO0jYkZeacO/O6MJ2TkbyQnrbD0itt8XUrGW7FZFtwS1d5+kKqzi3rHAMW0m +3yCjtyKGE9w9vtiJkU4zwEly/T9O5EWrJI0OpAMVuvClyEx0xM+iG/f/bgf98uncY9aiC42P +s3c5lNvQ/D1cjVFktuAng6VN0607xIkCHAQSAQIABgUCUdKd6QAKCRCeVk6lMHomgViTEACS +3lf8i6h1ivLxnZ0mWe/jwg9n/LSbyettcbvojged146BHyG5TV01uTKsLNKOxMQvvYNwemjC +6uJYCxkY+yaloWxH72i3qP0l5sk+rIKIi2lNgPKEkXt0u5AnsVcOpzHXaAezkHW1nlJOn6/L +g0Eg+wRW4UCeHRW7bCFbOccG1wwEB7ZUEKTZ03QEKEiLhzS6yF/376076EERBHkh2vHqRu+2 +7KoAYlrs+qCe/WZclE3kwxHFnQVg20dEc6HjXH43KfVtcV5mEentGcEiwHYmeqnSMXfqIX+B +cSGXUAWJmBVMFmIHcqKpa4v8pEdX5LZss3jzu0SZ1cxXbh7WjnuM3YRoWRSfnbIt+WiWOlHv +oGetltz9gjIqjnH56suirlIaan1Nj/AfUqKFXhjS3adxbAZdIKd2axpJlxs8FE7GMOQq6wu0 +mFEvC4v5/TkDWGjE0asJGMpWpTLdACueXkPaw2gCCds+IZ6cdS+AlfGiHZfUveK4J21FZvN4 +FIq3lPjANqUF0JZaBIJ3KcJB9TB0D8fNwV53V32FlFjfv+qYsQ0nVNpYPEnagEsx6L3lgdN0 +EBybDdnhLzxa2hQhSze03FYc5c5AGO1YWJiYOabUNOdmLfkLUqR+kdL1b59pK5T9x8H5rAZ9 +h4aeWmiT9bt9PrQYndct5sNgD8+Lz6IQFYkCHAQSAQIABgUCUhs6kwAKCRARrGPgjqpj9/H7 +EACPKA/P9IBJ9UcD91dcyCzT0wajwJBTZzWLvWDVCPUX3RNQ376Nj7uT67yh/XBVf8oFda5X +f1ZL/0GU4zKFZZ6gj2TYNlcuoSMiqPEZTWPOO1ywBLfBNPKVHxfZUUp+TIo3F9LPChicOO6c +yu4yKOgNQPcopwcZaioEuOXHiaquHc9XxNgwyaW5s6bRLhKWXekb3dSGmqFXS1uCxaZDNzVi +cgIvX6EhI/WzoQz/eUgPwtX7yaRIMZwl2+FLw4cdFd7GtI3ajD1V3/C7r9BKaVi0aepbxnJr +fZnoweqI2ykdEqifybJeVPCJhfJsMNMkuC6pHPIHuv51aPXt6d+44SS/1G9pdRnS76oVVLFz +Kqj1B9hlghRPidZuybORoILq8XhxsD3aNKJX+mxeX4muvSUtS2e9TAyVWI0tA05qPiHAs4rB +Be9aORycfw1Mz/iDkeDQUaGcRYDCv9tIyWiYRvbaAu87hfbY1+ot48k/7n//sdJB3B4njQnX +2omyJPUxYE3YvDStKxPj1gAL19B6dqxyaXCpE2Clr7gGKxFcoFbGbFqUUEVMuKCHZkNMx9KD +iWBFCGnawSVMACyWuqlyAs3p0U62tgjvKaLct+G2awkSutypK5GSqbIsAq0z+9ao3gPtzcmf +V9E4tGHttxUmGN5aiSykdb/rTaRZxY/7FvFFkYkCHAQSAQoABgUCUbbwIAAKCRC0FWuXAAAA +AHdGD/9OWBYYFcxEhh3TPGQoF8aOIX35GZuhxQ7XCUMWVZzTJ+/ndJccjTQlIYabBrZq9WK9 +zE6XB6399ElE4ODELv2Qsrw7JLlIl+MumgdoSSX7zsf/U4x3d3njrwKtWHQcT2GUnoFRlWHD +G+lzVAd2DVoqYFiFSEo2hszwaRlWhGw7Equ/5tt74/t8njIm0H8kZZsovs4NoYTxTfo7/ufI +deUFgxa2aE09labP4XtZUKtoqmA53ir16dA3MsKyjc2trDjkEpl1t8HZmxy19/w0+aDyKm5b +F7+dGIggyqDTdNKnP9i9BAdZyrjfQtK8Wj3trbGg0h7YMYq+IYGz+8yoFIkYdizqzR3fVR32 +ua9+Ziz0cVgUEDRu5wSc27QBpyz9jlN+rexHEznL8FWG8pxm7uAf6vlgi3ERznFrslJicI37 +9mJ6xAZwBQNKsJjZQL96BEunJmcWSfOSRmgrZF1RTdW1Z7sYk3Z6+ab1Zmlbn5juBU/yYaWa +Qtu9QIFjXDJR1hZTRZcQh+f/z3epgYKBnI81E3z0RLdMHp01KJo7JQJ8hTajGtSYLJldkToA +6upq0gpzEOjm4qOWcEczO8o5YXC5Tep4FBNLorPd0lBmz0DGU4qVa1JqWPBZUo4WVooOrYKD +QPq9CQ96z2ZKwvAXUkLiK9m0uK/1JDRG/rdAP4tDXIkCHAQTAQIABgUCRms3fwAKCRBpIeXk +NW7baLDaD/9yuGlZf86+IgQgLL/ggN9amG9sLQkyW4vveCQO/LSJHq4jydvFcF+6zpirbmhF +Wt9bljYf3eOJlro6ZfsLldaKpbbDRHhSGdqwksLNIwvj9+OYL9PGV3eNasiZdscE5GmsZj4n +3o3kr80vaQSwYD0wov0geMIpdA2NvRpvSl8PgcM9Wi9mhuYYahsdJQgo+PMkfX1TTYCE4Gf2 +aYWtniC7+8jY3cMWKIkepW90nEe0sjMcEmN78spPAxocudoOuX71DgxwIxqmms3HYy8LFjkH +4xkBIvx3PhoMjqvXPvY24xgMPkrbIY1d2rxKl/X+vX22Q25PT1P02jAx4hzznHTf8fpHkuvF +l31SGN8xv03l+gywO5Tq/+NWk0MAueKYbCXFmacTCX4PeUSW0O+1TKExBNYsXOnX8HPkp1nq +8UmKsW6CiBjbctRg4JZhrO6bOtcld0SgKiDDpSQq2q8qPvwv6S+I1+pXKruxSExrixO8c/jq +HO4apcO5b4yazdPE8W3XtLx9YyWSvS+DWpNk81T/49m7RnsIsiagokxdp5CAJtdK/Nb3D+jv +5uctvR1sOVLo2a2M2MuvCYL59Z9+CsyNjFUSmyYb7lqqhYEwfX9siW119O9nkBYl+Sf01M7r +X+0S5mr4VzKU6FuIj6qEWWc3ccTTY4+BH+Kt3b+lQ1JCj4kCHAQTAQIABgUCSS55hwAKCRDg +38WRr7c46DWjD/4wcZ7xpUrGRB3G0/xUG77q5//aI4BzFMQQpWbxNfRtAxBAv5jcZVfIWJ7+ +iENsC+/puo+zYLnaDal9G65yfIEOQpLut0WzLIlTke56KIk/uWD8bCTfg5cEf+1JUT8DecoK +UGKRPSlt7g/+WB6jznnYAtJeFN1PBmiir4gOdfKobvazGP5Ov/nlEUIkcXEfoQ0SoFyM9TDj +prC5XEkgo2j1lb+Zi3leXveqDCfB7ogK9vtKMIjgZXQKjgdraZLqderAgAqFJpULtURazHIA +qGgtniaMrvUALglI1HMrQQTjHwOvpBGGdM57+de99T/OPeZNerRT8/tRMf9Z4xL/hro1+ZEX +YJ7zH8UGFcwWiLPnfK2h4UGLs7RcQGlOT0aza+h28nn3hVNidM8x3yk8KBh6ue73l9gzKCnW +GmjkQN4+43Pd4L8GGawD6SEyHrNfpEUd5o/cq3TJomswB+v6YxjaCkKFuJYG1tKrC2OON8gi +AR2BRSd2ssI0Cwj0XEQHLTcYzU2zIxxghkqFEj+bOeC1mETI1uomBWPilr+GKk9mhnmD5lUr +AvUeSJTNEOHOKDTrrwmF8Z2QBSD53fI13kpSBNdkht4OI9ATlxuw7obaPij9Bt6wnavKPGBJ +FA2hYXuxeUXLBC2c9cBP4764l1EqOhCKVC5wG/txxISzdMlWQIkCHAQTAQIABgUCSolqTAAK +CRCxrddjHEAOuFheEADLGzmdVJihJTPTZvw2t3+svYjqfjQJgIu6B2duRcSBmKGelaYe8cXs +of0EFLPRwAiWOChUOPO7K0F+bxQ+ufhenEKbFXg6c2MEJ8DoE7fB/z6drgV+eB5zqN4E826T +6tEDqg0ZFpKqEeDF3vtPQqsE1agpkV/GKvIJS9+tJxv5FafnUT8QpBZCuZt/pzC+CXf/W8DJ +3xDrLuGmA//X8BHa/K9gZm2Z5tLfYupukgPRGaHZX7XVuJldkQygsG3Pj9XZgR8Y2piW68GH +zDXGswVA6l5vDe1QKfrnNM0tDUauYLpRcySIlUUHQ6YAQu8nO2IZfAbad58NGiWPupCpbYDm +D580MALvRcb/z1PRynHGLKxLsZHUh0C+cSaGP0ovHbxnWt9YJ5X4QmZSGXmcf3cAd6EZ/nGG +kptgoHlaA1dCqYbuw6V52GZtNUKV7072pb2g3Z4fTpeaVb5cT8C5Qm+ARS4gHkgOTPeLO416 +QB7a7+64IPi3Buhoua2oYkAYSokfzTSsbnEl01qOYcjzo15gX2YfZw3rO394uXXjtxoUX0+K +kWT1ee+7aj26CqMKR0CTbK40UgsUmNPKNj4ESCqu+sCsMcf7yxJd7mGPi+dxQIxEvu1im3No +3eDJDk7k/PXZjH1TtwBFfPnOxFV92+YK4LFdpzG6m7OrsEt30PKJEokCHAQTAQIABgUCTFx4 +WgAKCRBEqjSA/7OWoRXuD/49JDWu3BUGCS7QrhXqT80fsW566Vf+zVypkImlVr/rOf70w0SK +CVZpNGxuopqH8O36OIcSdIxvXLjFkf9DhFmxZw/u0JiutKX3OWFsGtryezY2e4cjSAP7ol2x +U4+UMpFwRu2pDt3JX4OpCREJ6ispWmQI1o1q9UJA0XQPUbmq9ie5rkpKqeR4v8IXTODUTSnk +nC4MiiIJm7iXq23UcbWHUB2NjNB2BVlXimqkmdLmzGt+7zQXCsfAJiBw6w5DCUOgJgqQm60P +CkDZMtJbKrjgfle3bT36CJUDXccMgDswwGyIn/Ant2QMTksCOYfhMJDBuIhcZlcCnsi4EoBe +dvM3h+NQr1p96lfYLDBkWv40KuvCr9rXaxntbsJT4kXtVh9AZMiQAzTvsL3ojltj9SGb2BSC +XEfiWwN0l3PMD16qs25vWMH52uDLLqhapHjwkidii0jCDIsksorP1NgA6LuE2yG2/s47ijQg +xu2a96Sz8pzr0ZBdSGwhJ206LazPAioKmRrzyipdLf9MXcLUS5nHWZ7ejPvvB2eR7Kb9cbo6 +2nqfgWATJLnKFJIsNxEB3Blwa5410Gily0rUJwvBiJUV0ZDbtB8fjtIAwlZwSTXgVXoB/lSl +bHAm1YyBwCVjD9omlZ8mNYy+0iNFu4kI1rBASYioNW/e862Wg7fQh9UlaIkCHAQTAQIABgUC +THVe9gAKCRAbQmWHgHHa4KmVD/40RygBoqCP7WzqhB1yI+haY2jFG62lNBdubUhY5nD+vEDv +BvOaKjmJHZedBB7nbFuT9qzBwFuX6qJnlNRmH+yG06L3HD+UW+AdVES83zU5MTEqxnfdce/k +uB/9nlpqugIIB8OicHb+tvXX2dpXntWX9vgsuQQ9NtzV1jvqeWZxuByJusZD3R1N2IY9ydk0 +ftKdwWPQUtjFkODFhTsmMLxv+OBM9KWaPc9YkrfV4MacnbGnweYQh9eCxRiipJkWp1Q+rtIh +T3DRRE0uyYObBMTjxpfw9xYFceFIziYM0QNJe3/t14jG6f+hfdbYZST3iKFXZXzrO4emt3nu +/8QIpm8pZg/bQnChGtx0q9BvFmYoOMxe4j94u28tgbQthVUpc3orfHflOIs0ravX8+vL8+gT +nyo6GN99zsrfWjZEbS/awPjMAKkWr17MDFRSf5vFKcp7kr81PevnkVeyJZ7TeTdogO3bc5Na +nBNspzlyGNc/yHrKsmCxwUs03TY6r1dIoZJ6k5Zr748K5d/hw0io1+ITUQluy4wp4Gy8Jv9G +c/ADceQEsRsP8MLZlWrb4reUFUIHq6wcQjUeoZUX8oqrfaqwC+QeDgUniy92+3ZBDcz8QYEv +lKh/I28794bWC1zCfPjmTxRNq/OuFj1Q82j1l/ij7va9U2RsRpShD0fGbMTagYkCHAQTAQIA +BgUCTHVe9gAKCRAbQmWHgHHa4KmVD/40RygBoqCP7WzqhB1yI+haY2jFG62lNBdubUhY5nD+ +vEDvBvOaKjmJHZedBB7nbFuT9qzBwFuX6qJnlNRmH+yG06L3HD+UW+AdVES83zU5MTEqxnfd +ce/kuB/9nlpqugIIB8OicHb+tvXX2dpXntWX9vgsuQQ9NtzV1jvqeWZxuByJusZD3R1N2IY9 +ydk0ftKdwWPQUtjFkODFhTsmMLxv+OBM9KWaPc9YkrfV4MacnbGnweYQh9eCxRiipJkWp1Q+ +rtIhT3DRRE0uyYObBMTjxpfw9xYFceFIziYM0QNJe3/t14jG6f+hfdbYZST3iKFXZXzrO4em +t3nu/8QIpm8pZg/bQnChGtx0q9BvFmYoOMxisMRRCQf5FTaIRgQQEQIABgUCQiLaSgAKCRB9 +DflY1yo6GN99zsrfWjZEbS/awPjMAKkWr17MDFRSf5vFKcp7kr81PevnkVeyJZ7TeTdogO3b +c5NanBNspzlyGNc/yHrKsmCxwUs03TY6r1dIoZJ6k5Zr748K5d/hw0io1+ITUQluy4wp4Gy8 +Jv9Gc/ADceQEsRsP8MLZlWrb4reUFUIHq6wcQjUeoZUX8oqrfaqwC+QeDgUniy92+3ZBDcz8 +QYEvlKh/I28794bWC1zCfPjmTxRNq/OuFj1Q82j1l/ij7va9U2RsRpShD0fGbMTagYkCHAQT +AQIABgUCTHvcEQAKCRAFXCweLWwm+bA0D/4+5oAKMTV2hAMIeiMfKi8AsSaXIJL/Z6TtUy8U +3GRfHG2UGQIjI/dZUvgDUSY7Y7Ppo7jbfza9i3PkyZ8jfIMS4DyD2Ml8GhAIi50STHhB9onW +axQ9DiP8pbD2Bwpg7np+worNnWceBnXcIT/H51UUHVuIcVeOuInaQmARPWP9ds01p1lT7nNT +3L+zCoFyvk8QVH+FotwJ38+/hsVE8DibOKtfvxC1iCb7FncXjQtj9+MJ5cHYebYzJDkuH+rc +B62oQlwf+edk0Jl2/KqmFkZFcAnjHfK5f6iDD1tCjDTzAqceAgKXrjpDOUioCiom/TO6I4Gd +rz2XdUCm7aLIy5LkxG2AwkuIfSi4EgMB3R1enJPXxL/eSjD39x9hYoFYq1EsOTOxcWFIiAQn +pE5cjgdRXnKioCPNoDySac3oOraOJzL6kp4oTcDAI0XrKWF0rMZMHafnDM5AASbota0Jfy2m +oIs4qliFkAUxW08GXqabBY2IknVKVhcMufZP54UMhccCDQDZRm8EI5sX0Alm5kejkCjKgjBe +Hou/eRmX7C2wRyc86iyK4PpTF6HOfzcr/46YQaPnszGJSFQZtohhc5VJPFn/3qWfQIAVFYZD +PR5e28hM2mIVBKWi76bjEmtNOHFrBfYIn3+T+G5D4OLOHBg7la/6mwZ0Qz37wTfBXW0q9IkC +HAQTAQIABgUCTPglggAKCRAy5nrarOTt1krGD/9BimKBl79ycq0fZt0eJhIbpjaxCrAyS4v6 +ZZY/pvFNsDhhYdmnNjACf5gU4rNVQcOzY+P6YxQd7dea/kqe8QDdJkH+PZHUGKDWgJPiafcM +QUApUlmAfKycqFmYPhYyNZk+/iZi/rxgCROfjlNFL9x8SYLaTFrTc98P+kh1Cbq0j4qeA5pl +bsosag+k+zC7EEx/u+UI1xmD1JPycUv5VHI1K9sgeybPoC0az+SPXtnMtG0r2w27RP8WcacS +KV0hS25HmbAWKBgu8C/Jl5Hp6J8F0eYcEZLyiKuPQ/YzZAj7KXTDeel0hl0dLT3dGF6K2fhP +zjvhriWEBa3nw8478a7/TiRDq+Uy0Fj/juEaWYFmuFsJRAO8qzzOVpdjLGNNE/RE3rBS6pFQ +TEAyGjLRZeCfP9LeZF4QIkck5dlt1hnVZWUHkWHcRvUXtgC3FMJcp2nGB/2qcoXXwcUxiaRK +eso6SplAQudWly45W2/e0QorTGVTwxLUMZ6qixHOgkh19ByNL3XGMWVC/iB7dcUfBdHf35Pm +zE7oti5NHa2JhV/neDq3guv0pq/JN2y2fnnk2RItkRtNEFYbLyt/pOh9i72fpuiPRPKqnKP/ +zfSdv7T++RvRVMY5f+Fgc5kMNwyJXq4/SedF3PvMshR4RBBvsK9GA8qZnX330LZ/SjSqlutb +6IkCHAQTAQIABgUCTUpkDgAKCRAWUhROG4HPXsbKEACFWNv9x/D5ADeokqbyN4ZppNkhPv+o +zs+RDwk/ZLoC2YUjxiztMmMSWrCZkVRSVff8aFFuelpn9sTswPnsb4vRqPA+68AyjFMFC6Do +w1dmbzqqgPKGr3DCL/yuuQbgcK6vRKqRMt1PPwhnoofhdn18SgcAZvDn9Klidy5bzVaIbkPW +/oLTCwDh8JEk4P3mT9Xg+qufyWpUAwkc8GLEGJOE1YTKaEn/VeU1+aErPpxs5y1/Xdk42dcC +fuP51b9lq8/EZmZlobvT8oOkrL5SoBy+q3MLEjXcqXctr3pR3JGzEsL3qj3zeVGz72c+XtjV +t4fhNV8TV5JwhlCLMssP3MUGr5mG61gqilh26w23rky0sxocl/z2pFBlLaks+skOC/0Xyf9U +tpXvlwa7fE5MTVHywwhpcezQG6rTzpyfmGhosapVO1WTa46aOL0LhYWFBqi5jRA4KxVz/ik0 +nA582TxXBCwTNGAEtG5ElskASnNmADVQ25CeaG3iBRMDMweekZvIXZunTzNJDw1kdO/BLM8U +eNAMCeFpkAkzguF09JVGzWbRc2kGCp9kJT6kIieVma4bo7m9Exw/wIxrNYc7VlYjQxuGMybl +JACT8130zlGpXEkUvFAFKJGGYgYG3t509B1v4rsQWU1C1sXsoBR8pevrjiaDKD2ZIDeShWNN +rSfxqokCHAQTAQIABgUCTcINswAKCRBrorXMtsyjExJBEADBt3+b81iyCglUsO661mKdMB8h +iGUpFeIvfJfCpyL5z5bnIqxPjbs3XVHLkaP5mPZa4504wr9p1ODv40McZV4IzcwiERHXbPCI +OyuPqYgr75diTWXgAlt1AT9//Xl7qUQGInBcNW5ybscCfqYIaii0w3eM0G4Nfi5pGoybOokU +vSBcwJ8lyBgFNT/nsE9C9a6plg/DX1NB6FL0zwtg+bjktBemvyG9LgLsbuE4McGa6DSaShPF +bsAlwPV9PaoEas/YcuQ281Tpm1Ta/cQS0KIWOrmxOcpCBYJ/Cii+tnJSCBOZUs0A96LwM13f +s1KfcT7o1ClLMQun3s5M2YPxNvSPzG6VB0nfICRDykE0ilzuARbg66I8xaGVMAplUD9nwcMB +LqBPpqwLpbMk/8EfaKxTeCvoOeVU5quDW/Bwno9in8BUmdveEwaeuFRWsMpNsvHmcK5sz63A +//J1cEeSTAcNpiruI/7D6GjAPDLZF+DFbCkDSN5fSWHGcYM4bHc89NVmT5YyC3jBfCLA2xun +OVfxtrqECSkKNyVD6MT/jjcFGUgMG5YGL3pMr+l/nd7o0+b9c0gT5/DXaVpQ2oH5TOOAUKw1 +KLJFrqyHrEwoDs7GkVq0W+l/TOhqGzgZD/ZAcPx39JITZgbI8s+CpCzXkaRTI2RQWJmjC5cb +2WC0QBOiuYkCHAQTAQIABgUCTtGoOAAKCRAE7wIvDBi5zDeoEACG2kzKwEY0KqM6DIiBSZLp +vYV/0IfsFvS7nK9A02RRIpIgNcM9rNS0P08dl573e8Oc3/nx7vYt5NS0HEYeTdypW5ukBwR0 +SzZ7lwNLoIgeIfcBBPt4WuTjUwIRaRJS8XpCO3kchk3u8VC2fdBcvcOf7jNPZePjmpVoBtNg +4qgpvRogKbL0y6ZEIrJwKsM54wO/n+KuoSzQUXb0OK/oI760AjXUCQ/FPYKCtksvoD1XlMAu +6ihuvXlp0GrfKOXmPeC6OVUpABHfAAgDJ49K+EdlW36WfBvzdNVwDUgu6lr0rhJOvXOQQW8q +CxzmgmF9z9hhpiJHmAhOasqn+qf2b2cv6mVxDmBcF06dnNdcbLjLrCsy9Q/o/VXFnZp8KDej +WWqdoGSKH6bouTxQp8YJRTg/lIKaVL73WK4A+7Jem4lQKPFE0PHV/71mSbGaauGbvpZ7arH6 +OTPJN+DmgpLh9L/USOVv85VIDQwfS94Dyjo5oyVZf14J18Upga2Ge4Y19667fkms00/rC5zH +xeia2gmUo2Ziy8nQ5P3QpBp8ZHWE7PIHlJuguaL4acr7CdiumtId/bTAMftgCjhRmL3QK9wM +LmO7Y4NNCxMTJ5AnVY0D6kAWdtY80Eu/CwF7amMnlp8AMSqkOWY0Pc3kpvH/sGw42xjTmspQ +thMrQfqyDSnNdYkCHAQTAQIABgUCT56umAAKCRAfNlZlF5PsJ+YUD/40gxCQNtEUDCfZBlnn +xCB6srLsiyO+ejcLLVgLYd2G6+6vP7mEJ33sueDBQraSuTPIoCA6amC+iFrgYd5g4W2WnMFG +j5uydJ9rLC5Zrykfq6QFz59CFk0pTVzX8qsqKtKhMUB8Q0Bnqcdr0g4fqTtrEear1GsKD88o +yMfeCJ8kTnwyyalBAWq2Ow567BDZlYIUEY6D7QZ0hv9WO/QxMIzUUN/Miho/S8XCkKK4tZa1 +lG0oaoCRBFDmjw8+STiWfsbSu1HRXTHHrYIoeWSCRjPH37Qtuy1f65HnKApGNpEfd1BYyuQQ +uakvKlWUEY4ss5VxnZNrgqszSCcJoEBykqVkxnWk+9p22Jum7sCpOpuKMg4/s+jd27VIGN4Q +jWrI0MtQkXsrToR39bk0zZdsogaGO5V9ftostQ2OwmG7kxLXgFbeKwwZ9S52g3cnxUUOh1fR +Mozj3vNI1zM5fFQ0jszyy9j6voTl3Wtkrz4zaTD4PbYjoNo9P1FqPUWHHbiksWCG5Py41avS +YXqgw0n3sQebA7z10nuNL3tctN/airC/HSfdRj0w35G3Xa8FaEvV8grxr5ijXTss+6en+Fk1 +vhwPr9raugSVhlGbRKslIkYxl1ZeI/DzFynzAtNCLRCKKXaWqAqwAeIJL/anSIQtIv9qUF5M +YRTT06jm+LHvlJan/IkCHAQTAQIABgUCUCfp/AAKCRDbkXthzQzRbpRzEADXyc/EovXXGF1J +ZJ1nSQgyDLOZnpBRGwKNkvnsRecjsJGMu/qyh3Jsy+Elbhpw6uWAcYbwTSv9RSurW1xENH/g +xKv43cubYAxAkywYRObSk4FbKHt26XU6ELdjAd3lXN/pJflpIDNP8USTr8SlyBmzmv0/Edeq +o1m+BHXSzeLhQQCkQ0cMuapHEFri161RtQEgkRSIK3a4huGZxQwAPQ66N5s472eAMSPdtD1F +1ZKtfPd4fayj+hgempjLWlkaEK555rbPRJTTo28brw5JUAYHcdmLJkzY6XqTkhPiTYo90WbX +27Doal/N/nmEtwaecd1BAMrIEpqfJ4jaH56gH8dcX2odSwC3Qlwa/wiQrZMzWHxcbe6ETZRg +/G3Kt+Dllweaflus9u7i1t+y90IKI70ZTltZfLzFGWqEkcxXK6GIelakDRaOlGIKcnSxcBDl +2rouC+wWC+hTv5RTAA6+W9srw04P0Q0Upff3XoInVcWdTJijXEWRP1kpc2LuzOI01960dmFv +HMbV8RGwsM247umt3k1X7+TiVD2YF57SHe5RCjeB0PoU7qhHiHXcyrBA7J9JNPhM9tEy3b6l +70ScppDoYiU4wWXEFFc+siFZsjNQlcC4ams+PeFHXtTPe30a6W+hishF2bGqc63Wy1m6o+If +WEQvlc36my67/QE8+BDCDokCHAQTAQIABgUCUGiGCwAKCRAQuqbK+XgZ+IzeEADM24EDKwYN +9HcrFtW1U+y4Rnz3sEaO2BpvagFessO2ETwrxuyWgsOiPqa3lZrgtj838ZDdKPhlhAmlOn7Y +L8/SXiYdW15/ADyrNIWca/I7rOaaBQqxsd83g9cKijvaQfhbu3ufWgNtikcqIMfLwlerxExP +JqtNqqszhJbEWt2sm1hn5uArTwjCtThbJH77mVY7hgVtW3NkA3seSWjA1vIgMW9qs1ch0PqM +MLgVEalluzqFhNDa2mKc7+1Ma+mzPYeZ3OFL7KYd54QE67cuWKAloC/F++OQb/WV3lBJWt2g +0aWTv22wVFvzgVBWP4a8wM/LLLD2CxMzC+waWEyRxLPsXzpcvGHMvzqA5uz/tsYv7bXeTh0S +R9LmFxwsqGEay0kS/eRHRmWuLdjFnx7gJGqqd7fzFwhHQD8ap8Z/z7fbQSJPe/Z5A8PyrmMv +/9EUv69xWJn0ljBzSxP8pRhU7L5dgZFDfgG/4QLzDrwEBgVFQpu/VJAT9Uvm0IYshahVffsq +CStHPUKVcWMs5GyELl//rBGEe/smIhheYI9wp7sB8BA7u7ieKaiYqQlEUDegsvV2kOQY0GwN +Gv/Eo4C+UR1JgyopbcoTHPyVrCVRpRDVuiTVlkMiiaAWdYyK+GLZ0Iri0clbR20jHiwJHU// +YisbBij7FUx4xDcoKhCGtE55ZYkCHAQTAQIABgUCUGiGJwAKCRAnsc7i6ZtrTEA8EACiAFIv +Bu0iThfWdeqxglv9GofjhfVT09flLwFMVMa9pVoNm9yF1/ErLa+umAMDfQkoFfImbN6BrWUQ +ACjpWKFPSdtrX8h7QQkENCAVQjBt4uHAvEC/iBF6Umtolkq95QbjLIZvY0qSWkFHqeqvGg/Z +1G+W39v9pXhTyvtj2uaSSRQNuAW5xwJ/kyBgNdIIPmPRm9o0OPM8a9oWRtUvte+np38vzJjr +UzVZlVEmXw0GZB8CB98FqT/xpwPvmPw6QXNJD9T1sSeuACPw48dvcjEwFdMwPUn9G32t6FoG +/FOm4ik/ZZqpiu8kxOo0EJFkl4W23g0o2oSfWbNr+wGf/z+Q0pQ+y14XXXdXjhN4sUM9zBDs +OtuxdN1FX7R4YdgqeOOInXfQ9d++DeuReBzT2cpzLUXwwxtS9iskoKx+9MTlbADpgpGw3bkY +KG8bTu+zqzFNYmHP5a71xVDLp6xqqIIax5GjHtx9zRDyzrwap0ZTyy+tOWRIQgLJ3HBkm/2E +e5zQWrAGitWjtrQjW+T0NNnWZX9k3ynVWOgjiKhtiN9wbzJyg4D0W7FDeTv28dmcYdk9MXhF +er3Nfd+i88HClFRBSIUGCIIfs77TO6n0EUdwYOXI5UjWJrWvLHUWzERXGcP24kha15SguyRt +FZ1sSMxCWZz+4M6HODJFHBpaoZ8J04kCHAQTAQIABgUCUGiGOAAKCRDgWyX6S6G7kMJsEACS +hvx0eRX6rMBdXqz15ChdwDrujzoD805d9erYoQkcd+iXN87XwnaidtEIr8q/jIJtxVK2OGN9 +yq+buMO86nWmfsE1jLvesThYZO20105lIQOLVGapE4AaWpM7dohVjjGZPvXQs8ZmtD5tGKnk +Lag2A0vTyGaUg5u+38FTpSrUOeSX623ZDE9Ddiqa/sDekrGzHrPp8onuolO5ypl6hnwDFiCe +KMlKs7cXLIBGz5bPAeycXNIjhPC5ey0BQbPKBCkT3FxFj/RgyPOGP9w7KrNHoXTALWuywWn2 +Dal7L1H8HuSyHya7CzikgZAwRzzrR2jFz5hkgwjyjxnvhsY0lowHo9x/8UXnDMnJ22sTkzhM +lC0Pp6xQVCPFMOfhmldHhi8nip+Z+edGSk+vF5+BkXmM51Weobv4P6hNwSDIt032sdqUzzE4 +S7rlykyfJjxwPZFGzjMuW6PFdQktjmTndNupjBck3GHNXKB/4XVCD0ey37BY12mcxLRd0iaH +/fJZHooIFPnsmAp0e/k8NmxBQ2ne6sK8pj+oNoP2RbySSTExP1qg6wUuLll5TZJwlTIAryw1 +fB1Isa3p2+uiLS+CY460oFx5eGGAs6HC2CbKmCzAHU9eLcnYKn3z2e2Ec3lKL0nj4TIF8HiC +jICd48zOii/qhHfl/x6rsCdpJI/2K3P5YIkCHAQTAQIABgUCUHhvSAAKCRBgw4EvY7/zenyg +D/9wEJBG9UuCJ5D7ggw4+9KFmtjhn0N7KSMnVGmhJ+SGIdO4/WB6Y0Lq+/5kw8dem7ZwVqcF +vcUYjJdIXF0p0wBiC6M1bM4RVxblaRYuT0ha8gM5pbw3LxWJWC3kIIrFFl87SKaXHN9iKD0o +4yzl1r37jfrg5OCMlmXJ/Wk1iCiwCdL6VGL3jYanzjkVFBh7w7N4qmvvGFaWP6MLXY3CubTn +abtEkVxhagPgxJLSb+rGmCD/FX/eMs4gwMOaXVqy3pweH58zaa8mmM6Q4yCY8tioxa2059Cb +9JsVQUb7WHAUWnU22tRVymbzt2mJYJ31lhlBb9/72JfOYEX6f5qgvyRP9fcuwkkfv2DEiecP +ZskDGaIFPzvTpwJRzM0AqoY69ew4A0LLDmM/yc/BkzR8vO0mL2qa6MrDxVTuOYZYiT0nI38R +CPIfXJbAZFfQXKu1RR8DEGRjDKMW9jjXU3juMlLv+f36BpecqAS8VxRmgYB+B1wBOcQCi/1H +CXee/C6BLjodqFziWhFl8ow7PQiR6HO31Xsg7dfDRXUiaHVpmKZEDZkuThg1LNoT3P6Kc8Lz +WOLEQ5mWXbKUkLFUtFFdLpwYUdH/Rj7Zpa2QosWhEfPRn2EKK9QydvF3gexlXdQXMdA34ocC ++X4QfkaIzYSKBpLpl3A1B+uGweBgvHBsk7UkX4kCHAQTAQIABgUCUH7uGgAKCRAfNlZlF5Ps +J7MEEACK3zUK9n8KglV8hj0wKSb8RgNaXrvM4Q9rbCsqp/gXQcnkcnXv4NKsK/vFb3UD5hIr +DfSyVX44pepIB78WbxXmTMsDXNHs7BWyn3BEB65MuEwT2tzwqQ4I5pRqT6qPW8s4dOTuRxPL +mtpKYYolBJvWtt9ETXQIjerHoO61QORVi1EzWoc/ZWdT5IeEvijl99eMQP2tZ2yzj67H4b9w +W5K1yB3OGtxcNUFYk46DnGbVwn15IVlUFLrOn60xbM4gAeoAA7NiBJmrpVl2Cj5L4/B0Qc4o +FE5jhMxRS1jKeq7psJjxR1OzJL2I5gB6dP9754utc3MNl3bymHuRQ3btPul4C0ZW7s9hEMd0 +j39KzDzMdUzn7aU/V6WHw8bLzM4J+g/kewOHz7QSNp3PmfmOo0uA0Cv5IQIgwUxIBcStHm9N +h9qVsVy1naU8nO7QdObJW2RZNJkESh0Fmm3OBF1Rfq730v5kweabxRh1ZzhVsmN3wBlmEHlG +M2KM1I5DJpMKDFnmfNIDMhVjsFZhzCATuMaW2nayXmjGCjglG9LKRKdAjC8ZEn/jmatQ3ei9 +v91u2naO2B3zYyb35pPLkcLF7trcD+6PnuzGUmnmidclXCwzbvRqfNJd1XozqeUyvbmL2LFb +rdjkmceSwvcRCwzsX8+IfxVvEqYmUQnRbGHuAvb42YkCHAQTAQIABgUCUMt/9QAKCRCQWneb +POFgwBiEEACuqHpnQBmh/Ju1CBi5HLtFgkIA42ZgWddyT9SsubjZWUGO0YFDvWkIdG5RPA8b +6R+g+JH4l9kPswR9rQz6jTndE/yKSHnVFsGFOtM9/k97+lOVe0vsV8ajyePuU2ak7ggvbU9S +j+2hRMnd9D8NmfMzWmkxCzmrIAIKuX9kc7rRnS+ZnZR7660aUltLn1usKLxeuLmsFiGBwYz7 +eMONeLOf5qQHOlglURmwnB8+o/xnPLjfyeeKPqrXy6Zj3oigt1RRV5lvcdSBw867F1HRzKKE +6Md5pWEIuMTTZ5T0ZkWTZl78xI/KmLeZGTz1J3aEEj8SQF2hPUa8PxqSeMzsPuM4q4zEfO3L +1ekVEWp19uMLpKPqQJytep5HbtRXTnRVyg5nIlvEJ3OjhmZlQq5pQNq9WznYt79a3uOUgzJ8 +JVdBzHbsszBC23078gnGAp/XY7OZvis3C8Gul1TB+kRtyH84gw4k9miY9t4Bpbrr8IEZ9OdX +fZYdVsTEbLJRFIBwwKmKrLsGCsFE9B5M6DMC3UpJpXMKF1tvHcrRB1Q9m/3Q10ZQhKNxQTti +sYPma3UgNAd7yvEizaR8VnKMdTnd1k7P7CWUB2/nBCKqLZNgHCis2OLoXX5kHweXHTQY3pei +HEDtkpPCb8TSHFgGPX/7uPd7b/uoTwGmZEMDRrib1Fb+BokCHAQTAQIABgUCUZtODgAKCRBE ++e56dhTPhAEbD/4/0q2LX7ncAV+4l2GSbYHF+2mNNp62DKbBMZZ4b9omM4UvWPwIznRsMCiU +V6GGrgjhKg42RYM3I2HwnOgstLgm3worppDWyeormfiOn0R92Sn7y7lQCbGTVChRjmJ+SDkb +/gtdiqUR8iChh9jK8uWrmh/BWdbdI3qUpy4dWAV6q1m7SnpQLvYWf4XfVAgF52HF/JNDtd+k +sGiWXlzq4nh9Vcvy4BC0Au4LFbC+4ZRjDMUxSLqDHpLzHB9bsldPSnLAfRx6U7Ty/niVrZGc +GoFGA/eX9SYkwwR/y1GsZ9Yks+cKIQnn2RQ66uD60IitTQ7J9U8WGNo5XV3sxKHxegPo5C/E +A2MAbOp1mnc0TbQ9sJD9Q+dq5o7+Cj7R+Dc4o96KAluqYPYlyAYPFQUnqUBYKpR4hytbVY4+ +9CQ97T26oy5+GuKyfcMnZm6XfrV7c9sgt+ma77zel6c3Y867JoemTa3oqNpi5A1DRMwqziwS +uMfpjPJ+m8NjsuUqDsiXRo1TdCGLHkoZjC08wpSP2ceJbQ6sXzg9umvWPkpRO/wj9tV5Rl7k +ZoQ9JhSOelhLBgx2sf+iLiIQCvqzZhWtZT4ONlVoy6S71et5vopwYTl46ST1RpY35ttUEYSN +Cn+H5Bh1Q+xC1849VGecVFB5j0CENCdaaQ0WuqFEBTkbpfRjb4kCHAQTAQIABgUCUcRUwgAK +CRBBokiV7lbwKkq1D/9pxiaJwluRwEn4bKL1AnyXYnvprv7sv9IHWG6JE34fe1+/AgIoPmXN +zIDL3Le2HMGb3txJR8z1PQCsNKu2wMFTBn2e+g3JPsaPzfRJcthf7wnZuVFiMdv/4MOjteZy +Kw9ncrbhp5ENDM/xn3d9PMUvao2AFqAzZU4yvmQ6M5UPMC+Dk9WMWbW22jE1KtCuEKPxUMlh +kjoeKtLN59S5NXGhx2S36/pxwOqTuEjFLJxdSnC774eZHQweV/+4SXjV1BLDQQuig5f+HOsl +E/Uw8ysqvPMUJJBc0t4feyChbiuuQI5/Ad5ntDyMf9klYRUOkkTguBkKsa2fVVAWChGw36ZM +s5cOtubIBb+03A+ANCzjsQOtnRexecGjUj7ezfGdXeAmQ/vfqS1EX4vsvgCW6p2CmD/lhxAm +4MFEMNKUyjLXAjbcBRc3TQ122YW+atrMiviAE6K+AhtNHEf1mHuGbtXEgoU35yKGanfjxWMJ +7A+cS/JrTgXlkqtu0MGwSf5ZSLrdlv6im9zpaqAgYKMQAYMDn0YeJRCk8y+0LyQlVF2EcEtD +7WJD+VbFsCG04wyYhPjbo0fxtPjG/49c/r5CGfL1OD9iKS4iviW18sH4Nxat3zpvELww9KP7 +PN+PJVtO09xWPZqvMvokPKdQihHf1l9iAeScKYXmInTF8B3L5P+BeYkCHAQTAQIABgUCUcck +qwAKCRD+ITon1w8NvG8pD/4xzFvWi+tFan2oGcJDV4HpIaS6uJdIHDY0gnyf8cbHadYB9iWq +H4vGHAVmF3xQLedl9nmAMEerk0zCDyby6Ea4ABqFYrgWs+HqSTrfkCsPBhqlFbyO5kSNlAH+ +RDF6Xup2xlnPjRLIzvyJXD8a7afe2gVuOJ1LpmiDxACPbdkw7xRv4FvtiXKJZjBLsZmSJxS0 +WZjDHAPAlneOPSJ3iIJ7ZeM4YQDPt7cXfFEVYmH5I3UpE7jI1YwCzY0gbeb5Y8PXYRqM++oR +Fxx5RqliiwKgfp/Jh/zC6fDlco22tp2QfJ8Ing9eUQhyKvC1opPiCSfwm8+y+yAZin9on71z +ZNM3R9YJwcyJby4tg/MA69VpzeB2JdsefibEGgjVgYJKtmPpoc46NoAk8PB5y792XCLThVew +7VzhAXsFaPOFLFM+iVlmHuikRrrcmtdntzKsVUyxzgFYr8PbdAaW7M8hKkSamHNQtKSZYoBH +yq0VCKdjVEGCDmPLlHLFRZP8bPN8z9mO70fGEzwVa7nbJU7vvcMDtnWsWqKD63zaOPbAtoOE +XHlsdDVm2gcFI8X5irDXtaR51acblQGxNU2on12JgSZsx1zD3USn890QhTvR5kDMfaYgzTAi +x8mw2pjZefhaWSssFc2kn4y1EQtZQF+XpsWf/fvbEVyeEBWGoPTVSk55rokCHAQTAQgABgUC +UI/f0wAKCRA5sU1qmXLUhiV4D/9gkJdBsB08JD1iUaevlPw42StbKnCL+W45eJ3smJ4w7Ksi +4Bj2mQrRH4LqunOU298gRS3qTGu6cYrGQiK/xPLzXv8Fc77DFbhwY4W5Nw49gP5azcCJCxlU +7KaROpFC7N1vpT8h2gnt6Vw1mbJX09oZ+XWnI3sLMYlQUllEpqOwYKmWqR271lYYQp8UhSZV +D81Jta+Vg3nlBmhoPjjISt6eQ19NwI9kgO1B32zn50wBqhehH58ZacFVwz+EKeRkBA/vEJuX +VdXU/Dz4nkKtkUe2tB51G7Z2zo+gKhx+aZjGB+QsbJLe5n5uRiD/G0K1mU5F4reS40tDHZjf +ts0EncXiibnw6OPOxSGaG5LUI84F92cCMdpBmsGnrB81Yd6PRVGVA7FN82MQdCcdxPaK8GJa +Vh4+ZgeqO4B7un+9sY7WyyOmzmZoT3Dp4Jn9v+fRaI3LyETiR1r7uOopgA1u6qIBxhfxh/bG +2rt1WWhNwsmFKP2eZhUJLdqJL3gtXgb/6PMvppWc1bYrYvvBfmAAItM6SNMXNAi6USdl3dnW ++v7mW00xW5cBLvyxU4uK+cePDK1/Wr/DVCxf12PvSSfgM264y0DslyGtgPvr5VuL50e5dBwX +NC+jpxDcEypdREyhJiRwTt4DsubIFyizsxvQLSZ9olay9QFcUk7er7OatwRXr4kCHAQTAQoA +BgUCUTD4mwAKCRDHfUWn4oNGK4Q6EACAriO2inJpJREu31qaIYpZRFdz5UP+D3CKA6w6UCrw +C0eU8BVsQyiGwzii+ls9zYubpEu99YRI73Gbj1D9UVUmEH2bIbWWSyci7949rC7Qg3oIlAUh ++HuM5T+d8BeyS6hWrPRh5fe4bXP2OkkK4fzzcCWLKkttk9V7XQoxTxCh674oe0IIWI1VHeNT +/dx5yCj2ZlOcHr212W++EI0bzI9reHlcNJ0xyXg/QoV5hC6kcUBrgCHga6V7uhjZWhfKJBz2 +/jf6ZAtvHAppjDpGZFjuvIPNnRq+dnUmeRX+h/Yu9eikpEoFYhe96E48Bmy1dc6qOr6MJ0Ql +wt9IUW7ymv8++IMhw8fjG5B5IVV5lfTUKJBkJ1HRPQOMDW+U6ZDfwnHjs1hfh+nHfnHXfXKO +0S0eUCWCWKho2bRfs4sJpHfuAPDPyj9IQ9jczjuMgX5MUJy9JFEWBmRde2cWlU/5ElKLGxDy +ItgkkpU/ciJUGhuUwQXPYokifhDAGU3EydaAU96PC8vP3JCkcDu85zjYKP1AQ3yvPFsXZrHZ +udwIx/hNdOq0d2DssWML5Nvt0ioNUV3oyHmsyIIFDAWi11RQO99C89naNKmiwEex9RNOUEbA +ZnvPxCly7Htxuulbfn/jzSRZQSiQ8HyxsqbwlDNCwHaAtg1Bu+H1orJVAVVHJATmKYkCHAQT +AQoABgUCUf5+9gAKCRAgCYIIDXZWpw0sEACCAP7LJJRZCWB2+pbnsGSJyjRsAeFtQcy0lA+w +hjdnSM5HJ0D6ngUZLz1Hfs98X65E3PTKMStG2c/rjHFwEA8KArnPZlPV+gDlA/4JWDAmKyhK +TAIBXkXELk1n6t3CI1bnZVlVc7928s4/H+4lMPlvOgiMfZo3tv43Dp90ch2gEo/TDqfXlBKy +nLAAA5c/ytxLQFFGvxpiADfG65XSUCgy1SDldYXHF8ka7wbVhmrXLD999AwCOI5kF+dRKkA9 +xq0VV2S3EP270K4y11hMcEtfeYCOcj6nVHfOxlz1+vO8Nntm9MH424vRhLLrK8w3n7FYGHgz +Bw7uTg9/iFg/cA7Y+53aBF3qTrMecmr3GnsiokaXDvIGBS9X3YRGKbmAS3BYcCqtg5Ez2Hyv +z2ANUjp7BHQVfXe68nUjxrBtguAcZ+l7l1fdQCPG14pqaT5mdwsB4NfyADqYucmh2wHliCSS +SZTINYkHi7Jm5n2Vqw2GVi0pksySxE88h2TSCTtxBRCG66Pbh99Q21rXUl/Xusj9oA8jn2d5 +lQ9Z9Se87t5MxYJm2zm04/In9gkxNQXPz4LoHa695UEbbxqaOH4t4DJNVCdQ3tV0rHwWTNMF +vGFe8vPkDe1QDQ6PRdaQgohfHgE/fFJ3tcmax7i6vLHnxsE3hJYR2cfqb7+BxomSlgd0oIkC +HwQQAQIACQUCThmWGwIHAAAKCRBbnqFhZpDPlFcaD/9ALWr4RBlkWrB7FraK4aD+tTcN4KnI +WTaKBYC8fJlkYcZ9Q+Skmb+jCU4UwYC0ku5Y+10Ntfc7UgbVWyx8d9qSvOLk0g3hyfN9l5wV +3Onjcx4wpxB8YgUtHMUh1IM2Lm/sRtSmwzZR5K6oG5VNCr6j6l8f2lhVuzLe+KCGacdo9eTh +y4gRDDzm2mJEflca1dT9MbHRoRXsBxPZpvWklxNqqdPA+0SqgYZE0BhaNuvMR+9C0kws6XHK +CHTfmxoHZ3c90z4hHh6+fcrrWqjfs4ThVfEk3iCfmGKwlEcpopCXGWWs28sJ02aqTFB/1QwP +q3iNhK8dwh8NdCUVtmBdrlyQofkHDJ1BkTfksNmGdAjgK0qAJzjVn58ysXnNtpur/beRZNyv +S2RPPsJaEKIo/lcm6Cd9aBkdF57J0wqesT+mt9N4S0DGZem5z89QgjZ+y3STWbqtgW3hTLSA ++xpW1EvgLM8c2YzllEcQHjrjef17A6nNw5z3NyyrASUQix81DTw9VsilKFlvkExsqYkhkgi5 +zjbF00SZMQ957oNoPou080AS/rQruVLYpEOA1UooJnMGIEL04x9YUVTCkFMj5DK0baGKISfL +Zr6no0jgQloFHCd1yrX363Dr3+/CNYRoMXF3Q5UexrtsDyItcxdPbGdsuR0cwKft4elYf1rp +03Zee4kCIgQQAQIADAUCT1NicAWDB4YfgAAKCRDIEwoLMBSAmmohD/9Db9oLiDQogn0PspeI +/nUlQEmKk2Qwt43YyuFiAifNMqPW9st/sQIXh78MTqsHFNGdIR2rd8chTWem79Hb20ArNnB8 +OaGi7Jwrqs046s2G2nu+4xqGgjJoD1LaX7/Rtqufi8iOJzQDu1E2w2mpjLDee3dcsw4XmTTy +106dFDcYzlyFAVhFZHxJ5+UA1rRlF+2Azw9mM8jm4Vd0aIJlaTsuB8KPbNdxaUYUnck1K+oA +MZ7jNM8934VzXJ8iy+ATrQxk9cIMkHKFSIZZIX5dBswuQyzIIwaHwJXHOr8t35mSr3Di0PCc +7OAxYvHjFHiqNbzrfdk+PA48f5wcyubu46oQNeRsyf/fJVHDstONWNyix178t6MYF6UZkNBR +ZFMEX2tuRabat7iKkNYwaZJeZVKLVeWiP9SVDe2rAs8uiVjRJV5o7FyrZvjT8uenRqPjNz+o +fb1foQDqC8ZIrMWF4Ah8yOsjNSYGccAJk4a1BAiYG2LzNN4ssD0w0RgIjrhsdR3TjnjXLrS6 +A9p2WNY7gjCmMAZZHHo2OnEZAS+Xr2ZHrgwSRLyk8XPAuHFPoZcv6IGEbEN3pqSPtgFhCdpO +Ifx9S6yQXX0aMQ+9CHfmdOhQJS9HK+rEJeetHXJPZNgmzGnDLvgRAkczMbaFC+4Hs+KxQdWj +zbP40H8q2KWZ4M5azYkCIgQQAQIADAUCT/Rq/wWDAeKFAAAKCRBjyvJ2Psdy1GOgD/9IhIXl +066PFQ8KFkcgIULaP+W1nlSZqKbsb36a4/SYOJmq6c4BO0eMVCEYWZJYK75qyM6KfID+d/he +hHMODdWCGswBlxi21xW7wA9hgwo0NjGdA1f95skFyBKTxYtnc4DCmT/UeO/Zuu5SlECqeAMB +y/qx0PXshT9a5Gwi0cbBBnmo4txZotHPF8fjubE6L2jPBmEJcJq7YSDfQP5FAvrSP8rMXCy1 +DEpU5Z3GRjNIW4fvubMjbSuPOWEzKoVnjdI8r7zT2w9Gry7xn2Wqxof7vxQZneh3Qqj3Bfhv +IdXpdznDra9mLp5v90Asv9kMi0dCPvjFzeB9KboBMUWfbpVD66egCRo9wl1/bpPKMbZS3Dan +dMgTXDdLnvheu7gqXrpqnEWziySbKbDra50ioIJ3WU+ea+aojOlwRvXAQoyYlSPNDOdHRhp+ +ALSqlgHhrHIRWJTCpkEQzRWbOkId6xuP4+gIQcUIJVqAmSTTfg3TJP7BEjuWwxdxNQh/ZVG4 +rhUGv3DGiywLtze0bC3hxtIybjHA+ATUFfsF5UdsJ7avXZXOGnb8dbOkKWGQ1NDcscI9L6gh +k9/PGRDJ/OLQEQQjyBVVBHLBFlQa+HVNL+tLt8KOM7/R5lacpuF9QpGGhtXZ3EXZJYjcQuPp +X+hh9ACX1sUQJqABIAwZAJtwSIIULokCIgQQAQIADAUCUFfqvQWDB4YfgAAKCRC/3GXuhFEF +vlQcEACdCJm4OMKUBmzeJzzE72OvOdEVFuC9C3sFFjYMHWibevGVz0M/jFTFmGQnMm9s6BiD +LSTR2o48n2tD0fyGhrQC4bf1LFfzrvCX+xvmalZFca9r7HIx0Qxh5JMFPGm0lR+DSVZsLVil +0TpanSZ34hIs3Y/HQKj9tjMHw5pGnEU8VrgVqze1YDLaZ78nk60YmI7PZeKBz/6Iy2tsmR/I +kC/HccZiSl9BcwRFznVJwSL1GimKArdvCF1d/afnDsemdgy2vzzfxBSU3urSqA86wmmIuSi/ +i7epZXqZPyIh6Q5Fcf1QMpJsFWU2q3IDZrwid6sqi0UmelMfxOXcLR2M20wal9Bsr8Ov4zVS +0SxrV2p33RlJ/tDm2+rEJj9UY3RdSUCKkto6wtbqwV9/4keim5E8Do45XYaJZ0A7H1pV/dOY +eoeuAT+AFV2NX7YP5OdnCk4HvfWoAPDuh/ceIvCHrdk+HT64nGhyI+gOiEZX4aefPh2W9y4J +ih74TJm98RpWx+ug5uNTScmHkAoCa49gf+f76+DRz8eh39GUqBLJRHodMynC8i4hPJ0gknl3 +xusxs3N3j8cBugA4Y7sU43JLUZybMJLg0FAJj9fMVxGfNZ9rRC091hQLPfezsNmw+dhnmt7q +pA0sSR1wA2hnl7U7v4fYS1tVwpSmEijjv7gwhu9l2YkCIgQQAQoADAUCUdXU9gWDB4YfgAAK +CRCUU0gQ8sMPSVK7D/4h2fiRLsDr7OkKnBGzBjEDKzTZxeiwwgkeekbrvf3Nfg1u80pNJdR+ +fNn6gOVNJN1qAhU/TOqNZzqvBY1ol9+OL2u4S2UZ0HoYumy/Cldt16oAlIBnxusmYZWzbnd4 +lU20ggVGmunPsFiiHer16alxnyGeEZh+zyYSKDILvYmfs0jjl7jKGp7Xj3ZwgLWdAsoL3EGn ++6OP+vaXXiPoHImfkhZG1GtmHCVfvt7SzRvFhv9ENr0duqkQyigbGNizBc05J4Q7pHiSHvxT +zVwxO0h7C1quilzteU/a3K2G3AjKNcN1dYHKffT8eFmFOMBE3XLSpki6hFUn6PdunDsdO0ES +4sq0cj26do8ojnRBMli47Mt6pzy8mu+fP22EiJxLlOrYb2gHYkZdH975x3aZZH3PEtQiVfhE +hEsPJo1lQXCVSgEFVq8X5f5n1bhx3DHVDCY0WU0ycor08hB+Vg7thmPLW8Upov4HC3j69QQD +Xm29oqY5t6mLl+Zs+s9Eh/187q33FS6KHiHmoQHLERBzH5A9bg6jA4FUvuewdSr6ljoNzuWq +vUEe80Pb6BkiltS6bltymi39XMfXGlVIluY3QFr8QQj1nM7rceS0+6tbMDfcwTpdH16NxxYO +4PHpnzuSEkvYog5LU5Or/wtLvPShBnYP/MX/cgJ9kvuNW46QrOBw8okCIgQRAQIADAUCUFfr +fAWDB4YfgAAKCRAKHfLGbXvrFFILD/sE3R62dwDTDg79CzxXodCjWtipkUbCia1CoQ/ObNrK +cJeXxue7Ln56CP9ouVflef+nH9cHJ4wi1PuMdRMZ6zWhn+h2vItfV4+VRaXMQHNmF3z/RCun +CTo50+oI+rVKDoRqHNX2YuH5Ei3dCMsvKW1R3lbfyGZ3B6eg4mMXrjnJxk1lzb8+SoLkXPOc +hJlqZlzaEMYAfEVSONrIU3K/RKQYdRZPnFV5l3xjwajuozsD/WBwuVZ0ncLRamZoaQknxwJo +fWyHa4E9C5OpXEb4OEyAm3Ek4qf+dswWwGKczZyGL42XNvJG1EsavaZkYozfpzqJijSLh0ap +uq6a2EWOHEtSwmTS4xUuahs7pWBz/851BMCjW3X34nln5VAqaEJ/i8MfQ0xGCZ1fOEi6XtYK +Xy8ErhhgkoBf9hRc0vuyIqgQoiC9B5SX1GYS+flvE00CfJWvmhDK5AIysv4qDwsXXsqjKSRZ +OyeooNiegjPjgqOd69wBi4Rk9aEIubVC/h4fLQQmQwsheyPcFju9MW9mx5hW24D7ZCUdyl+j +gtCxhFtgBFksictk3Mn7PGGPhtScoi4ac3iFE/izELA1gQLu5GvXl7a90nzVtnWd2rDxRBkN +dxN8yEnIZR9onzA4mcUguL/klj0zrCCODGYrjyJdP/RYPCZz0EIkKTFOZ8XL+3LmwYkCIgQS +AQIADAUCUgkJcwWDB4YfgAAKCRAvmEp4jzEI0BkVD/9u63n1MVvgiHuu+VIwgBjMKjVbX0St +WlOi8wYLz40i2X4/4CoFrKQdqCHwWafIDhwu8hFQE5ozQX2y4gO7iNP94v3t33oZ3acIcamh +SC10Km5aQrbxy0iKvByssLyDdMEYa11Ypmz8lQ53nVxymxgrwVed+79+P/z1c7ET/P15WlVM +oLheCziuYtwB1ng7QZM2xwx/4tSPi4EG7cetP2fb0NbEk0/qWC4/KP2xIGQ8incsbqTp/N9l +m+7D+SBCoIB8yfUgYjal1sE1isEefbyOdgsbZ/pHUnGE2HZ9IzHwjh2d2mL92tWM1OAwqgDs +fYBO/NPvjCBof1Y5BNqwkMntJSbGovlQlohHd+66y0h8XRSrQ9wmoqib06bG+z8ogHvRI+Q0 +oJffwL++W9CAIk3iiOnGa0nkapEnTH/KiaCZ0fCWv0WMye4oIJSux50IkG23TAvFWncZE/QI +xq/ARkUAb4jrRwmxJdkJuPVZ6KYazfkwA3V97vGI8rr84A7lJjcWiuoDzmtzdyiy2mV5VjVg +2UFDo5XBGSweHBq/yzlKfX1WdiHrMHOp6l/CW+cbOQk/BkHt318zD0NQnyCKoC4yCKciE24P +SukOWPbaCq1fK1m15hBfIx6+giM2ljjVaYWx3sc6him4P7OW6ySq4bxoyRS4E+5uljAVrAAQ +cEol34kCIgQTAQIADAUCUX55wwWDB4YfgAAKCRBn5y/rrqN+8G2KD/4sOIavZORCTxQNXg+K +2ef/CqgrSCSGuINEutBCoPv+8zMzr32V/JC/WtAfjNKh2LGgpEIhPp1dPZaqgTB2+wZIcHKu +R6ajvulhWMRKWVs/SGslZnbEXA2mhg4kRr++xjKQASH045EnwTyGSsiVZkduGMff9S7o9vJg +T8LH9n/Tv/n7VinZ+dFhIQXweIu9eo0c811KCK9INckrl1Tq7Ch/6mlh3QQ/AeGwVUScSZb0 +PPV3EN3qLrRhfaOzZBf6RPVVQydbVAt+6trmibHAGeiiWLS4P1YqGm2WEm7GOvXC+mFbz4l0 +0cIQxvMIv+Ef+kmFJ0OalWHY8i5pMiwA6RRJDNhFOgXWIUEVuwRcNzOUQNmGhl66Iym8jNU8 +xP6WFUtp+B//paUJlfD8f38MU4Gg6t/m1VhkzeuadJjsWOkQVEQsC3bdI6p+hcbnp/DZkhFJ +ShLVZfpzDN9omGOYRSuSGz+VaWVB2l1i4HX/3mg73PE3hMKfNV6LQqForl/hBFtwwlaPo5P9 +ELVD4xXpHzQ4E43Q4Yf0Z8FWE/q8+1e2xXXly+pUb0LB8GY3cj51hyNgMgwPIMKBB2gVt01/ +TU/aM9p6x8XyEkHYDdFLkwK0R1QGVIn9LCAa0jn3nESjO6SCkVBg+EvTQF+uJAX/xaw3MCK4 +WybO/wrSLj+tiLqjDYkCIgQTAQIADAUCUhURjgWDFpJegAAKCRCRndSpl+qPrfX+D/9e0tPe +tPj80FQb/qafoazRSFE6VM3KIkREZW/9uYAKtyZRQmcTT2184d3X87Ybf3yvi8xg/O5Vk17D +feHyHjtexsc0XegWW6L1pRSRGCdCAH1UYquQ/+tzSEqCwuaMMl6B9T+XCDg0+/RDesPPp19T +lK0MX5gewCB0h8+KND0G9JsE0vUSO5T6z6nYmLkkZ1pbSkyX4UfYGOorBhiak49S9MHhdGeR +7MTKuos8LkNi79WSK+/2dOnWbd7lopyXESVR1JhoFTs3PZb6rdsPYYL/MSbIKg7Oy63feCCs +f4fDqEBpsP06clwJzCq2gVG9M5Iyb4wj2CkZ37qdGP+AmJ/W5ayhxntsNVzI5NSOfv5ITnV0 +ITOfsUeRyTOgnKD7eXpkxmjHebqDj8ItrYhd+gT3pB9Byt0ozlNq/lxS21x9AHsUED1v0dI/ +vRrEsOTlGpMASdh96LItf2RS1XwT8X61X/TMdeb1j3ZrL0xdoa1J5U04FcwlBSk+OANY6ujr +L8SSQyL1rHzBeI2Rq2UdycC+ScjHqAuhWif8ISydf7k/lKtIKcv7I3s2THnGEEds4Yakfz+h +56j7IyQfLF2Be9VHaiW+5H44bY0NiM4ILkCQWskPFM++cYRjGO99t2YmttM3KKDkceJS9Qvj +9ohB/B2TEd+xNSdlzRjU7MvS0E3yp4kCPgQwAQIAKAUCTOVYgiEdAENhbiBjb21wcm9taXNl +IG15IHRydXN0IG5ldHdvcmsACgkQ1mFZmoCpbEgkAg//VcErsn1CvXoXQiPvA0XbvTynXAlc +1c5eFYbfbb3XLE8r5571KzoQanRbA/qeblLp/zn/zYOG+xWSca9BwzZ29rIgs9eBWSpbOc9t +cYfIOz/OmygYI/stap3yrTHqkbNYUtLip6eli88Hq5JtiOBxAeiOb5UmxJKocFXR6k76J5t9 +kRjYNrNgY67JDIFwWePcPCiVgrN0LlDemgGrZdkG9eA/Ar7tpdD2tu2xF93vApNhJtqfc5m2 +xfDLGQqFVNUjHRLQqWBzOboGDvCc9PdHu533qYx2Qv0P3VXAhGkaVFk/mdhvx1ovgJhkl3L3 +cMpFqlnSRvfjSe4SQ2qn8fx2tU8qLCo35V9/Q+njBPepK6/Z3lbGDMaqx9NQL1dyMFcfu09C +nTBr00mCDqPigBIqe4Ei2ylRoyXQ80SmJuCdhtxOE9DHUhFQcOhdBDAiqHmjMi+bw2jQR2FG +eOZSRw31PD4xZ9fRkbBzpnwV2nZHcYZvMre1pg1F37QaI0NhQYcON7tgRAy+uBNPJ3l0eQXH +mANjKT+jFBMDlhAsKe02kjH3UYDwgMC7hypMaL23KsLJM5fQmMVlfyWllRYM6bh5KZhHs4Iu +5gDLmL2KUm280z8xUWxCXMzUAAEHbe9PLpNRtCX5gEAON/dgUSeVonZupcAH6aJtO6NVq/XD +vmke3NOJBhwEEwEIAAYFAk72OPsACgkQJJv37rGBfaBPUTAAx6cMClfC2TTmGtUnrwczvL0U +s44JfNHM2gFHVSjRZdnD9LzXCpq6sJJKZLMaoOJoccUAD3bN3bjPWOA9RwRn+8TeD+RwAFiU +CIdK6A7STBB/F2xFjwdxh3wyWgd2HSpjF9TZDQ7K7UvThOQP5LhyI2oV//FxmLZLCnEKHIl/ +EcndV/GlaTwqotnuzokAti+BarKqBJ83PrgZ80rRqNclKqmxfG/YbwyeW5ozgRRlevy33zkX +Hjq6jXEHVE2X7FC1gAOy5QbEIzpEJg3uwLOQ9bGRFqSaZUGBD+V2wlgPY90J5EZS1NqHRxDY +ZPuWkaL90WyPFk2ALhGKuBpz3Rks61r2h9Ihxz/gaprLhhAUFH6iTW81UJXAq/nWznxbvgRe +0ZYDvPjcRJ1z1txlP2i8YsaBCeCHe6b8L6zJ3oOEPaR3sneh5peEmsuhnz3DkHSM9EYaGfMe +x161FG/X/wTi2xVQuOE3pHcZFDhgzU6edRRtdV0t8JkLSDRFuCfpXe/z6Mnh128/MpCG2iaf +TJmxv+f2Z7wtbX2ct5vVGZSo2P0kIRjlZzbsnbf+DB6caFTFkYGxYKBGJ8sjmxK/ViNIu7QR +56iyyXsLGE+Nzg4mEVk4EoHbAzggkkcqqoJVE4fyZ2KkRECs9CJdJcM7xNF8uKWk7NBMvWxa +LCSm2915Euvfx4ssfCuoyx0es2Tf7P8ExqPqLbybTqsp0yYb1x81b0i46UB91VrBCq6q3uYk +A545UiL1MGLUBIBHx4YX5Sh+qssg8grBL2EFeRA15lEtuykb+r6Bg1Ki6rp7wlWQRZhka3+o +LBxHFbCC18SpaJJCsIp4gnTgBYU9N8ryMcQUIm7Cwi8N4brP864ZQgdcbSifn0oCl9kNUfgn +usew/h+VOFidlNfZ1b3xBqQehoAZkjegbWH4WBA9ucZwpsV9x+C2Nzta1ihMxpZFT05Q7VkQ +ZbWKNzCMsHmSVq0DN+V9xuk9qjnDeS+qYGQUfv/v2goXakgSlakVxoIYYpBg+K5eX4M41QRB +hYmIXoJM/t6dyzFCyrNnHjcTGVusNFUc5i/R+AqS0I7mxCXQt2AGast71CDatONHeVNBAWTw +zWpc5NmpBEs7WYCVRpNBErrsOLI/PiB5V9lE74Do7vEyAuEL2oP6ddt/eB9zC+72TQBWGhXz +2Z0iSovyMHIfocqNlLZa/nlfif6uJMV85dusrenqyGTny0VLVsUNOBLT/0N6Alt0ENr1DkLa +5xrEIK2Keso04RBbZLTJ7N5YqSsCFgkJlqt8t9eiEcZGA127J6ntGtyNHK9f7MPDL64NdH5/ +vOUUT4DtpijkmkjB4Aaw7KPWc//3UXPTaxF45GD04su18cGOev4PLOfjOkBQak8htagE+siO +Rn7gQZP+WXt1UJ0HVgQLaizZWX2YEDPMGfNwHP3xo3joiaT+AO84nM3lOO8HPfsz/H7JvMoT +bocdmGVer8EKlaKmSlOvlBSelQKV/WNE9xPSsnXAHESEVqb1AAlEmMoPt0naSJpAZ5OWH132 +1qnFHe84VDIuasf3rSix2q24biEOlMPppZJGbtdrZBRR3MDxxFnqWZJo6zL+tV6aIjn8vY0a +DwnXVE9CL/AGEAazMOuyKbLq8WR6wL2GYuCrmwDfJNLcl2TfpCOpM06cAzhkoByD+Ek+2Ltu +4oNns/IEuPobljxZE5jdRhyBGqtpUnq5GBuRAQ7HM4ZFkr29k7GpOG56oug9XtsTtwbh7iIH +yG6gxQdcTWLcpx9vhWodEopEzx5Z/eYDUk05BAiL9UdzhAh0SSpkBIRQck9iMSU57MPs931R +9v0/DFTXYrDWf5FYezY0fhulLaKEDYhQ1pEoX4d1Sk7soHhsn6yp41x/oB9uMQjrcSivXutm +7nOD7LAN6Jget9AfJGFjQmMmEB45CZUrj1BoBXJ6n6rejz/RPqOWZKpZsydYxUaOP/Hp2miA +s7fSQ6wOF0qhDg9VURwu52qJjiMBv6Zfsz/mYdCXAVHQkRYhIyD94zgSuKqJ9Fzr9GmOJX9h +KVqU53q3iQgcBBABAgAGBQJDopd5AAoJEIDsrKa/hAOe6M4//0oAWI8LLS3cSIf9ZF+/Ba5L +pbvsleS5Al/acez6tJOMXKZjYkF0zdk/fW3Qjj+AcB1MlKt/+VIYxcPne4V4kGhCz/d44NfO +XV/rLoP4AaxpTVzbv63+YV/Eejbg0OZKjetpjlkbmIGoQMXzUI8fSVR2OEK8xOpzHLlv5041 +gWAD+wG8LFygPcgRyzMrpjeB0LcSEnyIaKEKRwZKo7H4zShQ2dYr4U7YaVEKzJYpsq8pqBwn +fTuzbdB5F2iNmbQEaNJtOgro6OmcKULUMHpmUjRyazcyPc6MpbvDDKElzLs4r6KF1mhlEMCd +ToWJ6K2pUJ8C3dy+3w2YDtSSzEicbX/CnWZOmR80dEcvhbidu5DaMnqUdWbhwKI2aZNHoxSh +SIuc0RrRvwUWL0DMzPHqLwN7BlRdGEnFjcsmcySEkxQtG4uGTRjkiXuAZ5i7+F+bnjqkah+u +bCN5vKnKq5WU6MRKcRYemx3WgyoScxuLbepfjzAcEU2WkodueawrrlhF8XqlyMmdtZWA5xxU +fHIWwK40FZwcK6piuX8hAYuPxutC4+FrWU5o6zwflZB8n7r0+BffdHYcTdjNW2oAlKNt7Oyl +qss6I08YbucKGiQv1ysUj936Bwi/2NcdZpoJ6244XEYlpIKD8nI6XxfI4mEX5Yl28pKq7xvV +rWd91ugda/dDXh/ih1/wz9xeNxHCAGL/h4Ckv2STiQx+Q9F5oyo6iNAhlhW1Lej/RdJ5CNgm +C//roJHuFhxnX8OZkVyYH3DQ67bKotZskiqJuQAhVlST1gQZg8zKMHQWKoCemlm7td5wjRrc +DgFMvjbfZ29KYrN+VPjzItE/vcVFUaNVwQ8Kc/LwMyMPzgSUBBQAyUfk17H5rUA1tqUknoP/ +0J00lzoNvk76r+A3C9Sh6TU+aGFioZjIIbScfBORMW6UxQPEd8MsB6oHXT2k9s6+vhRrZufF +FILY7I/DG4dm4fjvXcT0OkWIx/vRjmDrp5ZSprWOZtKqQicQvj0AVTpYByLlIWk6Oot2TWk0 +5Lw9aXSwnuYK4K+8RLK6z7XZDz98Z6az8/e5r3Nv14diDL9o+bxauC3pk/IXwJj8O3KUa6IX +wWtNpQJNQatmk313yMxuoCGULYtwGfcHBwAx7ZDF5bn2SAPhgm1EVxC5TkDR4pglWkl0wsLY +qNc2hoy4yRECigGf4pBbeAtXVCD3rgAlFmFs1PJTKMKG+UTa6pCi83X1Ip5YCFnWzskspQEK +joGV02gg5crBo+2Pr39YNvw/UusA4Io8Wlgv2HJolgZZdfuZEzYndYy6I8FFQpSPqa9J6kB5 +6yv1kP9c3nmkBZMzpDsHJDTmPFtjbn0WHZ+fDf3R0B/kTA6tbDeoTYJub3je43+q/kq+dgpy +7K0eSMjaheKO8ZOGZSJQbQm1+E59V0cFFpwSqdKL4kFZK9Fd/P+nsPgxFguk4+mMznMCNsXS +T/X0Bz/+WFl2vslWVfAH5InsVaTy6NuiUwylh75X7Tbj70eQ+FgUvA0veipvEMBSTvY+aj+0 +0kZEAGe6i3CzG994dyrRpiun5EBpx0kXDFPMDRzBZrvDNGfxfowRg8SGp9pEyEMkxjVMN6Tf +aZQil8b/7+p3iBlV7MKyJny5In2ix4WVbvOMHR4KtyyKtGnZg3Mo3CeAcXBUuy4K6THs9AkU +brTfAo3VBdGI026dbsXCsN9Gi1JKx9ZsUnINkaLYn9vG3kECVmJmkbMF18+ibEJLoCmnES7V +lZ7u4tuNFL9lZAGb8+bMbd5Sq16sBpUrWXd6fwR+nLVDYzXSkZEQugr5H2TBXTAKTzm4LdPK +IgStIo2GQWHyyFz+QH1uHixEbvD51VpTwsQ3KMmUiDcnSl335HUKMgB+Db6b1KeuRg3D7bgr +CbG1A9rT1vcbDF7t7w0xuzCyIrHgW2Lo+WdOO1aHg759tN/6TKr/25Uvwb/njanRxJqQlEO0 +cU1sKTwIc2/+aMRprjhZsrm+UEgsXg4Fn4dYqGqxAqvg6pzjGIw141mfPuh1xF+Vdw77FomU +Vh0IwALpgAQvD/KWjkHxMTSnWa/nUoMbvs0OAHZBiHxQaCIJB4xqdjveMmYuT69ky5gLZ6Iw +oIA2xjc3w0SxdSHC3MykiXOua5Q4QpnCxaSS90mruzshk5zS1sa38pYJ2U14Xg9wdUz8xQcR +5iqR27EYWNS1A2yyN7Dcruj613xffItAs6YkdSaDKH7qKyc2W1aW0BzITscKzQOxwkCXtCE1 +iYTMKpq9sgc8lRdPjOJtmsMgyXdpBpFELozParO+CrzFJNOKTKjiix7bluPwZqVGhnbEZevA ++LA6QRBYsvrDrTK2XEjwUl5ofGmmg15oltlHsAoYqScNIDbAJyn/GZmpLuYQJMgzJaiX4QC7 +wgdw12RJn2Zsv4pk1TTPTd1n/8Y8PwQKOnLimt1ZGpiNZOnuiZSAZo+/35O8hLVsAtBdEQIQ +QQgKsmEp5+YQx7clrxsJGDjY2VGc8E8EmzMGmlXxVlmZKFrdU8HelNCRvSXc+WIhR5eW9XBb +nNT0iTZ9AjB4eyXH43LzLHLW8lPiqXe4Rimk95Cm0jEgFefPQO+ZoFR23WmxJTcP2cyPd4Vk +o8Y8C4rU0rDbnmz/O0PUksdPS44O7MqX+gq0nGa0bZNH+xGduHbY9hx39Y9rlav13HymwJKB +Q4IEmRZC6QPmJ1mV0s9gNzj+hVp5nPROCcp1O6qdOwIIiQgcBBABAgAGBQJDopd5AAoJEIDs +rKa/hAOe6M4//0oAWI8LLS3cSIf9ZF+/Ba5LpbvsleS5Al/acez6tJOMXKZjYkF0zdk/fW3Q +jj+AcB1MlKt/+VIYxcPne4V4kGhCz/d44NfOXV/rLoP4AaxpTVzbv63+YV/Eejbg0OZKjetp +jlkbmIGoQMXzUI8fSVR2OEK8xOpzHLlv5041gWAD+wG8LFygPcgRyzMrpjeB0LcSEnyIaKEK +RwZKo7H4zShQ2dYr4U7YaVEKzJYpsq8pqBwnfTuzbdB5F2iNmbQEaNJtOgro6OmcKULUMHpm +UjRyazcyPc6MpbvDDKElzLs4r6KF1mhlEMCdToWJ6K2pUJ8C3dy+3w2YDtSSzEicbX/CnWZO +mR80dEcvhbidu5DaMnqUdWbhwKI2aZNHoxShSIuc0RrRvwUWL0DMzPHqLwN7BlRdGEnFjcsm +cySEkxQtG4uGTRjkiXuAZ5i7+F+bnjqkah+ubCN5vKnKq5WU6MRKcRYemx3WgyoScxuLbepf +jzAcEU2WkodueawrrlhF8XqlyMmdtZWA5xxUfHIWwK40FZwcK6piuX8hAYuPxutC4+FrWU5o +6zwflZB8n7r0+BffdHYcTdjNW2oAlKNt7Oylqss6I08YbucKGiQv1ysUj936Bwi/2NcdZpoJ +6244XEYlpIKD8nI6XxfI4mEX5Yl28pKq7xvVrWd91ugda/dDXh/ih1/wz9xeNxHCAGL/h4Ck +v2STiQx+Q9F5oyo6iNAhlhW1Lej/RdJ5CNgmC//roJHuFhxnX8OZkVyYH3DQ67bKotZskiqJ +uQAhVlST1gQZg8zKMHQWKoCemlm7td5wjRrcDgFMvjbfZ29KYrN+VPjzItE/vcVFUaNVwQ8K +c/LwMyMPzgSUBBQAyUfk17H5rUA1tqUknoP/0J00lzoNvk76r+A3C9Sh6TU+aGFioZjIIbSc +fBORMW6UxQPEd8MsB6oHXT2k9s6+vhRrZufFFILY7I/DG4dm4fjvXcT0OkWIx/vRjmDrp5ZS +prWOZtKqQicQvj0AVTpYByLlIWk6Oot2TWk05Lw9aXSwnuYK4K+8RLK6z7XZDz98Z6az8/e5 +r3Nv14diDL9o+bxauC3pk/IXwJj8O3KUa6IXwWtNpQJNQatmk313yMxuoCGULYtwGfcHBwAx +7ZDF5bn2SAPhgm1EVxC5TkDR4pglWkl0wsLYqNc2hoy4yRECigGf4pBbeAtXVCD3rgAlFmFs +1PJTKMKG+UTa6pCi83X1Ip5YCFnWzskspQEKjoGV02gg5crBo+2Pr39YNvw/UusA4Io8Wlgv +2HJolgZZdfuZEzYndYy6I8FFQpSPqa9J6kB56yv1kP9c3nmkBZMzpDsHJDTmPFtjbn0WHZ+f +Df3R0B/kTA6tbDeoTYJub3je43+q/kq+dgpy7K0eSMjaheKO8ZOGZSJQbQm1+E59V0cFFpwS +qdKL4kFZK9Fd/P+nsPgxFguk4+mMznMCNsXST/X0Bz/+WFl2vslWVfAH5InsVaTy6NuiUwyl +h75X7Tbj70eQ+FgUvA0veipvEMBSTvY+aj+00kZEAGe6i3CzG994dyrRpiun5EBpx0kXDFPM +DRzBZrvDNGfxfowRg8SGp9pEyEMkxjVMN6TfaZQil8b/7+p3iBlV7MKyJny5In2ix4WVbvOM +HR4KtyyKtGnZg3Mo3CeAcXBUuy4K6THs9AkUbrTfAo3VBdGI026dbsXCsN9Gi1JKx9ZsUnIN +kaLYn9vG3kECVmJmkbMF18+ibEJLoCmnES7VlZ7u4tuNFL9lZAGb8+bMbd5Sq16sBpUrWXd6 +fwR+nLVDYzXSkZEQugr5H2TBXTAKTzm4LdPKIgStIo2GQWHyyFz+QH1uHixEbvD51VpTwsQ3 +KMmUiDcnSl335HUKMgB+Db6b1KeuRg3D7bgrCbG1A9rT1vcbDF7t7w0xuzCyIrHgW2Lo+WdO +O1aHg759tN/6TKr/25Uvwb/njanRxJqQlEO0cU1sKTwIc2/+aMRprjhZsrm+UEgsXg4Fn4dY +qGqxAqvg6pzjGIw141mfPuh1xF+Vdw77FomUVh0IwALpgAQvD/KWjkHxMTSnWa/nUoMbvs0O +AHZBiHxQaCIJB4xqdjveMmYuT69ky5gLZ6IwoIA2xjc3w0SxdSHC3MykiXOua5Q4QpnCxaSS +90mruzshk5zS1sa38pYJ2U14Xg9wdUz8xQcR5iqR27EYWNS1A2yyN7Dcruj613xffItAs6Yk +dSaDKH7qKyc2W1aW0BzITscKzQOxwkCXtCE1iYTMKpq9sgc8lRdPjOJtmsMgyXdpBpFELozP +arO+CrzFJNOKTKjiix7bluPwZqVGhnbEZevA+LA6QRBYsvrDrTK2XEjwUl5ofGmmg15oltlH +sAoYqScNIDbAJyn/GZmpLuYQJMgzJaiX4QC7wgdw12RJn2Zsv4pk1TTPTd1n/8Y8PwQKOnLi +mt1ZGpiNZOnuiZSAZo+/35O8hLVsAtBdEQIQQQgKsmEp5+YQx7clrxsJGDjY2VGc8E8EmzMG +mlXxVlmZKFrdU8HelNCRvSXc+WIhR5eW9XBbnNT0iTZ9AjB4eyXH43LzLHLW8lPiqXe4Rimk +95Cm0jEgFefPQO+ZoFR23WmxJTcP2cyPd4Vko8Y8C4rU0rDbnmz/O0PUksdPS44O7MqX+gq0 +nGa0bZNH+xGduHbY9hx39Y9rlav13HymwJKBQ4IEmRZC6QPmJ1mV0s9gNzj+hVp5nPROCcp1 +O6qdO+7XiQgcBBABAgAGBQJKxQGAAAoJEHxJLFtJE2Lx4vM//jjm7GEczwCYG10l8IwyGcVW +jDe3tf86DBFbY3Kwtuuv5Wv/PDO6zZV4pklZk5uoTzRSmcf0oGfcYmbgGRSE7oXsTBvyaZbw +uNbq8FxOz5AXxb6umemdInvkdufO5U2epHDIeHdLClcIBVvNHEbWN6DMzU+vi7/yHKqwZwnG +PolLoW4yu0896ufkpGCeSqs8VbutmZp6ypY0zEpxYQNbWB50e8qPhNEWWIFFpaqX9rg6zd56 +yz429I661XTe6I9MNxCgFr6ftGxK4Md/fTEqtddBXb6d4fBv627uxrjpwGVgw7qX+kYW9Egh +g29VJheyQSQlQ/mGQffhUxi4Ut6ZBut1t5Idv9UMv9OSMtwCAVfgBZDwTDMEuf7T0tOEe78M +8658O32dhcU3AjPE+rF3jpKOSKeUcOZRUniywj+ybzrLeJLR7aKEgeJM3nUouw5R5p6Oqupx +EW2Daxk2Y8/MCkq9Jddw7ZljbWijiEOjwCQAFeRVyE5EuTVg1hK+mGyQBTIsikHjKiu2ayAP +tVth5l2QrwiMDVguj+PVAXyv81jAx7WD0p3rS0SHermwIWYlDLLHVJTjlUmB262iuOGfCNg7 +2Y3ngEOdrq++VZoc8HT0HoWgQVosDqnzPFnrJjK2sb82Nx4Fx8nOYyhiRpZO9A/hdZk8dq+c +RTiWG1gM4vnLY4yuNGh+aQsALYaV2xp7x8rOzmGgsfhhTtuRrvzIsSrfw6vpdABhYWTtcBIu +7SIBLkIAi0FUKE4bzbcDsHbgWJ/7dKHNf8bsi/3HkeeZhUsotZA7a7yYcV8qo0ryrm/OTO6N +K6XI5eo51NPTMzlI0yDb8btE7odroRNIz5USVT0lK4EGWgkvsZSndXc4F3EP8MXgpkTM3ahc +4seWME59PXhKHaiYYthgvozdE9xxh7tmsAhwBKmO+B2IALr7on9qulZGAee+0c83y4kl5l5L +xiO4GIkZmi7qfc66I0hZbNv+e1qv1f3fbsjhPKa1qCGcvJqTXGzhBeq/QAPYsmEY0Vz/8M16 +WK7O+bkb4A6U4utm9QdeOZT2itmEVD2SxUOWBm4zVX8JGxhN9DsGG6gKLQ1vO8KmZoJCFE7K +mc3/og0Q2StADf3qTdwxpgXw1OyDu0ezZ+obW0y78/Su0DlUpCGoWDCGYxcR75j3N+1MZRAu +gr+xfa/WsRb9ZvD9B4bih1+m6syHGgiiOF+f6vVScTbdbyRi2nWkYroA/t43mYUZMpfx6OGc +lI6h2IAnoVmb/07734F3qfwC9QVzizouYvcw0OQLBWxYAYwPwgfgmPxWjQhEsg81fRdU9VvE +6O2dOYUAVioLo0ORGOzqCJ9s+SHi+qg11+rbpbKeubFyz9gh4XGbgeBpJy1B9o4WS3I+uM58 +Lkm+vaqnE1XM70dS2Wl0/aHSTi8zb9fOux010IbXlbNaqorh304RMX7VNJe3hhZyxy5nVCj1 +n06Hx81gYt8crbO1+BBkSURAnJN4ubzF/v1zY478srMd6x7hSctPbGeZflJACbTZxvLUBprm +gq0oHEOtl1XICiXkZqtoq9Tz1xJCDVQfXhUsJz+nYY20dI3bPN75OzyD+pqEPOiqbgfJ78yi +pR9H0HN2WBCSP5rY+ZJG66UXqMvmushHjj1ps1Jp/52sPzqK/PQXzuk3fj3LZdC7sLoD1jdv +Vqg0vylLamdOS6FzzbVB400aSnD/GuMzaMiVK9Fr4hp9h5ipWTJjVVpFjihQcfhvvcS0E0Gg +D5iyuAsPH7jZf/F+cs+upkNuNSj1glPaoos/X8oPtQiwYhW4aTtZxSiq3gFLm7JpooAjJhtg +8jHjsBnvsQQSQEGVYdlDSV9VtccRXHvYqxj21IFPlIK5Xzeuh1vuXg68q42e/2BBYuYetT0D +3FMxjHMWfUSg82viQw4h+4o9grvuDkyqpXKA4rT83cWQyHXen0EhkiqGqFGCfCtZfbaFBgR6 +uMcXeDZSe0Yr6iKOIuylpwWo3TMdrgTaesbJ9+H9Xzmtpcyb4uz8LjLxSIhG0j7/9qtp5/ni +Z4KLWDlxbSDWbQ/CE9X3a53Uw6a+dDZu1RSXM2VwjnpB/e4WDtJcWIQXJhwGbwSIXvHVxTP5 +g2T1ma6QWfWvo+6zsjLJj6otxD/Z/A6zyhN77wAj+w0/PcUh2sMutKRUgPPME8wWvQpz76SP +I6RwEfNXU5Ayfbc92uBh8E2tLWiknd/34K+CCxpqcKL848JdxkW0NyYW1dgi4YobmmMZkK+6 +oFDTni5uHTCNzWQHWirz6qMSm/XtkjdKf5TomLmEjYZfYGl+GbP6mQLPxPdNjol+zsKRaTgu +tj6Xe0m6SrCCJODDiIn7tcrar5JqZvUZRXX6+Ei6D08D0aGNfx23P1NtyBJFkb7BbXgQgvf8 +kOsa7704o0lNhhnks1dy59p9z0nuwC2kjJmohFNN/P8BkAmbH2xUnVeVAAZAxGUQSoLD0qIR +VO4268i4buWo2h8LHPVISrLZu8llPjCE4mWXSEjAMASHQc4rDVDhU/yJg6bqF7T5S9R/+Rqe +CMypbWpDQ/fHgZW32HKy1SK0+Ue4lI1R+fNEE7sDj6yd2MGl0noxF6dYDHrx2aWe+vYT0ZYO +sjVVBQOJpURX8HSknOLcYngdhb6YmD3q+h/2tKHrlvgRpPkADurOosYy39ltAagnQi6e51YD +LoI9ZxpjQra/EO2a1We9dqKVsnbz1U9WHXIN3wxvK6nF0f8AAA1e/wAADVkBEAABAQAAAAAA +AAAAAAAAAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAKBwcIBwYKCAgICwoKCw4YEA4NDQ4d +FRYRGCMfJSQiHyIhJis3LyYpNCkhIjBBMTQ5Oz4+PiUuRElDPEg3PT47/9sAQwEKCwsODQ4c +EBAcOygiKDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 +Ozs7/8AAEQgAkAB4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkK +C//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHw +JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 +eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY +2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkK +C//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1Lw +FWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2 +d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW +19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A9moqtf30Gm2cl3cvtijGSa4a +++LNlGStlZvKR0ZuBWkKU6nwomU4x3PQqK8guPinrEzYhhihX86ns/Ffia/XzElJUHOV4/rW +/wBUqJXlZEe2i9j1iivMP+Ex1q3+/KCw6gip4PiXdREC5tUkHcrwaTwlVbK4e1iekUVzmheN +dO1ycWyK8U5Gdrf410QOa55RcXaSNE09ULRRRUjCiiigAooooAKKKKAOY+IblfCN1g9cA/rX +h1fQPiXT4dU0o2dwXEcrclCARgE8ZB9K4J/AGkKeJr38ZU/+Ir0MLiIUoNSOerTlJ3R54v3h +Xpfg3UdNGmrHPMsToOc9+KrQeBdAd2SS7vkYdPnX/wCIqy3gfRoThb+9GP8AaQ/+yVdavRqx +s2yYU5wdzH164t57+V7XHlZOCOh5rn5n5NdrJ4U0xBt/tC8x16p/8RTP+EK0uRQ32q9IPfzE +/wDiKuGKpRSSYnSm3c5/wjP5XiKFywUDqScelevR6/pCR4k1S0DDqPOXI/WvPLjwdplpbtPG +9zI6so2yspU5YDoFHrW7pOmRWpEiqVyuPlHH41xYmpGpPmibU4uKszqY9f0aZtseq2bN6eeu +f51fVldQyMGU9CDkGueMCOpxtYe3NYWoabJJOZrWV7V1yFe1cxnH1HX8a57Glz0CiuFg8U6r +pjql2PtkXTMgCv8Agw4/MfjXU6VrthrCH7NKRIoy8LjDr+Hp7jIosFzRooopDCiiigClqXKR +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 +Ozv/wAARCACQAHgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL +/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk +M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4 +eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ +2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL +/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV +YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 +eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX +2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD2aiq1/fQabZyXdy+2KMZJrhr7 +4s2UZK2Vm8pHRm4FaQpTqfCiZTjHc9CoryC4+KesTNiGGKFfzqez8V+Jr9fMSUlQc5Xj+tb/ +AFSoleVkR7aL2PWKK8w/4THWrf78oLDqCKng+Jd1EQLm1SQdyvBpPCVVsrh7WJ6RRXOaF410 +7XJxbIrxTkZ2t/jXRA5rnlFxdpI0TT1QtFFFSMKKKKACiiigAooooA5j4huV8I3WD1wD+teH +V9A+JdPh1TSjZ3BcRytyUIBGATxkH0rgn8AaQp4mvfxlT/4ivQwuIhSg1I56tOUndHni/eFe +l+DdR00aasc8yxOg5z34qtB4F0B3ZJLu+Rh0+df/AIirLeB9GhOFv70Y/wBpD/7JV1q9GrGz +bJhTnB3MfXri3nv5XtceVk4I6Hmufmfk12snhTTEG3+0LzHXqn/xFM/4QrS5FDfar0g9/MT/ +AOIq8YqlFJJydLbdzm/CM+lOIoXLFROpJw6F69H76kJHiTVLQMOo85cj9a88uPB2mWlu08b3 +MjqyjbKylTlgOgUetbuk6ZFakSKpXK4+UcfjXFiakak+aJtTi4qzOpj1/Rpm2x6rZs3p565/ +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 +GL2PKvmGe3T51+7t98IqFDs+7btT5miLdwV9jA== +=bZzH +-----END PGP PUBLIC KEY BLOCK----- |