diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-06-21 08:37:02 +0400 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-06-21 08:37:02 +0400 |
commit | 1d4caab51e298a21a56aea817815c1a63947e1f7 (patch) | |
tree | b2eafa042049f0770a6627472b041b1f2ef7b86f | |
parent | 0f8d170019d57997e69fd4c4b14c04126b870f01 (diff) | |
parent | d67d0613f3fe728c1268be28c8d8e5b99d985a2f (diff) |
Merge branch 'master' of git.bouncycastle.org:bc-java
15 files changed, 531 insertions, 92 deletions
diff --git a/docs/releasenotes.html b/docs/releasenotes.html index cdb2976e..7d1febd9 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -28,7 +28,8 @@ Release 1.51 <ul> <li>The AEAD GCM AlgorithmParameters object was unable to return a GCMParameterSpec object. This has been fixed.</li> <li>Cipher.getIV() was returning null for AEAD mode ciphers. This has been fixed.</li> -<li>CipherInputStream would fail for some AEAD mode ciphers if the message was over 4k in length. This has been fixed. +<li>CipherInputStream would fail for some AEAD mode ciphers if the message was over 4k in length. This has been fixed.</li> +<li>The JCE provider will now produce simple RSAPrivateKey objects where CRT coefficients are not provided.</li> </ul> <h3>2.1.3 Additional Features and Functionality</h3> <ul> diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 77597049..f5b4c9a8 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -399,7 +399,8 @@ public class PGPSignature } if (this.getSignatureType() != KEY_REVOCATION - && this.getSignatureType() != SUBKEY_REVOCATION) + && this.getSignatureType() != SUBKEY_REVOCATION + && this.getSignatureType() != DIRECT_KEY) { throw new PGPException("signature is not a key signature"); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPPad.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPPad.java new file mode 100644 index 00000000..c9cebe7d --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPPad.java @@ -0,0 +1,50 @@ +package org.bouncycastle.openpgp.operator; + +import org.bouncycastle.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/bouncycastle/openpgp/operator/bc/BcImplProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java index 87b303fd..357634f4 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcImplProvider.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.Wrapper; import org.bouncycastle.crypto.digests.MD2Digest; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.digests.RIPEMD160Digest; @@ -18,6 +19,7 @@ import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.digests.TigerDigest; import org.bouncycastle.crypto.encodings.PKCS1Encoding; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.engines.BlowfishEngine; import org.bouncycastle.crypto.engines.CAST5Engine; import org.bouncycastle.crypto.engines.CamelliaEngine; @@ -25,10 +27,12 @@ import org.bouncycastle.crypto.engines.DESEngine; import org.bouncycastle.crypto.engines.DESedeEngine; import org.bouncycastle.crypto.engines.ElGamalEngine; import org.bouncycastle.crypto.engines.IDEAEngine; +import org.bouncycastle.crypto.engines.RFC3394WrapEngine; import org.bouncycastle.crypto.engines.RSABlindedEngine; import org.bouncycastle.crypto.engines.TwofishEngine; import org.bouncycastle.crypto.signers.DSADigestSigner; import org.bouncycastle.crypto.signers.DSASigner; +import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.RSADigestSigner; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; @@ -73,8 +77,10 @@ class BcImplProvider 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"); + throw new PGPException("cannot recognise keyAlgorithm: " + keyAlgorithm); } } @@ -120,6 +126,24 @@ class BcImplProvider 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 { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java index 035c823a..0e346eed 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyConverter.java @@ -2,21 +2,29 @@ package org.bouncycastle.openpgp.operator.bc; import java.util.Date; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.bcpg.BCPGKey; import org.bouncycastle.bcpg.DSAPublicBCPGKey; import org.bouncycastle.bcpg.DSASecretBCPGKey; import org.bouncycastle.bcpg.ECDHPublicBCPGKey; import org.bouncycastle.bcpg.ECDSAPublicBCPGKey; +import org.bouncycastle.bcpg.ECPublicBCPGKey; +import org.bouncycastle.bcpg.ECSecretBCPGKey; import org.bouncycastle.bcpg.ElGamalPublicBCPGKey; import org.bouncycastle.bcpg.ElGamalSecretBCPGKey; +import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.bcpg.RSAPublicBCPGKey; import org.bouncycastle.bcpg.RSASecretBCPGKey; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; import org.bouncycastle.crypto.params.DSAPublicKeyParameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.ElGamalParameters; import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters; @@ -70,12 +78,12 @@ public class BcPGPKeyConverter ECPublicKeyParameters eK = (ECPublicKeyParameters)pubKey; if (algorithm == PGPPublicKey.EC) - { // TODO: KDF parameters - bcpgKey = new ECDHPublicBCPGKey(null, eK.getQ(), 0, 0); + { // TODO: KDF parameters setting + bcpgKey = new ECDHPublicBCPGKey(((ECNamedDomainParameters)eK.getParameters()).getName(), eK.getQ(), HashAlgorithmTags.SHA256, SymmetricKeyAlgorithmTags.AES_128); } else { - bcpgKey = new ECDSAPublicBCPGKey(null, eK.getQ()); + bcpgKey = new ECDSAPublicBCPGKey(((ECNamedDomainParameters)eK.getParameters()).getName(), eK.getQ()); } } else @@ -111,6 +119,12 @@ public class BcPGPKeyConverter 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"); } @@ -141,6 +155,13 @@ public class BcPGPKeyConverter 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 ecParams = ECNamedCurveTable.getByOID(ecPub.getCurveOID()); + + return new ECPublicKeyParameters(ecPub.getPoint(), new ECNamedDomainParameters(ecPub.getCurveOID(), ecParams.getCurve(), ecParams.getG(), ecParams.getN(), ecParams.getH())); default: throw new PGPException("unknown public key algorithm encountered"); } @@ -183,6 +204,14 @@ public class BcPGPKeyConverter 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 ecParams = ECNamedCurveTable.getByOID(ecPub.getCurveOID()); + + return new ECPrivateKeyParameters(ecPriv.getX(), new ECNamedDomainParameters(ecPub.getCurveOID(), ecParams.getCurve(), ecParams.getG(), ecParams.getN(), ecParams.getH())); default: throw new PGPException("unknown public key algorithm encountered"); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPair.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPair.java index ebf0fa0b..29460894 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPair.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPair.java @@ -27,7 +27,7 @@ public class BcPGPKeyPair public BcPGPKeyPair(int algorithm, AsymmetricCipherKeyPair keyPair, Date date) throws PGPException { - this.pub = getPublicKey(algorithm, (AsymmetricKeyParameter)keyPair.getPublic(), date); - this.priv = getPrivateKey(this.pub, (AsymmetricKeyParameter)keyPair.getPrivate()); + this.pub = getPublicKey(algorithm, keyPair.getPublic(), date); + this.priv = getPrivateKey(this.pub, keyPair.getPrivate()); } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java index faf782d5..1d77ff09 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java @@ -1,16 +1,25 @@ package org.bouncycastle.openpgp.operator.bc; +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.bcpg.ECDHPublicBCPGKey; +import org.bouncycastle.bcpg.ECSecretBCPGKey; import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedAsymmetricBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.Wrapper; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.bouncycastle.crypto.params.KeyParameter; +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.PGPPad; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.RFC6637KDFCalculator; /** * A decryptor factory for handling public key decryption operations. @@ -31,57 +40,87 @@ public class BcPublicKeyDataDecryptorFactory { try { - AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(keyAlgorithm); - - AsymmetricKeyParameter key = keyConverter.getPrivateKey(privKey); - - BufferedAsymmetricBlockCipher c1 = new BufferedAsymmetricBlockCipher(c); - - c1.init(false, key); - - if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT - || keyAlgorithm == PGPPublicKey.RSA_GENERAL) + if (keyAlgorithm != PGPPublicKey.ECDH) { - byte[] bi = secKeyData[0]; + AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(keyAlgorithm); - 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]; + AsymmetricKeyParameter key = keyConverter.getPrivateKey(privKey); - 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); - } + BufferedAsymmetricBlockCipher c1 = new BufferedAsymmetricBlockCipher(c); - bi = secKeyData[1]; // encoded MPI - for (int i = 0; i != tmp.length; i++) - { - tmp[i] = 0; - } + c1.init(false, key); - if (bi.length - 2 > size) // leading Zero? Shouldn't happen but... + if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT + || keyAlgorithm == PGPPublicKey.RSA_GENERAL) { - c1.processBytes(bi, 3, bi.length - 3); + byte[] bi = secKeyData[0]; + + c1.processBytes(bi, 2, bi.length - 2); } else { - System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2); - c1.processBytes(tmp, 0, tmp.length); + 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); - return c1.doFinal(); + 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) { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java index 53e71ad2..b1fa548d 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java @@ -1,14 +1,33 @@ package org.bouncycastle.openpgp.operator.bc; +import java.io.IOException; +import java.math.BigInteger; import java.security.SecureRandom; +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.crypto.AsymmetricBlockCipher; +import org.bouncycastle.crypto.EphemeralKeyPair; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.KeyEncoder; +import org.bouncycastle.crypto.Wrapper; +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.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.operator.PGPPad; import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.RFC6637KDFCalculator; /** * A method generator for supporting public key based encryption operations. @@ -47,22 +66,74 @@ public class BcPublicKeyKeyEncryptionMethodGenerator { try { - AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(pubKey.getAlgorithm()); + if (pubKey.getAlgorithm() != PGPPublicKey.ECDH) + { + AsymmetricBlockCipher c = BcImplProvider.createPublicKeyCipher(pubKey.getAlgorithm()); - AsymmetricKeyParameter key = keyConverter.getPublicKey(pubKey); + AsymmetricKeyParameter key = keyConverter.getPublicKey(pubKey); - if (random == null) - { - random = new SecureRandom(); + 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()); - c.init(true, new ParametersWithRandom(key, random)); + KeyParameter key = new KeyParameter(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, pubKey.getFingerprint())); - return c.processBlock(sessionInfo, 0, sessionInfo.length); + 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/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePublicKeyDataDecryptorFactoryBuilder.java index f626012b..589d17c3 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 @@ -25,6 +25,7 @@ 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.PGPPad; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.RFC6637KDFCalculator; @@ -159,7 +160,7 @@ public class JcePublicKeyDataDecryptorFactoryBuilder Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY); - return PGPUtil.unpadSessionData(paddedSessionKey.getEncoded()); + return PGPPad.unpadSessionData(paddedSessionKey.getEncoded()); } catch (InvalidKeyException 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 f15faa8e..c229f9f6 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 @@ -32,6 +32,7 @@ import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.operator.PGPPad; import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.RFC6637KDFCalculator; @@ -121,7 +122,7 @@ public class JcePublicKeyKeyEncryptionMethodGenerator c.init(Cipher.WRAP_MODE, key, random); - byte[] paddedSessionData = PGPUtil.padSessionData(sessionInfo); + 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(); 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 d58ff012..7da5bc5d 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 @@ -121,40 +121,4 @@ 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 595c97d2..77fc1e36 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDHTest.java @@ -12,8 +12,14 @@ import java.security.spec.ECGenParameterSpec; import java.util.Date; import java.util.Iterator; +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; @@ -34,6 +40,10 @@ import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair; +import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; @@ -242,6 +252,72 @@ public class PGPECDHTest } } + private void encryptDecryptBCTest() + 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' }; + + + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + + X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-256"); + keyGen.init(new ECKeyGenerationParameters(new ECNamedDomainParameters(NISTNamedCurves.getOID("P-256"), x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN()), new SecureRandom())); + + AsymmetricCipherKeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdhKeyPair = new BcPGPKeyPair(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 BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5).setSecureRandom(new SecureRandom())); + + cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(ecdhKeyPair.getPublicKey())); + + OutputStream cOut = cPk.open(new UncloseableOutputStream(cbOut), data.length); + + cOut.write(data); + + cOut.close(); + + JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(cbOut.toByteArray()); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpF.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(ecdhKeyPair.getPrivateKey())); + + pgpF = new JcaPGPObjectFactory(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 { @@ -262,6 +338,7 @@ public class PGPECDHTest testDecrypt(secretKeyRing); encryptDecryptTest(); + encryptDecryptBCTest(); generate(); } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDSATest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDSATest.java index 0e7ba9aa..e59c9760 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDSATest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECDSATest.java @@ -3,12 +3,19 @@ package org.bouncycastle.openpgp.test; import java.io.ByteArrayInputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.SecureRandom; import java.security.Security; import java.security.spec.ECGenParameterSpec; import java.util.Date; import java.util.Iterator; +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPKeyPair; @@ -22,6 +29,12 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; @@ -151,6 +164,88 @@ public class PGPECDSATest } } + private void generateAndSignBC() + throws Exception + { + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + + X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-256"); + keyGen.init(new ECKeyGenerationParameters(new ECNamedDomainParameters(NISTNamedCurves.getOID("P-256"), x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN()), new SecureRandom())); + + AsymmetricCipherKeyPair kpEnc = keyGen.generateKeyPair(); + + PGPKeyPair ecdsaKeyPair = new BcPGPKeyPair(PGPPublicKey.ECDSA, kpEnc, new Date()); + + // + // try a signature + // + PGPSignatureGenerator signGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.ECDSA, HashAlgorithmTags.SHA256)); + + signGen.init(PGPSignature.BINARY_DOCUMENT, ecdsaKeyPair.getPrivateKey()); + + signGen.update("hello world!".getBytes()); + + PGPSignature sig = signGen.generate(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), ecdsaKeyPair.getPublicKey()); + + sig.update("hello world!".getBytes()); + + if (!sig.verify()) + { + fail("signature failed to verify!"); + } + + // + // generate a key ring + // + char[] passPhrase = "test".toCharArray(); + PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1); + PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, ecdsaKeyPair, + "test@bouncycastle.org", sha1Calc, null, null, new BcPGPContentSignerBuilder(ecdsaKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1), new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha1Calc).setProvider("BC").build(passPhrase)); + + PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing(); + + PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing(); + + KeyFingerPrintCalculator fingerCalc = new BcKeyFingerprintCalculator(); + + PGPPublicKeyRing pubRingEnc = new PGPPublicKeyRing(pubRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(pubRing.getEncoded(), pubRingEnc.getEncoded())) + { + fail("public key ring encoding failed"); + } + + PGPSecretKeyRing secRingEnc = new PGPSecretKeyRing(secRing.getEncoded(), fingerCalc); + + if (!Arrays.areEqual(secRing.getEncoded(), secRingEnc.getEncoded())) + { + fail("secret key ring encoding failed"); + } + + + // + // try a signature using encoded key + // + signGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.ECDSA, HashAlgorithmTags.SHA256)); + + signGen.init(PGPSignature.BINARY_DOCUMENT, secRing.getSecretKey().extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passPhrase))); + + signGen.update("hello world!".getBytes()); + + sig = signGen.generate(); + + sig.init(new BcPGPContentVerifierBuilderProvider(), secRing.getSecretKey().getPublicKey()); + + sig.update("hello world!".getBytes()); + + if (!sig.verify()) + { + fail("re-encoded signature failed to verify!"); + } + } + public void performTest() throws Exception { @@ -179,6 +274,7 @@ public class PGPECDSATest PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(testPrivKey, new JcaKeyFingerprintCalculator()); generateAndSign(); + generateAndSignBC(); // // sExpr diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECMessageTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECMessageTest.java index 40743d13..7183b91f 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECMessageTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPECMessageTest.java @@ -21,6 +21,7 @@ import org.bouncycastle.openpgp.PGPSignatureList; import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRing; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; @@ -189,12 +190,95 @@ public class PGPECMessageTest } } + private void testBCEncMessage() + throws Exception + { + PGPObjectFactory pgpFact = new JcaPGPObjectFactory(encMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpFact.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + PGPPublicKey publicKey = new JcaPGPPublicKeyRing(testPubKey).getPublicKey(encP.getKeyID()); + + PGPSecretKey secretKey = PGPSecretKey.parseSecretKeyFromSExpr(new ByteArrayInputStream(sExprKeySub), new JcePBEProtectionRemoverFactory("test".toCharArray()), publicKey); + + InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(secretKey.extractPrivateKey(null))); + + PGPObjectFactory plainFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator()); + + PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject(); + + PGPObjectFactory compFact = new PGPObjectFactory(cData.getDataStream(), new BcKeyFingerprintCalculator()); + + PGPLiteralData lData = (PGPLiteralData)compFact.nextObject(); + + if (!"test.txt".equals(lData.getFileName())) + { + fail("wrong file name detected"); + } + } + + private void testBCSignedEncMessage() + throws Exception + { + PGPObjectFactory pgpFact = new JcaPGPObjectFactory(signedEncMessage); + + PGPEncryptedDataList encList = (PGPEncryptedDataList)pgpFact.nextObject(); + + PGPPublicKeyEncryptedData encP = (PGPPublicKeyEncryptedData)encList.get(0); + + JcaPGPPublicKeyRing publicKeyRing = new JcaPGPPublicKeyRing(testPubKey); + + PGPPublicKey publicKey = publicKeyRing.getPublicKey(encP.getKeyID()); + + PGPSecretKey secretKey = PGPSecretKey.parseSecretKeyFromSExpr(new ByteArrayInputStream(sExprKeySub), new JcePBEProtectionRemoverFactory("test".toCharArray()), publicKey); + + InputStream clear = encP.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(secretKey.extractPrivateKey(null))); + + PGPObjectFactory plainFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator()); + + PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject(); + + PGPObjectFactory compFact = new PGPObjectFactory(cData.getDataStream(), new BcKeyFingerprintCalculator()); + + PGPOnePassSignatureList sList = (PGPOnePassSignatureList)compFact.nextObject(); + + PGPOnePassSignature ops = sList.get(0); + + PGPLiteralData lData = (PGPLiteralData)compFact.nextObject(); + + if (!"test.txt".equals(lData.getFileName())) + { + fail("wrong file name detected"); + } + + InputStream dIn = lData .getInputStream(); + int ch; + + ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), publicKeyRing.getPublicKey(ops.getKeyID())); + + while ((ch = dIn.read()) >= 0) + { + ops.update((byte)ch); + } + + PGPSignatureList p3 = (PGPSignatureList)compFact.nextObject(); + + if (!ops.verify(p3.get(0))) + { + fail("Failed signature check"); + } + } + public void performTest() throws Exception { testMasterKey(); testEncMessage(); testSignedEncMessage(); + testBCEncMessage(); + testBCSignedEncMessage(); } public String getName() diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java index 35ae0355..b0990d62 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java @@ -27,6 +27,7 @@ public class RegressionTest new PGPNoPrivateKeyTest(), new PGPECDSATest(), new PGPECDHTest(), + new PGPECMessageTest(), new PGPParsingTest() }; |