package org.ripple.bouncycastle.jcajce.provider.asymmetric.ec; import java.io.IOException; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.Hashtable; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; import org.ripple.bouncycastle.asn1.ASN1Encoding; import org.ripple.bouncycastle.asn1.ASN1ObjectIdentifier; import org.ripple.bouncycastle.asn1.DERNull; import org.ripple.bouncycastle.asn1.cms.ecc.ECCCMSSharedInfo; import org.ripple.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.ripple.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.ripple.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.ripple.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.ripple.bouncycastle.asn1.x9.X9IntegerConverter; import org.ripple.bouncycastle.crypto.BasicAgreement; import org.ripple.bouncycastle.crypto.CipherParameters; import org.ripple.bouncycastle.crypto.DerivationFunction; import org.ripple.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.ripple.bouncycastle.crypto.agreement.ECDHCBasicAgreement; import org.ripple.bouncycastle.crypto.agreement.ECMQVBasicAgreement; import org.ripple.bouncycastle.crypto.agreement.kdf.ConcatenationKDFGenerator; import org.ripple.bouncycastle.crypto.agreement.kdf.DHKDFParameters; import org.ripple.bouncycastle.crypto.agreement.kdf.ECDHKEKGenerator; import org.ripple.bouncycastle.crypto.digests.SHA1Digest; import org.ripple.bouncycastle.crypto.digests.SHA224Digest; import org.ripple.bouncycastle.crypto.digests.SHA256Digest; import org.ripple.bouncycastle.crypto.digests.SHA384Digest; import org.ripple.bouncycastle.crypto.digests.SHA512Digest; import org.ripple.bouncycastle.crypto.generators.KDF2BytesGenerator; import org.ripple.bouncycastle.crypto.params.DESParameters; import org.ripple.bouncycastle.crypto.params.ECDomainParameters; import org.ripple.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.ripple.bouncycastle.crypto.params.ECPublicKeyParameters; import org.ripple.bouncycastle.crypto.params.KDFParameters; import org.ripple.bouncycastle.crypto.params.MQVPrivateParameters; import org.ripple.bouncycastle.crypto.params.MQVPublicParameters; import org.ripple.bouncycastle.jcajce.provider.asymmetric.util.DESUtil; import org.ripple.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.ripple.bouncycastle.jcajce.provider.asymmetric.util.KeyMaterialGenerator; import org.ripple.bouncycastle.jcajce.spec.MQVParameterSpec; import org.ripple.bouncycastle.jcajce.spec.UserKeyingMaterialSpec; import org.ripple.bouncycastle.jce.interfaces.ECPrivateKey; import org.ripple.bouncycastle.jce.interfaces.ECPublicKey; import org.ripple.bouncycastle.jce.interfaces.MQVPrivateKey; import org.ripple.bouncycastle.jce.interfaces.MQVPublicKey; import org.ripple.bouncycastle.util.Integers; import org.ripple.bouncycastle.util.Pack; import org.ripple.bouncycastle.util.Strings; import org.ripple.bouncycastle.util.encoders.Hex; /** * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363 * both the simple one, and the simple one with cofactors are supported. * * Also, MQV key agreement per SEC-1 */ public class KeyAgreementSpi extends javax.crypto.KeyAgreementSpi { private static final X9IntegerConverter converter = new X9IntegerConverter(); private static final Hashtable algorithms = new Hashtable(); private static final Hashtable algorithmNames = new Hashtable(); private static final Hashtable oids = new Hashtable(); private static final Hashtable des = new Hashtable(); static { Integer i64 = Integers.valueOf(64); Integer i128 = Integers.valueOf(128); Integer i192 = Integers.valueOf(192); Integer i256 = Integers.valueOf(256); algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); algorithms.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), i192); algorithms.put(OIWObjectIdentifiers.desCBC.getId(), i64); algorithms.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), Integers.valueOf(160)); algorithms.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), i256); algorithms.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), Integers.valueOf(384)); algorithms.put(PKCSObjectIdentifiers.id_hmacWithSHA512.getId(), Integers.valueOf(512)); algorithmNames.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), "AES"); algorithmNames.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), "AES"); algorithmNames.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), "AES"); algorithmNames.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), "AES"); algorithmNames.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), "AES"); algorithmNames.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), "AES"); algorithmNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DESEDE"); algorithmNames.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DESEDE"); algorithmNames.put(OIWObjectIdentifiers.desCBC.getId(), "DES"); oids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC); oids.put("AES", NISTObjectIdentifiers.id_aes256_CBC); oids.put("DES", OIWObjectIdentifiers.desCBC); des.put("DES", "DES"); des.put("DESEDE", "DES"); des.put(OIWObjectIdentifiers.desCBC.getId(), "DES"); des.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DES"); des.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DES"); } private static KeyMaterialGenerator old_ecc_cms_Generator = new KeyMaterialGenerator() { public byte[] generateKDFMaterial(ASN1ObjectIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters) { ECCCMSSharedInfo eccInfo; // this isn't correct with AES and RFC 5753, but we have messages predating it... eccInfo = new ECCCMSSharedInfo(new AlgorithmIdentifier(keyAlgorithm, DERNull.INSTANCE), userKeyMaterialParameters, Pack.intToBigEndian(keySize)); try { return eccInfo.getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new IllegalStateException("Unable to create KDF material: " + e); } } }; private static KeyMaterialGenerator ecc_cms_Generator = new KeyMaterialGenerator() { public byte[] generateKDFMaterial(ASN1ObjectIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters) { ECCCMSSharedInfo eccInfo; if (DESUtil.isDES(keyAlgorithm.getId()) || keyAlgorithm.equals(PKCSObjectIdentifiers.id_alg_CMSRC2wrap)) { eccInfo = new ECCCMSSharedInfo(new AlgorithmIdentifier(keyAlgorithm, DERNull.INSTANCE), userKeyMaterialParameters, Pack.intToBigEndian(keySize)); } else { eccInfo = new ECCCMSSharedInfo(new AlgorithmIdentifier(keyAlgorithm), userKeyMaterialParameters, Pack.intToBigEndian(keySize)); } try { return eccInfo.getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new IllegalStateException("Unable to create KDF material: " + e); } } }; private KeyMaterialGenerator kmGen; private String kaAlgorithm; private BigInteger result; private ECDomainParameters parameters; private BasicAgreement agreement; private DerivationFunction kdf; private MQVParameterSpec mqvParameters; private byte[] ukmParameters; private byte[] bigIntToBytes( BigInteger r) { return converter.integerToBytes(r, converter.getByteLength(parameters.getCurve())); } protected KeyAgreementSpi( String kaAlgorithm, BasicAgreement agreement, DerivationFunction kdf) { this(kaAlgorithm, agreement, kdf, null); } protected KeyAgreementSpi( String kaAlgorithm, BasicAgreement agreement, DerivationFunction kdf, KeyMaterialGenerator kmGen) { this.kaAlgorithm = kaAlgorithm; this.agreement = agreement; this.kdf = kdf; this.kmGen = kmGen; } protected Key engineDoPhase( Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { if (parameters == null) { throw new IllegalStateException(kaAlgorithm + " not initialised."); } if (!lastPhase) { throw new IllegalStateException(kaAlgorithm + " can only be between two parties."); } CipherParameters pubKey; if (agreement instanceof ECMQVBasicAgreement) { if (!(key instanceof MQVPublicKey)) { ECPublicKeyParameters staticKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter((PublicKey)key); ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(mqvParameters.getOtherPartyEphemeralKey()); pubKey = new MQVPublicParameters(staticKey, ephemKey); } else { MQVPublicKey mqvPubKey = (MQVPublicKey)key; ECPublicKeyParameters staticKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey()); ECPublicKeyParameters ephemKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey()); pubKey = new MQVPublicParameters(staticKey, ephemKey); // TODO Validate that all the keys are using the same parameters? } } else { if (!(key instanceof PublicKey)) { throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + getSimpleName(ECPublicKey.class) + " for doPhase"); } pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key); // TODO Validate that all the keys are using the same parameters? } result = agreement.calculateAgreement(pubKey); return null; } protected byte[] engineGenerateSecret() throws IllegalStateException { if (kdf != null) { throw new UnsupportedOperationException( "KDF can only be used when algorithm is known"); } return bigIntToBytes(result); } protected int engineGenerateSecret( byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { byte[] secret = engineGenerateSecret(); if (sharedSecret.length - offset < secret.length) { throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); } System.arraycopy(secret, 0, sharedSecret, offset, secret.length); return secret.length; } protected SecretKey engineGenerateSecret( String algorithm) throws NoSuchAlgorithmException { byte[] secret = bigIntToBytes(result); String algKey = Strings.toUpperCase(algorithm); String oidAlgorithm = algorithm; if (oids.containsKey(algKey)) { oidAlgorithm = ((ASN1ObjectIdentifier)oids.get(algKey)).getId(); } if (kdf != null) { if (!algorithms.containsKey(oidAlgorithm)) { throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm); } int keySize = ((Integer)algorithms.get(oidAlgorithm)).intValue(); if (kmGen != null) { KDFParameters params = new KDFParameters(secret, kmGen.generateKDFMaterial(new ASN1ObjectIdentifier(oidAlgorithm), keySize, ukmParameters)); byte[] keyBytes = new byte[keySize / 8]; kdf.init(params); kdf.generateBytes(keyBytes, 0, keyBytes.length); secret = keyBytes; } else if (ukmParameters != null) { KDFParameters params = new KDFParameters(secret, ukmParameters); byte[] keyBytes = new byte[keySize / 8]; kdf.init(params); kdf.generateBytes(keyBytes, 0, keyBytes.length); secret = keyBytes; } else { DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(oidAlgorithm), keySize, secret); byte[] keyBytes = new byte[keySize / 8]; ECDHKEKGenerator kekGen = new ECDHKEKGenerator(new SHA1Digest()); kekGen.init(params); kekGen.generateBytes(keyBytes, 0, keyBytes.length); secret = keyBytes; } } else { if (algorithms.containsKey(oidAlgorithm)) { Integer length = (Integer)algorithms.get(oidAlgorithm); byte[] key = new byte[length.intValue() / 8]; System.arraycopy(secret, 0, key, 0, key.length); secret = key; } } if (des.containsKey(oidAlgorithm)) { DESParameters.setOddParity(secret); } return new SecretKeySpec(secret, getAlgorithmName(algorithm)); } private String getAlgorithmName(String algorithm) { if (algorithmNames.containsKey(algorithm)) { return (String)algorithmNames.get(algorithm); } return algorithm; } protected void engineInit( Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { if (params != null && !(params instanceof MQVParameterSpec || params instanceof UserKeyingMaterialSpec)) { throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); } initFromKey(key, params); } protected void engineInit( Key key, SecureRandom random) throws InvalidKeyException { initFromKey(key, null); } private void initFromKey(Key key, AlgorithmParameterSpec parameterSpec) throws InvalidKeyException { if (agreement instanceof ECMQVBasicAgreement) { mqvParameters = null; if (!(key instanceof MQVPrivateKey) && !(parameterSpec instanceof MQVParameterSpec)) { throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + getSimpleName(MQVParameterSpec.class) + " for initialisation"); } ECPrivateKeyParameters staticPrivKey; ECPrivateKeyParameters ephemPrivKey; ECPublicKeyParameters ephemPubKey; if (key instanceof MQVPrivateKey) { MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key; staticPrivKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey()); ephemPrivKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey()); ephemPubKey = null; if (mqvPrivKey.getEphemeralPublicKey() != null) { ephemPubKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey()); } } else { MQVParameterSpec mqvParameterSpec = (MQVParameterSpec)parameterSpec; staticPrivKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter((PrivateKey)key); ephemPrivKey = (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(mqvParameterSpec.getEphemeralPrivateKey()); ephemPubKey = null; if (mqvParameterSpec.getEphemeralPublicKey() != null) { ephemPubKey = (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(mqvParameterSpec.getEphemeralPublicKey()); } mqvParameters = mqvParameterSpec; ukmParameters = mqvParameterSpec.getUserKeyingMaterial(); } MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey); this.parameters = staticPrivKey.getParameters(); // TODO Validate that all the keys are using the same parameters? agreement.init(localParams); } else { if (!(key instanceof PrivateKey)) { throw new InvalidKeyException(kaAlgorithm + " key agreement requires " + getSimpleName(ECPrivateKey.class) + " for initialisation"); } ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key); this.parameters = privKey.getParameters(); ukmParameters = (parameterSpec instanceof UserKeyingMaterialSpec) ? ((UserKeyingMaterialSpec)parameterSpec).getUserKeyingMaterial() : null; agreement.init(privKey); } } private static String getSimpleName(Class clazz) { String fullName = clazz.getName(); return fullName.substring(fullName.lastIndexOf('.') + 1); } public static class DH extends KeyAgreementSpi { public DH() { super("ECDH", new ECDHBasicAgreement(), null); } } public static class DHC extends KeyAgreementSpi { public DHC() { super("ECDHC", new ECDHCBasicAgreement(), null); } } public static class MQV extends KeyAgreementSpi { public MQV() { super("ECMQV", new ECMQVBasicAgreement(), null); } } public static class DHwithSHA1KDF extends KeyAgreementSpi { public DHwithSHA1KDF() { super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA1Digest())); } } public static class DHwithSHA1KDFAndSharedInfo extends KeyAgreementSpi { public DHwithSHA1KDFAndSharedInfo() { super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA1Digest()), old_ecc_cms_Generator); } } public static class CDHwithSHA1KDFAndSharedInfo extends KeyAgreementSpi { public CDHwithSHA1KDFAndSharedInfo() { super("ECCDHwithSHA1KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA1Digest()), ecc_cms_Generator); } } public static class DHwithSHA224KDFAndSharedInfo extends KeyAgreementSpi { public DHwithSHA224KDFAndSharedInfo() { super("ECDHwithSHA224KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA224Digest()), ecc_cms_Generator); } } public static class CDHwithSHA224KDFAndSharedInfo extends KeyAgreementSpi { public CDHwithSHA224KDFAndSharedInfo() { super("ECCDHwithSHA224KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA224Digest()), ecc_cms_Generator); } } public static class DHwithSHA256KDFAndSharedInfo extends KeyAgreementSpi { public DHwithSHA256KDFAndSharedInfo() { super("ECDHwithSHA256KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA256Digest()), ecc_cms_Generator); } } public static class CDHwithSHA256KDFAndSharedInfo extends KeyAgreementSpi { public CDHwithSHA256KDFAndSharedInfo() { super("ECCDHwithSHA256KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA256Digest()), ecc_cms_Generator); } } public static class DHwithSHA384KDFAndSharedInfo extends KeyAgreementSpi { public DHwithSHA384KDFAndSharedInfo() { super("ECDHwithSHA384KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA384Digest()), ecc_cms_Generator); } } public static class CDHwithSHA384KDFAndSharedInfo extends KeyAgreementSpi { public CDHwithSHA384KDFAndSharedInfo() { super("ECCDHwithSHA384KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA384Digest()), ecc_cms_Generator); } } public static class DHwithSHA512KDFAndSharedInfo extends KeyAgreementSpi { public DHwithSHA512KDFAndSharedInfo() { super("ECDHwithSHA512KDF", new ECDHBasicAgreement(), new KDF2BytesGenerator(new SHA512Digest()), ecc_cms_Generator); } } public static class CDHwithSHA512KDFAndSharedInfo extends KeyAgreementSpi { public CDHwithSHA512KDFAndSharedInfo() { super("ECCDHwithSHA512KDF", new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA512Digest()), ecc_cms_Generator); } } public static class MQVwithSHA1KDFAndSharedInfo extends KeyAgreementSpi { public MQVwithSHA1KDFAndSharedInfo() { super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(new SHA1Digest()), old_ecc_cms_Generator); } } public static class MQVwithSHA224KDFAndSharedInfo extends KeyAgreementSpi { public MQVwithSHA224KDFAndSharedInfo() { super("ECMQVwithSHA224KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(new SHA224Digest()), ecc_cms_Generator); } } public static class MQVwithSHA256KDFAndSharedInfo extends KeyAgreementSpi { public MQVwithSHA256KDFAndSharedInfo() { super("ECMQVwithSHA256KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(new SHA256Digest()), ecc_cms_Generator); } } public static class MQVwithSHA384KDFAndSharedInfo extends KeyAgreementSpi { public MQVwithSHA384KDFAndSharedInfo() { super("ECMQVwithSHA384KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(new SHA384Digest()), ecc_cms_Generator); } } public static class MQVwithSHA512KDFAndSharedInfo extends KeyAgreementSpi { public MQVwithSHA512KDFAndSharedInfo() { super("ECMQVwithSHA512KDF", new ECMQVBasicAgreement(), new KDF2BytesGenerator(new SHA512Digest()), ecc_cms_Generator); } } public static class DHwithSHA1CKDF extends KeyAgreementSpi { public DHwithSHA1CKDF() { super("ECDHwithSHA1CKDF", new ECDHCBasicAgreement(), new ConcatenationKDFGenerator(new SHA1Digest())); } } public static class DHwithSHA256CKDF extends KeyAgreementSpi { public DHwithSHA256CKDF() { super("ECDHwithSHA256CKDF", new ECDHCBasicAgreement(), new ConcatenationKDFGenerator(new SHA256Digest())); } } public static class DHwithSHA384CKDF extends KeyAgreementSpi { public DHwithSHA384CKDF() { super("ECDHwithSHA384CKDF", new ECDHCBasicAgreement(), new ConcatenationKDFGenerator(new SHA384Digest())); } } public static class DHwithSHA512CKDF extends KeyAgreementSpi { public DHwithSHA512CKDF() { super("ECDHwithSHA512CKDF", new ECDHCBasicAgreement(), new ConcatenationKDFGenerator(new SHA512Digest())); } } public static class MQVwithSHA1CKDF extends KeyAgreementSpi { public MQVwithSHA1CKDF() { super("ECMQVwithSHA1CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(new SHA1Digest())); } } public static class MQVwithSHA224CKDF extends KeyAgreementSpi { public MQVwithSHA224CKDF() { super("ECMQVwithSHA224CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(new SHA224Digest())); } } public static class MQVwithSHA256CKDF extends KeyAgreementSpi { public MQVwithSHA256CKDF() { super("ECMQVwithSHA256CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(new SHA256Digest())); } } public static class MQVwithSHA384CKDF extends KeyAgreementSpi { public MQVwithSHA384CKDF() { super("ECMQVwithSHA384CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(new SHA384Digest())); } } public static class MQVwithSHA512CKDF extends KeyAgreementSpi { public MQVwithSHA512CKDF() { super("ECMQVwithSHA512CKDF", new ECMQVBasicAgreement(), new ConcatenationKDFGenerator(new SHA512Digest())); } } }