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
path: root/pg
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2013-11-11 09:15:09 +0400
committerDavid Hook <dgh@cryptoworkshop.com>2013-11-11 09:15:09 +0400
commit18e00b832084aa03f6b59a0893a203e4d8659015 (patch)
tree19b3daa1c763d960f93c846af58d041ac1220153 /pg
parent738544026e62ed5836a278273f76b5bdf5947e7b (diff)
added support of OpenPGP ECDH KDF (RFC 6637)
Diffstat (limited to 'pg')
-rw-r--r--pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java13
-rw-r--r--pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java86
-rw-r--r--pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java158
-rw-r--r--pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java82
-rw-r--r--pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java22
-rw-r--r--pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/PGPUtil.java36
-rw-r--r--pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java141
7 files changed, 428 insertions, 110 deletions
diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
index 3cb556bc..a935dc33 100644
--- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
+++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyEncSessionPacket.java
@@ -2,8 +2,8 @@ package org.bouncycastle.bcpg;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.math.BigInteger;
+import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.Streams;
/**
@@ -62,7 +62,7 @@ public class PublicKeyEncSessionPacket
public PublicKeyEncSessionPacket(
long keyID,
int algorithm,
- BigInteger[] data)
+ byte[][] data)
{
this.version = 3;
this.keyID = keyID;
@@ -71,14 +71,7 @@ public class PublicKeyEncSessionPacket
for (int i = 0; i != data.length; i++)
{
- try
- {
- this.data[i] = new MPInteger(data[i]).getEncoded();
- }
- catch (IOException e)
- {
- throw new IllegalArgumentException("Invalid BigInteger passed to PublicKeyEncSessionPacket");
- }
+ this.data[i] = Arrays.clone(data[i]);
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java
index f93bd079..58160a97 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java
@@ -1,8 +1,10 @@
package org.bouncycastle.openpgp.operator;
+import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.bcpg.ContainedPacket;
+import org.bouncycastle.bcpg.MPInteger;
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -19,56 +21,74 @@ public abstract class PublicKeyKeyEncryptionMethodGenerator
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());
+ 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 BigInteger[] processSessionInfo(
+ public byte[][] processSessionInfo(
byte[] encryptedSessionInfo)
throws PGPException
{
- BigInteger[] data;
+ byte[][] data;
switch (pubKey.getAlgorithm())
{
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- data = new BigInteger[1];
+ case PGPPublicKey.RSA_ENCRYPT:
+ case PGPPublicKey.RSA_GENERAL:
+ data = new byte[1][];
- data[0] = new BigInteger(1, encryptedSessionInfo);
- break;
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL:
- byte[] b1 = new byte[encryptedSessionInfo.length / 2];
- byte[] b2 = new byte[encryptedSessionInfo.length / 2];
+ 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);
+ System.arraycopy(encryptedSessionInfo, 0, b1, 0, b1.length);
+ System.arraycopy(encryptedSessionInfo, b1.length, b2, 0, b2.length);
- data = new BigInteger[2];
- data[0] = new BigInteger(1, b1);
- data[1] = new BigInteger(1, b2);
- break;
- default:
- throw new PGPException("unknown asymmetric algorithm: " + pubKey.getAlgorithm());
+ 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
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
index 9d7a1312..2bb0d96d 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java
@@ -1,26 +1,41 @@
package org.bouncycastle.openpgp.operator.jcajce;
+import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
+import java.security.Key;
import java.security.PrivateKey;
import java.security.Provider;
import javax.crypto.Cipher;
-
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.BCPGKey;
+import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
+import org.bouncycastle.bcpg.ECSecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
import org.bouncycastle.jce.interfaces.ElGamalKey;
+import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.bouncycastle.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()
{
@@ -77,6 +92,10 @@ public class JcePublicKeyDataDecryptorFactoryBuilder
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);
}
@@ -88,7 +107,6 @@ public class JcePublicKeyDataDecryptorFactoryBuilder
};
}
-
public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey)
{
return new PublicKeyDataDecryptorFactory()
@@ -96,6 +114,11 @@ public class JcePublicKeyDataDecryptorFactoryBuilder
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);
}
@@ -107,79 +130,108 @@ public class JcePublicKeyDataDecryptorFactoryBuilder
};
}
+ private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData)
+ throws PGPException
+ {
+ ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
+ X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID());
+ ECDomainParameters ecParams = new ECDomainParameters(x9Params.getCurve(), x9Params.getG(), x9Params.getN());
+
+ byte[] enc = secKeyData[0];
+
+ int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+ byte[] pEnc = new byte[pLen];
+
+ System.arraycopy(enc, 2, pEnc, 0, pLen);
+
+ byte[] keyEnc = new byte[enc[pLen + 2]];
+
+ System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length);
+
+ Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm());
+
+ ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize();
+
+ RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
+
+ Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap");
+
+ try
+ {
+ c.init(Cipher.UNWRAP_MODE, key);
+
+ Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
+
+ return PGPUtil.unpadSessionData(paddedSessionKey.getEncoded());
+ }
+ catch (GeneralSecurityException 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);
- byte[] plain;
- if (keyAlgorithm == PGPPublicKey.ECDH)
+ 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)
{
- plain = null;
+ byte[] bi = secKeyData[0]; // encoded MPI
+
+ c1.update(bi, 2, bi.length - 2);
}
else
{
- try
- {
- c1.init(Cipher.DECRYPT_MODE, privKey);
- }
- catch (InvalidKeyException e)
- {
- throw new PGPException("error setting asymmetric cipher", e);
- }
+ ElGamalKey k = (ElGamalKey)privKey;
+ int size = (k.getParameters().getP().bitLength() + 7) / 8;
+ byte[] tmp = new byte[size];
- if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT
- || keyAlgorithm == PGPPublicKey.RSA_GENERAL)
+ byte[] bi = secKeyData[0]; // encoded MPI
+ if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
{
- byte[] bi = secKeyData[0]; // encoded MPI
-
- c1.update(bi, 2, bi.length - 2);
+ c1.update(bi, 3, bi.length - 3);
}
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);
- }
+ 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;
+ }
- try
+ if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
{
- plain = c1.doFinal();
+ c1.update(bi, 3, bi.length - 3);
}
- catch (Exception e)
+ else
{
- throw new PGPException("exception decrypting session data", e);
+ System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
+ c1.update(tmp);
}
}
- return plain;
+ try
+ {
+ return c1.doFinal();
+ }
+ catch (Exception e)
+ {
+ throw new PGPException("exception decrypting session data", e);
+ }
}
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
index 97418533..08e227f7 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyKeyEncryptionMethodGenerator.java
@@ -1,5 +1,7 @@
package org.bouncycastle.openpgp.operator.jcajce;
+import java.io.IOException;
+import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.Provider;
@@ -8,13 +10,30 @@ import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
-
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
+import org.bouncycastle.bcpg.MPInteger;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.crypto.EphemeralKeyPair;
+import org.bouncycastle.crypto.KeyEncoder;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.DefaultJcaJceHelper;
import org.bouncycastle.jcajce.NamedJcaJceHelper;
import org.bouncycastle.jcajce.ProviderJcaJceHelper;
+import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator;
+import org.bouncycastle.openpgp.operator.RFC6637KDFCalculator;
public class JcePublicKeyKeyEncryptionMethodGenerator
extends PublicKeyKeyEncryptionMethodGenerator
@@ -22,6 +41,7 @@ public class JcePublicKeyKeyEncryptionMethodGenerator
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.
@@ -69,13 +89,61 @@ public class JcePublicKeyKeyEncryptionMethodGenerator
{
try
{
- Cipher c = helper.createPublicKeyCipher(pubKey.getAlgorithm());
+ 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());
- Key key = keyConverter.getPublicKey(pubKey);
+ c.init(Cipher.WRAP_MODE, key, random);
- c.init(Cipher.ENCRYPT_MODE, key, random);
+ byte[] paddedSessionData = PGPUtil.padSessionData(sessionInfo);
- return c.doFinal(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)
{
@@ -89,5 +157,9 @@ public class JcePublicKeyKeyEncryptionMethodGenerator
{
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/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
index 23f065bf..3a5e90c2 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/OperatorHelper.java
@@ -13,6 +13,7 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jcajce.JcaJceHelper;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -131,6 +132,27 @@ class OperatorHelper
}
}
+ Cipher createKeyWrapper(int encAlgorithm)
+ throws PGPException
+ {
+ try
+ {
+ switch (encAlgorithm)
+ {
+ case SymmetricKeyAlgorithmTags.AES_128:
+ case SymmetricKeyAlgorithmTags.AES_192:
+ case SymmetricKeyAlgorithmTags.AES_256:
+ return helper.createCipher("AESWrap");
+ default:
+ throw new PGPException("unknown wrap algorithm: " + encAlgorithm);
+ }
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new PGPException("cannot create cipher: " + e.getMessage(), e);
+ }
+ }
+
private Signature createSignature(String cipherName)
throws PGPException
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/PGPUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/PGPUtil.java
index a9fefb7b..edcaf1fb 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/PGPUtil.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/PGPUtil.java
@@ -146,4 +146,40 @@ class PGPUtil
return new SecretKeySpec(keyBytes, algName);
}
+
+ public static byte[] padSessionData(byte[] sessionInfo)
+ {
+ byte[] result = new byte[40];
+
+ System.arraycopy(sessionInfo, 0, result, 0, sessionInfo.length);
+
+ byte padValue = (byte)(result.length -sessionInfo.length);
+
+ for (int i = sessionInfo.length; i != result.length; i++)
+ {
+ result[i] = padValue;
+ }
+
+ return result;
+ }
+
+ public static byte[] unpadSessionData(byte[] encoded)
+ throws PGPException
+ {
+ byte padValue = encoded[encoded.length - 1];
+
+ for (int i = encoded.length - padValue; i != encoded.length; i++)
+ {
+ if (encoded[i] != padValue)
+ {
+ throw new PGPException("bad padding found in session data");
+ }
+ }
+
+ byte[] taggedKey = new byte[encoded.length - padValue];
+
+ System.arraycopy(encoded, 0, taggedKey, 0, taggedKey.length);
+
+ return taggedKey;
+ }
}
diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java
index 9caf5e50..f462e957 100644
--- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java
+++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java
@@ -1,7 +1,11 @@
package org.bouncycastle.openpgp.test;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec;
@@ -9,13 +13,21 @@ import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPEncryptedData;
+import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
+import org.bouncycastle.openpgp.PGPLiteralData;
+import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUtil;
@@ -27,24 +39,28 @@ import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProv
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.UncloseableOutputStream;
public class PGPECDHTest
extends SimpleTest
{
byte[] testPubKey =
Base64.decode(
- "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT\n" +
- "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB\n" +
- "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l\n" +
- "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV\n" +
- "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi\n" +
- "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr\n" +
- "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy\n" +
- "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI\n" +
- "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS\n" +
+ "mFIEUb4GwBMIKoZIzj0DAQcCAwS8p3TFaRAx58qCG63W+UNthXBPSJDnVDPTb/sT" +
+ "iXePaAZ/Gh1GKXTq7k6ab/67MMeVFp/EdySumqdWLtvceFKstFBUZXN0IEVDRFNB" +
+ "LUVDREggKEtleSBhbmQgc3Via2V5IGFyZSAyNTYgYml0cyBsb25nKSA8dGVzdC5l" +
+ "Y2RzYS5lY2RoQGV4YW1wbGUuY29tPoh6BBMTCAAiBQJRvgbAAhsDBgsJCAcDAgYV" +
+ "CAIJCgsEFgIDAQIeAQIXgAAKCRD3wDlWjFo9U5O2AQDi89NO6JbaIObC63jMMWsi" +
+ "AaQHrBCPkDZLibgNv73DLgD/faouH4YZJs+cONQBPVnP1baG1NpWR5ppN3JULFcr" +
+ "hcq4VgRRvgbAEggqhkjOPQMBBwIDBLtY8Nmfz0zSEa8C1snTOWN+VcT8pXPwgJRy" +
+ "z6kSP4nPt1xj1lPKj5zwPXKWxMkPO9ocqhKdg2mOh6/rc1ObIoMDAQgHiGEEGBMI" +
+ "AAkFAlG+BsACGwwACgkQ98A5VoxaPVN8cgEAj4dMNMNwRSg2ZBWunqUAHqIedVbS" +
"dmwmbysD192L3z4A/ReXEa0gtv8OFWjuALD1ovEK8TpDORLUb6IuUb5jUIzY");
byte[] testPrivKey =
@@ -58,6 +74,14 @@ public class PGPECDHTest
"AheAAAoJEPfAOVaMWj1Tk7YBAOLz007oltog5sLreMwxayIBpAesEI+QNkuJuA2/" +
"vcMuAP99qi4fhhkmz5w41AE9Wc/VtobU2lZHmmk3clQsVyuFyg==");
+ byte[] testMessage =
+ Base64.decode(
+ "hH4Dp5+FdoujIBwSAgMErx4BSvgXY3irwthgxU8zPoAoR+8rhmxdpwbw6ZJAO2GX" +
+ "azWJ85JNcobHKDeGeUq6wkTFu+g6yG99gIX8J5xJAjBRhyCRcaFgwbdDV4orWTe3" +
+ "iewiT8qs4BQ23e0c8t+thdKoK4thMsCJy7wSKqY0sJTSVAELroNbCOi2lcO15YmW" +
+ "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" +
+ "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg==");
+
private void generate()
throws Exception
{
@@ -119,6 +143,101 @@ public class PGPECDHTest
}
}
+ private void testDecrypt(PGPSecretKeyRing secretKeyRing)
+ throws Exception
+ {
+ PGPObjectFactory pgpF = new PGPObjectFactory(testMessage);
+
+ PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject();
+
+ PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0);
+
+ PGPSecretKey secretKey = secretKeyRing.getSecretKey(); // secretKeyRing.getSecretKey(encP.getKeyID());
+//
+// PGPPrivateKey pgpPrivKey = secretKey.extractPrivateKey()extractPrivateKey(null);
+//
+// clear = encP.getDataStream(pgpPrivKey, "BC");
+//
+// bOut.reset();
+//
+// while ((ch = clear.read()) >= 0)
+// {
+// bOut.write(ch);
+// }
+//
+// out = bOut.toByteArray();
+//
+// if (!areEqual(out, text))
+// {
+// fail("wrong plain text in generated packet");
+// }
+ }
+
+ private void encryptDecryptTest()
+ throws Exception
+ {
+ byte[] text = { (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)' ', (byte)'w', (byte)'o', (byte)'r', (byte)'l', (byte)'d', (byte)'!', (byte)'\n' };
+
+
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH", "BC");
+
+ keyGen.initialize(new ECGenParameterSpec("P-256"));
+
+ KeyPair kpEnc = keyGen.generateKeyPair();
+
+ PGPKeyPair ecdhKeyPair = new JcaPGPKeyPair(PGPPublicKey.ECDH, kpEnc, new Date());
+
+ PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
+ ByteArrayOutputStream ldOut = new ByteArrayOutputStream();
+ OutputStream pOut = lData.open(ldOut, PGPLiteralDataGenerator.UTF8, PGPLiteralData.CONSOLE, text.length, new Date());
+
+ pOut.write(text);
+
+ pOut.close();
+
+ byte[] data = ldOut.toByteArray();
+
+ ByteArrayOutputStream cbOut = new ByteArrayOutputStream();
+
+ PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setProvider("BC").setSecureRandom(new SecureRandom()));
+
+ cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(ecdhKeyPair.getPublicKey()).setProvider("BC"));
+
+ OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), data.length);
+
+ cOut.write(data);
+
+ cOut.close();
+
+ PGPObjectFactory pgpF = new PGPObjectFactory(cbOut.toByteArray());
+
+ PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject();
+
+ PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0);
+
+ InputStream clear = encP.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(ecdhKeyPair.getPrivateKey()));
+
+ pgpF = new PGPObjectFactory(clear);
+
+ PGPLiteralData ld = (PGPLiteralData)pgpF.nextObject();
+
+ clear = ld.getInputStream();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ int ch;
+ while ((ch = clear.read()) >= 0)
+ {
+ bOut.write(ch);
+ }
+
+ byte[] out = bOut.toByteArray();
+
+ if (!areEqual(out, text))
+ {
+ fail("wrong plain text in generated packet");
+ }
+ }
+
public void performTest()
throws Exception
{
@@ -136,6 +255,10 @@ public class PGPECDHTest
//
PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator());
+ testDecrypt(secretKeyRing);
+
+ encryptDecryptTest();
+
generate();
}