Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pg/src/main/java/org/spongycastle/openpgp/operator')
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java10
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java57
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java134
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PBEProtectionRemoverFactory.java9
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java31
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java104
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java20
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java10
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java20
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java10
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java9
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java30
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java25
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java5
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java39
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java36
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java40
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java21
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java23
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPPad.java50
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java229
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java10
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java100
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java115
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java174
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java68
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java68
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java95
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java43
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java142
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java98
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java75
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java131
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java82
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java239
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java33
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java139
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java139
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java75
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java68
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java35
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java74
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java156
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java113
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java149
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java377
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java48
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java34
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java109
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java142
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEProtectionRemoverFactory.java106
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java100
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java180
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java175
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java239
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java166
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java200
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java124
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java81
-rw-r--r--pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java56
60 files changed, 5470 insertions, 0 deletions
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/KeyFingerPrintCalculator.java
new file mode 100644
index 00000000..a5a8a269
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java
new file mode 100644
index 00000000..5660468e
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEDataDecryptorFactory.java
@@ -0,0 +1,57 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.bcpg.S2K;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * A factory for performing PBE decryption operations.
+ */
+public abstract class PBEDataDecryptorFactory
+ implements PGPDataDecryptorFactory
+{
+ private char[] passPhrase;
+ private PGPDigestCalculatorProvider calculatorProvider;
+
+ /**
+ * Construct a PBE data decryptor factory.
+ *
+ * @param passPhrase the pass phrase to generate decryption keys with.
+ * @param calculatorProvider the digest to use in key generation.
+ */
+ protected PBEDataDecryptorFactory(char[] passPhrase, PGPDigestCalculatorProvider calculatorProvider)
+ {
+ this.passPhrase = passPhrase;
+ this.calculatorProvider = calculatorProvider;
+ }
+
+ /**
+ * Generates an encryption key using the pass phrase and digest calculator configured for this
+ * factory.
+ *
+ * @param keyAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} to generate a
+ * key for.
+ * @param s2k the string-to-key specification to use to generate the key.
+ * @return the key bytes for the encryption algorithm, generated using the pass phrase of this
+ * factory.
+ * @throws PGPException if an error occurs generating the key.
+ */
+ public byte[] makeKeyFromPassPhrase(int keyAlgorithm, S2K s2k)
+ throws PGPException
+ {
+ return PGPUtil.makeKeyFromPassPhrase(calculatorProvider, keyAlgorithm, s2k, passPhrase);
+ }
+
+ /**
+ * Decrypts session data from an encrypted data packet.
+ *
+ * @param keyAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} used to
+ * encrypt the session data.
+ * @param key the key bytes for the encryption algorithm.
+ * @param seckKeyData the encrypted session data to decrypt.
+ * @return the decrypted session data.
+ * @throws PGPException if an error occurs decrypting the session data.
+ */
+ public abstract byte[] recoverSessionData(int keyAlgorithm, byte[] key, byte[] seckKeyData)
+ throws PGPException;
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..55a3e64b
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java
@@ -0,0 +1,134 @@
+package org.spongycastle.openpgp.operator;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.bcpg.ContainedPacket;
+import org.spongycastle.bcpg.S2K;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.bcpg.SymmetricKeyEncSessionPacket;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * PGP style PBE encryption method.
+ * <p/>
+ * A pass phrase is used to generate an encryption key using the PGP {@link S2K string-to-key}
+ * method. This class always uses the {@link S2K#SALTED_AND_ITERATED salted and iterated form of the
+ * S2K algorithm}.
+ * <p/>
+ * Note that the iteration count provided to this method is a single byte as described by the
+ * {@link S2K} algorithm, and the actual iteration count ranges exponentially from
+ * <code>0x01<code> == 1088 to <code>0xFF</code> == 65,011,712.
+ */
+public abstract class PBEKeyEncryptionMethodGenerator
+ extends PGPKeyEncryptionMethodGenerator
+{
+ private char[] passPhrase;
+ private PGPDigestCalculator s2kDigestCalculator;
+ private S2K s2k;
+ private SecureRandom random;
+ private int s2kCount;
+
+ /**
+ * Construct a PBE key generator using the default iteration count (<code>0x60</code> == 65536
+ * iterations).
+ *
+ * @param passPhrase the pass phrase to encrypt with.
+ * @param s2kDigestCalculator a digest calculator to use in the string-to-key function.
+ */
+ protected PBEKeyEncryptionMethodGenerator(
+ char[] passPhrase,
+ PGPDigestCalculator s2kDigestCalculator)
+ {
+ this(passPhrase, s2kDigestCalculator, 0x60);
+ }
+
+ /**
+ * Construct a PBE key generator using a specific iteration level.
+ *
+ * @param passPhrase the pass phrase to encrypt with.
+ * @param s2kDigestCalculator a digest calculator to use in the string-to-key function.
+ * @param s2kCount a single byte {@link S2K} iteration count specifier, which is translated to
+ * an actual iteration count by the S2K class.
+ */
+ 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;
+ }
+
+ /**
+ * Sets a user defined source of randomness.
+ * <p/>
+ * If no SecureRandom is configured, a default SecureRandom will be used.
+ *
+ * @return the current generator.
+ */
+ public PBEKeyEncryptionMethodGenerator setSecureRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ /**
+ * Generate a key for a symmetric encryption algorithm using the PBE configuration in this
+ * method.
+ *
+ * @param encAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} to generate
+ * the key for.
+ * @return the bytes of the generated key.
+ * @throws PGPException if an error occurs performing the string-to-key generation.
+ */
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/PBEProtectionRemoverFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEProtectionRemoverFactory.java
new file mode 100644
index 00000000..99166f6f
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PBEProtectionRemoverFactory.java
@@ -0,0 +1,9 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.openpgp.PGPException;
+
+public interface PBEProtectionRemoverFactory
+{
+ PBESecretKeyDecryptor createDecryptor(String protection)
+ throws PGPException;
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyDecryptor.java
new file mode 100644
index 00000000..2f75702f
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PBESecretKeyEncryptor.java
new file mode 100644
index 00000000..2bd1bf8f
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSigner.java
new file mode 100644
index 00000000..7a3891e6
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentSignerBuilder.java
new file mode 100644
index 00000000..44f8c8dd
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifier.java
new file mode 100644
index 00000000..7ed1aea6
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilder.java
new file mode 100644
index 00000000..9e8e4a94
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPContentVerifierBuilderProvider.java
new file mode 100644
index 00000000..44c138bf
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java
new file mode 100644
index 00000000..ceeca672
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptor.java
@@ -0,0 +1,30 @@
+package org.spongycastle.openpgp.operator;
+
+import java.io.InputStream;
+
+/**
+ * A decryptor that wraps a stream of PGP encrypted data to decrypt, and optionally integrity check,
+ * the data.
+ */
+public interface PGPDataDecryptor
+{
+ /**
+ * Wraps an encrypted data stream with a stream that will return the decrypted data.
+ *
+ * @param in the encrypted data.
+ * @return a decrypting stream.
+ */
+ InputStream getInputStream(InputStream in);
+
+ /**
+ * Obtains the block size of the encryption algorithm used in this decryptor.
+ *
+ * @return the block size of the cipher in bytes.
+ */
+ int getBlockSize();
+
+ /**
+ * Obtains the digest calculator used to verify the integrity check.
+ */
+ PGPDigestCalculator getIntegrityCalculator();
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java
new file mode 100644
index 00000000..7223d039
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorFactory.java
@@ -0,0 +1,25 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * Base interface of factories for {@link PGPDataDecryptor}.
+ */
+public interface PGPDataDecryptorFactory
+{
+ /**
+ * Constructs a data decryptor.
+ *
+ * @param withIntegrityPacket <code>true</code> if the packet to be decrypted has integrity
+ * checking enabled.
+ * @param encAlgorithm the identifier of the {@link SymmetricKeyAlgorithmTags encryption
+ * algorithm} to decrypt with.
+ * @param key the bytes of the key for the cipher.
+ * @return a data decryptor that can decrypt (and verify) streams of encrypted data.
+ * @throws PGPException if an error occurs initialising the decryption and integrity checking
+ * functions.
+ */
+ public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
+ throws PGPException;
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataDecryptorProvider.java
new file mode 100644
index 00000000..9e87a565
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java
new file mode 100644
index 00000000..9811a6de
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptor.java
@@ -0,0 +1,39 @@
+package org.spongycastle.openpgp.operator;
+
+import java.io.OutputStream;
+
+/**
+ * A data encryptor, combining a cipher instance and an optional integrity check calculator.
+ * <p/>
+ * {@link PGPDataEncryptor} instances are generally not constructed directly, but obtained from a
+ * {@link PGPDataEncryptorBuilder}.
+ */
+public interface PGPDataEncryptor
+{
+ /**
+ * Constructs an encrypting output stream that encrypts data using the underlying cipher of this
+ * encryptor.
+ * <p/>
+ * The cipher instance in this encryptor is used for all output streams obtained from this
+ * method, so it should only be invoked once.
+ *
+ * @param out the stream to wrap and write encrypted data to.
+ * @return a cipher output stream appropriate to the type of this data encryptor.
+ */
+ OutputStream getOutputStream(OutputStream out);
+
+ /**
+ * Obtains the integrity check calculator configured for this encryptor instance.
+ *
+ * @return the integrity check calculator, or <code>null</code> if no integrity checking was
+ * configured.
+ */
+ PGPDigestCalculator getIntegrityCalculator();
+
+ /**
+ * Gets the block size of the underlying cipher used by this encryptor.
+ *
+ * @return the block size in bytes.
+ */
+ int getBlockSize();
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java
new file mode 100644
index 00000000..a889b3be
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDataEncryptorBuilder.java
@@ -0,0 +1,36 @@
+package org.spongycastle.openpgp.operator;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * A builder for {@link PGPDataEncryptor} instances, which can be used to encrypt data objects.
+ */
+public interface PGPDataEncryptorBuilder
+{
+ /**
+ * The encryption algorithm used by data encryptors created by this builder.
+ *
+ * @return one of the {@link SymmetricKeyAlgorithmTags symmetric encryption algorithms}.
+ */
+ int getAlgorithm();
+
+ /**
+ * Builds a data encryptor using the algorithm configured for this builder.
+ *
+ * @param keyBytes the bytes of the key to use for the cipher.
+ * @return a data encryptor with an initialised cipher.
+ * @throws PGPException if an error occurs initialising the configured encryption.
+ */
+ PGPDataEncryptor build(byte[] keyBytes)
+ throws PGPException;
+
+ /**
+ * Gets the SecureRandom instance used by this builder. <br/>
+ * If a SecureRandom has not been explicitly configured, a default {@link SecureRandom} is
+ * constructed and retained by the this builder.
+ */
+ SecureRandom getSecureRandom();
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java
new file mode 100644
index 00000000..a4b926b0
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculator.java
@@ -0,0 +1,40 @@
+package org.spongycastle.openpgp.operator;
+
+import java.io.OutputStream;
+
+import org.spongycastle.bcpg.HashAlgorithmTags;
+
+/**
+ * A digest calculator, which consumes a stream of data and computes a digest value over it.
+ */
+public interface PGPDigestCalculator
+{
+ /**
+ * Return the {@link HashAlgorithmTags algorithm number} representing the digest implemented by
+ * this calculator.
+ *
+ * @return the hash 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 that data to be digested can be written to.
+ */
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java
new file mode 100644
index 00000000..8ad35b13
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPDigestCalculatorProvider.java
@@ -0,0 +1,21 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * A factory for digest algorithms.
+ */
+public interface PGPDigestCalculatorProvider
+{
+ /**
+ * Construct a new instance of a cryptographic digest.
+ *
+ * @param algorithm the identifier of the {@link HashAlgorithmTags digest algorithm} to
+ * instantiate.
+ * @return a digest calculator for the specified algorithm.
+ * @throws PGPException if an error occurs constructing the specified digest.
+ */
+ PGPDigestCalculator get(int algorithm)
+ throws PGPException;
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..d0a3c5b6
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPKeyEncryptionMethodGenerator.java
@@ -0,0 +1,23 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.bcpg.ContainedPacket;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * An encryption method that can be applied to encrypt data in a {@link PGPEncryptedDataGenerator}.
+ */
+public abstract class PGPKeyEncryptionMethodGenerator
+{
+ /**
+ * Generates a packet encoding the details of this encryption method.
+ *
+ * @param encAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} being used
+ * @param sessionInfo session data generated by the encrypted data generator.
+ * @return a packet encoding the provided information and the configuration of this instance.
+ * @throws PGPException if an error occurs constructing the packet.
+ */
+ public abstract ContainedPacket generate(int encAlgorithm, byte[] sessionInfo)
+ throws PGPException;
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/PGPPad.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPPad.java
new file mode 100644
index 00000000..d3ef360c
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPPad.java
@@ -0,0 +1,50 @@
+package org.spongycastle.openpgp.operator;
+
+import org.spongycastle.openpgp.PGPException;
+
+/**
+ * Utility class that provides padding addition and removal for PGP session keys.
+ */
+public class PGPPad
+{
+ private PGPPad()
+ {
+
+ }
+
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java
new file mode 100644
index 00000000..c318c673
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/PGPUtil.java
@@ -0,0 +1,229 @@
+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
+ {
+ // TODO: Never used
+ 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;
+ case SymmetricKeyAlgorithmTags.CAMELLIA_128:
+ keySize = 128;
+ algName = "Camellia";
+ break;
+ case SymmetricKeyAlgorithmTags.CAMELLIA_192:
+ keySize = 192;
+ algName = "Camellia";
+ break;
+ case SymmetricKeyAlgorithmTags.CAMELLIA_256:
+ keySize = 256;
+ algName = "Camellia";
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java
new file mode 100644
index 00000000..35d2a01d
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..8030b946
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java
new file mode 100644
index 00000000..bc342f68
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/RFC6637KDFCalculator.java
@@ -0,0 +1,115 @@
+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(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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java
new file mode 100644
index 00000000..f54870f0
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcImplProvider.java
@@ -0,0 +1,174 @@
+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.Wrapper;
+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.AESFastEngine;
+import org.spongycastle.crypto.engines.BlowfishEngine;
+import org.spongycastle.crypto.engines.CAST5Engine;
+import org.spongycastle.crypto.engines.CamelliaEngine;
+import org.spongycastle.crypto.engines.DESEngine;
+import org.spongycastle.crypto.engines.DESedeEngine;
+import org.spongycastle.crypto.engines.ElGamalEngine;
+import org.spongycastle.crypto.engines.IDEAEngine;
+import org.spongycastle.crypto.engines.RFC3394WrapEngine;
+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.ECDSASigner;
+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));
+ case PublicKeyAlgorithmTags.ECDSA:
+ return new DSADigestSigner(new ECDSASigner(), createDigest(hashAlgorithm));
+ default:
+ throw new PGPException("cannot recognise keyAlgorithm: " + 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.CAMELLIA_128:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_192:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_256:
+ engine = new CamelliaEngine();
+ break;
+ case SymmetricKeyAlgorithmTags.BLOWFISH:
+ engine = new BlowfishEngine();
+ break;
+ case SymmetricKeyAlgorithmTags.CAST5:
+ engine = new CAST5Engine();
+ break;
+ case SymmetricKeyAlgorithmTags.DES:
+ engine = new DESEngine();
+ break;
+ case SymmetricKeyAlgorithmTags.IDEA:
+ engine = new IDEAEngine();
+ 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 Wrapper createWrapper(int encAlgorithm)
+ throws PGPException
+ {
+ switch (encAlgorithm)
+ {
+ case SymmetricKeyAlgorithmTags.AES_128:
+ case SymmetricKeyAlgorithmTags.AES_192:
+ case SymmetricKeyAlgorithmTags.AES_256:
+ return new RFC3394WrapEngine(new AESFastEngine());
+ case SymmetricKeyAlgorithmTags.CAMELLIA_128:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_192:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_256:
+ return new RFC3394WrapEngine(new CamelliaEngine());
+ default:
+ throw new PGPException("unknown wrap algorithm: " + encAlgorithm);
+ }
+ }
+
+ 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.");
+ case PGPPublicKey.ECDH:
+ throw new PGPException("Not implemented.");
+ default:
+ throw new PGPException("unknown asymmetric algorithm: " + encAlgorithm);
+ }
+
+ return c;
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcKeyFingerprintCalculator.java
new file mode 100644
index 00000000..9b9b7f26
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java
new file mode 100644
index 00000000..b9a1c189
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEDataDecryptorFactory.java
@@ -0,0 +1,68 @@
+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 {@link PBEDataDecryptorFactory} for handling PBE decryption operations using the Bouncy Castle
+ * lightweight API to implement cryptographic primitives.
+ */
+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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..2a151f0d
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBEKeyEncryptionMethodGenerator.java
@@ -0,0 +1,95 @@
+package org.spongycastle.openpgp.operator.bc;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.bcpg.S2K;
+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 digest and the default S2K count
+ * for key generation.
+ *
+ * @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 and the default S2K
+ * count for key generation.
+ *
+ * @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
+ * generation.
+ *
+ * @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 single byte {@link 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 for key generation.
+ *
+ * @param passPhrase the passphrase to use as the primary source of key material.
+ * @param s2kCount the single byte {@link S2K} count to use.
+ */
+ public BcPBEKeyEncryptionMethodGenerator(char[] passPhrase, int s2kCount)
+ {
+ super(passPhrase, new SHA1PGPDigestCalculator(), s2kCount);
+ }
+
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java
new file mode 100644
index 00000000..bf0a0db9
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPBESecretKeyEncryptorBuilder.java
new file mode 100644
index 00000000..aea664ae
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentSignerBuilder.java
new file mode 100644
index 00000000..cd98ef38
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPContentVerifierBuilderProvider.java
new file mode 100644
index 00000000..a2cfbf91
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java
new file mode 100644
index 00000000..bfb2946a
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDataEncryptorBuilder.java
@@ -0,0 +1,131 @@
+package org.spongycastle.openpgp.operator.bc;
+
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+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;
+
+/**
+ * {@link PGPDataEncryptorBuilder} implementation that uses the Bouncy Castle lightweight API to
+ * implement cryptographic primitives.
+ */
+public class BcPGPDataEncryptorBuilder
+ implements PGPDataEncryptorBuilder
+{
+ private SecureRandom random;
+ private boolean withIntegrityPacket;
+ private int encAlgorithm;
+
+ /**
+ * Constructs a new data encryptor builder for a specified cipher type.
+ *
+ * @param encAlgorithm one of the {@link SymmetricKeyAlgorithmTags supported symmetric cipher
+ * algorithms}. May not be {@link SymmetricKeyAlgorithmTags#NULL}.
+ */
+ public BcPGPDataEncryptorBuilder(int encAlgorithm)
+ {
+ this.encAlgorithm = encAlgorithm;
+
+ if (encAlgorithm == 0)
+ {
+ throw new IllegalArgumentException("null cipher specified");
+ }
+ }
+
+ /**
+ * Sets 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.
+ * <p/>
+ * If no SecureRandom is configured, a default SecureRandom will be used.
+ *
+ * @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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPDigestCalculatorProvider.java
new file mode 100644
index 00000000..50d5fc73
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java
new file mode 100644
index 00000000..40577467
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyConverter.java
@@ -0,0 +1,239 @@
+package org.spongycastle.openpgp.operator.bc;
+
+import java.util.Date;
+
+import org.spongycastle.asn1.x9.ECNamedCurveTable;
+import org.spongycastle.asn1.x9.X9ECParameters;
+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.ECPublicBCPGKey;
+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.crypto.ec.CustomNamedCurves;
+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.ECNamedDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+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 setting
+ bcpgKey = new ECDHPublicBCPGKey(((ECNamedDomainParameters)eK.getParameters()).getName(), eK.getQ(), HashAlgorithmTags.SHA256, SymmetricKeyAlgorithmTags.AES_128);
+ }
+ else
+ {
+ bcpgKey = new ECDSAPublicBCPGKey(((ECNamedDomainParameters)eK.getParameters()).getName(), 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;
+ case PGPPublicKey.ECDH:
+ case PGPPublicKey.ECDSA:
+ ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey;
+
+ privPk = new ECSecretBCPGKey(ecK.getD());
+ 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()));
+ case PGPPublicKey.ECDH:
+ case PGPPublicKey.ECDSA:
+ ECPublicBCPGKey ecPub = (ECPublicBCPGKey)publicPk.getKey();
+
+ X9ECParameters x9 = CustomNamedCurves.getByOID(ecPub.getCurveOID());
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByOID(ecPub.getCurveOID());
+ }
+
+ return new ECPublicKeyParameters(ecPub.getPoint(),
+ new ECNamedDomainParameters(ecPub.getCurveOID(), x9.getCurve(), x9.getG(), x9.getN(), x9.getH()));
+ 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()));
+ case PGPPublicKey.ECDH:
+ case PGPPublicKey.ECDSA:
+ ECPublicBCPGKey ecPub = (ECPublicBCPGKey)pubPk.getKey();
+ ECSecretBCPGKey ecPriv = (ECSecretBCPGKey)privPk;
+
+ X9ECParameters x9 = CustomNamedCurves.getByOID(ecPub.getCurveOID());
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByOID(ecPub.getCurveOID());
+ }
+
+ return new ECPrivateKeyParameters(ecPriv.getX(),
+ new ECNamedDomainParameters(ecPub.getCurveOID(), x9.getCurve(), x9.getG(), x9.getN(), x9.getH()));
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPGPKeyPair.java
new file mode 100644
index 00000000..ed6a0d7e
--- /dev/null
+++ b/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, keyPair.getPublic(), date);
+ this.priv = getPrivateKey(this.pub, keyPair.getPrivate());
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
new file mode 100644
index 00000000..4c5124bb
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
@@ -0,0 +1,139 @@
+package org.spongycastle.openpgp.operator.bc;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.bcpg.ECDHPublicBCPGKey;
+import org.spongycastle.bcpg.ECSecretBCPGKey;
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.BufferedAsymmetricBlockCipher;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.Wrapper;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.spongycastle.crypto.params.KeyParameter;
+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.PGPPad;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.RFC6637KDFCalculator;
+
+/**
+ * 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
+ {
+ if (keyAlgorithm != PGPPublicKey.ECDH)
+ {
+ 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();
+ }
+ else
+ {
+ ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)privKey.getPublicKeyPacket().getKey();
+ X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID());
+
+ 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);
+
+ Wrapper c = BcImplProvider.createWrapper(ecKey.getSymmetricKeyAlgorithm());
+
+ ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privKey.getPrivateKeyDataPacket()).getX()).normalize();
+
+ RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(new BcPGPDigestCalculatorProvider().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
+ KeyParameter key = new KeyParameter(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, new BcKeyFingerprintCalculator().calculateFingerprint(privKey.getPublicKeyPacket())));
+
+ c.init(false, key);
+
+ return PGPPad.unpadSessionData(c.unwrap(keyEnc, 0, keyEnc.length));
+ }
+ }
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..72d501b0
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java
@@ -0,0 +1,139 @@
+package org.spongycastle.openpgp.operator.bc;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+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.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.EphemeralKeyPair;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.KeyEncoder;
+import org.spongycastle.crypto.Wrapper;
+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.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.operator.PGPPad;
+import org.spongycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator;
+import org.spongycastle.openpgp.operator.RFC6637KDFCalculator;
+
+/**
+ * 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
+ {
+ if (pubKey.getAlgorithm() != PGPPublicKey.ECDH)
+ {
+ 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);
+ }
+ else
+ {
+ 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(new BcPGPDigestCalculatorProvider().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
+
+ KeyParameter key = new KeyParameter(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, pubKey.getFingerprint()));
+
+ Wrapper c = BcImplProvider.createWrapper(ecKey.getSymmetricKeyAlgorithm());
+
+ c.init(true, new ParametersWithRandom(key, random));
+
+ byte[] paddedSessionData = PGPPad.padSessionData(sessionInfo);
+
+ byte[] C = c.wrap(paddedSessionData, 0, paddedSessionData.length);
+ 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;
+ }
+ }
+ catch (InvalidCipherTextException e)
+ {
+ throw new PGPException("exception encrypting session info: " + e.getMessage(), e);
+ }
+ catch (IOException e)
+ {
+ throw new PGPException("exception encrypting session info: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/BcUtil.java
new file mode 100644
index 00000000..c3be8c83
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SHA1PGPDigestCalculator.java
new file mode 100644
index 00000000..15572a39
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java b/pg/src/main/java/org/spongycastle/openpgp/operator/bc/SignerOutputStream.java
new file mode 100644
index 00000000..cdc2d7e3
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java
new file mode 100644
index 00000000..e72955f2
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaKeyFingerprintCalculator.java
@@ -0,0 +1,74 @@
+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
+{
+
+ // FIXME: Convert this to builder style so we can set provider?
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentSignerBuilder.java
new file mode 100644
index 00000000..5cb6f007
--- /dev/null
+++ b/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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
new file mode 100644
index 00000000..99973776
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPContentVerifierBuilderProvider.java
@@ -0,0 +1,113 @@
+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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPRuntimeOperationException;
+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)
+ {
+ throw new PGPRuntimeOperationException("unable to verify signature: " + e.getMessage(), e);
+ }
+ }
+
+ public OutputStream getOutputStream()
+ {
+ return new SignatureOutputStream(signature);
+ }
+ };
+ }
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java
new file mode 100644
index 00000000..753e289c
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPDigestCalculatorProviderBuilder.java
@@ -0,0 +1,149 @@
+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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.operator.PGPDigestCalculator;
+import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
+
+/**
+ * A builder for {@link PGPDigestCalculatorProvider} instances that obtain cryptographic primitives
+ * using the JCA API.
+ * <p/>
+ * By default digest calculator providers obtained from this builder will use the default JCA
+ * algorithm lookup mechanisms (i.e. specifying no provider), but a specific provider can be
+ * specified prior to building.
+ */
+public class JcaPGPDigestCalculatorProviderBuilder
+{
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+
+ /**
+ * Default constructor.
+ */
+ public JcaPGPDigestCalculatorProviderBuilder()
+ {
+ }
+
+ /**
+ * Sets the provider to use to obtain cryptographic primitives.
+ *
+ * @param provider the JCA provider to use.
+ * @return the current builder.
+ */
+ public JcaPGPDigestCalculatorProviderBuilder setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ /**
+ * Sets the provider to use to obtain cryptographic primitives.
+ *
+ * @param providerName the name of the JCA provider to use.
+ * @return the current builder.
+ */
+ public JcaPGPDigestCalculatorProviderBuilder setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ /**
+ * Constructs a new PGPDigestCalculatorProvider
+ *
+ * @return a PGPDigestCalculatorProvider that will use the JCA algorithm lookup strategy
+ * configured on this builder.
+ * @throws PGPException if an error occurs constructing the digest calculator provider.
+ */
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
new file mode 100644
index 00000000..2f82fdfa
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyConverter.java
@@ -0,0 +1,377 @@
+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.ECNamedCurveTable;
+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.crypto.ec.CustomNamedCurves;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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()),
+ getX9Parameters(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()),
+ getX9Parameters(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(),
+ getX9Parameters(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(),
+ getX9Parameters(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 getX9Parameters(ASN1ObjectIdentifier curveOid)
+ {
+ X9ECParameters x9 = CustomNamedCurves.getByOID(curveOid);
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByOID(curveOid);
+ }
+
+ return new ECNamedCurveSpec(curveOid.getId(), x9.getCurve(), x9.getG(), x9.getN(),
+ x9.getH(), x9.getSeed());
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPKeyPair.java
new file mode 100644
index 00000000..b32d00d7
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcaPGPPrivateKey.java
new file mode 100644
index 00000000..a22f4561
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java
new file mode 100644
index 00000000..d0e73b24
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEDataDecryptorFactoryBuilder.java
@@ -0,0 +1,109 @@
+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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.PGPDataDecryptor;
+import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
+
+/**
+ * Builder for {@link PBEDataDecryptorFactory} instances that obtain cryptographic primitives using
+ * the JCE API.
+ */
+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;
+ }
+
+ /**
+ * Construct a {@link PBEDataDecryptorFactory} to use to decrypt PBE encrypted data.
+ *
+ * @param passPhrase the pass phrase to use to generate keys in the resulting factory.
+ * @return a decryptor factory that can be used to generate PBE keys.
+ */
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..510ca578
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java
@@ -0,0 +1,142 @@
+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.bcpg.S2K;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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 digest and the default S2K count
+ * for key generation.
+ *
+ * @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 and the default S2K
+ * count for key generation.
+ *
+ * @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
+ * generation.
+ *
+ * @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 single byte {@link 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 for key generation.
+ *
+ * @param passPhrase the passphrase to use as the primary source of key material.
+ * @param s2kCount the single byte {@link S2K} count to use.
+ */
+ public JcePBEKeyEncryptionMethodGenerator(char[] passPhrase, int s2kCount)
+ {
+ super(passPhrase, new SHA1PGPDigestCalculator(), s2kCount);
+ }
+
+ /**
+ * Sets the JCE provider to source cryptographic primitives from.
+ *
+ * @param provider the JCE provider to use.
+ * @return the current generator.
+ */
+ public JcePBEKeyEncryptionMethodGenerator setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ /**
+ * Sets the JCE provider to source cryptographic primitives from.
+ *
+ * @param providerName the name of the JCE provider to use.
+ * @return the current generator.
+ */
+ public JcePBEKeyEncryptionMethodGenerator setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEProtectionRemoverFactory.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEProtectionRemoverFactory.java
new file mode 100644
index 00000000..1e8dc4b9
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBEProtectionRemoverFactory.java
@@ -0,0 +1,106 @@
+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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.operator.PBEProtectionRemoverFactory;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
+
+public class JcePBEProtectionRemoverFactory
+ implements PBEProtectionRemoverFactory
+{
+ private final char[] passPhrase;
+
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+ private PGPDigestCalculatorProvider calculatorProvider;
+
+ private JcaPGPDigestCalculatorProviderBuilder calculatorProviderBuilder;
+
+ public JcePBEProtectionRemoverFactory(char[] passPhrase)
+ {
+ this.passPhrase = passPhrase;
+ this.calculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder();
+ }
+
+ public JcePBEProtectionRemoverFactory(char[] passPhrase, PGPDigestCalculatorProvider calculatorProvider)
+ {
+ this.passPhrase = passPhrase;
+ this.calculatorProvider = calculatorProvider;
+ }
+
+ public JcePBEProtectionRemoverFactory setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ if (calculatorProviderBuilder != null)
+ {
+ calculatorProviderBuilder.setProvider(provider);
+ }
+
+ return this;
+ }
+
+ public JcePBEProtectionRemoverFactory setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ if (calculatorProviderBuilder != null)
+ {
+ calculatorProviderBuilder.setProvider(providerName);
+ }
+
+ return this;
+ }
+
+ public PBESecretKeyDecryptor createDecryptor(String protection)
+ 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) + "/CBC/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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java
new file mode 100644
index 00000000..15ab5358
--- /dev/null
+++ b/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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePBESecretKeyEncryptorBuilder.java
new file mode 100644
index 00000000..4a137418
--- /dev/null
+++ b/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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java
new file mode 100644
index 00000000..6761e607
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePGPDataEncryptorBuilder.java
@@ -0,0 +1,175 @@
+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.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.operator.PGPDataEncryptor;
+import org.spongycastle.openpgp.operator.PGPDataEncryptorBuilder;
+import org.spongycastle.openpgp.operator.PGPDigestCalculator;
+
+/**
+ * {@link PGPDataEncryptorBuilder} implementation that sources cryptographic primitives using the
+ * JCE APIs.
+ * <p/>
+ * By default, cryptographic primitives will be loaded using the default JCE load order (i.e.
+ * without specifying a provider). <br/>
+ * A specific provider can be specified using one of the {@link #setProvider(String)} methods.
+ */
+public class JcePGPDataEncryptorBuilder
+ implements PGPDataEncryptorBuilder
+{
+ private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
+ private SecureRandom random;
+ private boolean withIntegrityPacket;
+ private int encAlgorithm;
+
+ /**
+ * Constructs a new data encryptor builder for a specified cipher type.
+ *
+ * @param encAlgorithm one of the {@link SymmetricKeyAlgorithmTags supported symmetric cipher
+ * algorithms}. May not be {@link SymmetricKeyAlgorithmTags#NULL}.
+ */
+ public JcePGPDataEncryptorBuilder(int encAlgorithm)
+ {
+ this.encAlgorithm = encAlgorithm;
+
+ if (encAlgorithm == 0)
+ {
+ throw new IllegalArgumentException("null cipher specified");
+ }
+ }
+
+ /**
+ * Sets 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;
+ }
+
+ /**
+ * Sets the JCE provider to source cryptographic primitives from.
+ *
+ * @param provider the JCE provider to use.
+ * @return the current builder.
+ */
+ public JcePGPDataEncryptorBuilder setProvider(Provider provider)
+ {
+ this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
+
+ return this;
+ }
+
+ /**
+ * Sets the JCE provider to source cryptographic primitives from.
+ *
+ * @param providerName the name of the JCE provider to use.
+ * @return the current builder.
+ */
+ public JcePGPDataEncryptorBuilder setProvider(String providerName)
+ {
+ this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+
+ return this;
+ }
+
+ /**
+ * Provide a user defined source of randomness.
+ * <p/>
+ * If no SecureRandom is configured, a default SecureRandom will be used.
+ *
+ * @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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
new file mode 100644
index 00000000..51534ba9
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
@@ -0,0 +1,239 @@
+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.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.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.PGPPad;
+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());
+
+ 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 PGPPad.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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
new file mode 100644
index 00000000..bd7f4999
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
@@ -0,0 +1,166 @@
+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.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.operator.PGPPad;
+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 = PGPPad.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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java
new file mode 100644
index 00000000..38e07cd0
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/OperatorHelper.java
@@ -0,0 +1,200 @@
+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.util.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");
+ case SymmetricKeyAlgorithmTags.CAMELLIA_128:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_192:
+ case SymmetricKeyAlgorithmTags.CAMELLIA_256:
+ return helper.createCipher("CamelliaWrap");
+ 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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java
new file mode 100644
index 00000000..5edbdb82
--- /dev/null
+++ b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/PGPUtil.java
@@ -0,0 +1,124 @@
+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.CAMELLIA_128:
+ return "Camellia";
+ case SymmetricKeyAlgorithmTags.CAMELLIA_192:
+ return "Camellia";
+ case SymmetricKeyAlgorithmTags.CAMELLIA_256:
+ return "Camellia";
+ 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 = getSymmetricCipherName(algorithm);
+
+ if (algName == null)
+ {
+ throw new PGPException("unknown symmetric algorithm: " + algorithm);
+ }
+
+ return new SecretKeySpec(keyBytes, algName);
+ }
+}
diff --git a/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SHA1PGPDigestCalculator.java
new file mode 100644
index 00000000..424770e6
--- /dev/null
+++ b/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/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java b/pg/src/main/java/org/spongycastle/openpgp/operator/jcajce/SignatureOutputStream.java
new file mode 100644
index 00000000..808de300
--- /dev/null
+++ b/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());
+ }
+ }
+}