I am trying to generate shared secret using EC named curve and finding mismatch in client vs server shared secret.
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Client
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(ecCurveName);
kpg.initialize(ecGenParameterSpec, new SecureRandom());
ECPublicKey ephemeralPublicKey = (ECPublicKey) kpg.generateKeyPair().getPublic();
ECPrivateKey clientEphemeralPrivateKey =(ECPrivateKey) kpg.generateKeyPair().getPrivate();
BigInteger pointx = ephemeralPublicKey.getW().getAffineX();
BigInteger pointy = ephemeralPublicKey.getW().getAffineY();
String eCClientEphemeralPublicKeyString = ("04"+pointx.toString(16)+pointy.toString(16)).toUpperCase();
byte[] remoteECCPkBytes = DatatypeConverter.parseBase64Binary(remoteECCPkBase64);
KeyFactory keyFactory= KeyFactory.getInstance("EC","BC");
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(remoteECCPkBytes);
PublicKey serverECCPublicKey = keyFactory.generatePublic(pkSpec);
KeyAgreement ka = KeyAgreement.getInstance("ECDH","BC");
ka.init(clientEphemeralPrivateKey);
ka.doPhase(serverECCPublicKey, true);
SecretKey agreedKey = ka.generateSecret("AES[256]");
byte[] sharedSecret = agreedKey.getEncoded();
// Server
String clientEphemeralPKBase64 = java.util.Base64.getEncoder().encodeToString(new BigInteger(eCClientEphemeralPublicKeyString, 16).toByteArray());
byte[] clientECPublicKeybytes = DatatypeConverter.parseBase64Binary(clientEphemeralPKBase64);
ECParameterSpec ecParameterSpec = ECNamedCurveTable.getParameterSpec(ecCurveName);
ECCurve curve = ecParameterSpec.getCurve();
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(curve.decodePoint(clientECPublicKeybytes), ecParameterSpec);
KeyFactory kf = KeyFactory.getInstance("EC","BC");
ECPublicKey ecClientPublicKey = (ECPublicKey)kf.generatePublic(pubKeySpec);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec( Base64.decodeBase64(serverprivateKeyBase64));
PrivateKey ecServerPrivateKey = kf.generatePrivate(privateKeySpec);
KeyAgreement ka = KeyAgreement.getInstance("ECDH","BC");
ka.init(ecServerPrivateKey);
ka.doPhase(ecClientPublicKey, true);
SecretKey agreedKey = ka.generateSecret("AES[256]");
byte[] sharedSecret = agreedKey.getEncoded();
This is of course deadly:
ECPublicKey ephemeralPublicKey = (ECPublicKey) kpg.generateKeyPair().getPublic();
ECPrivateKey clientEphemeralPrivateKey =(ECPrivateKey) kpg.generateKeyPair().getPrivate();
If you call generateKeyPair
twice your public and private key will not be part of the same key pair.
You need to create two key pairs, one for the server, one for the client and then communicate the public keys. Creating a public key and immediately tossing away the private key cannot be useful, other than to retrieve the domain parameters in a roundabout way.
Instead you should do:
KeyPair clientEphemeralKeyPair = kpg.generateKeyPair();
ECPublicKey clientEphemeralPublicKey = (ECPublicKey) clientEphemeralKeyPair.getPublic();
ECPrivateKey clientEphemeralPrivateKey = (ECPrivateKey) clientEphemeralKeyPair.getPrivate();