1

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();  
vison
  • 59
  • 1
  • 9

1 Answers1

3

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();  
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • You forgot to @MaartenBodewes me, so I never saw this question. If you had added [tag:cryptography] then I would have seen it as well. – Maarten Bodewes Mar 04 '18 at 22:09
  • Thank you. I am checking on this but it would be great if you could suggest changes. Difficult part of me is .. getting public key from private key with current code structure.. I am not able to get to the point where I can get private key(pkcs8 format) and public key (X509 format) from java so I have open ssl and java keys in same format and I can use same function to generate shared secret. my objective is to use server static public key generated by Open SSL on client side and on server side, use dynamic/ ephemeral public key generated by client from java code – vison Mar 05 '18 at 16:58
  • You should only have to exchange the public keys. Java cannot handle PEM out of the box, you need to convert to DER, which OpenSSL should be able to do (usually just `-outform DER`). – Maarten Bodewes Mar 05 '18 at 17:14
  • Thank you for the help.. this worked to get matching shared secret.. One last question.. I see sometime below code generates X and Y less than 32 bytes () each and if i just call function again it generates 32 bytes X and Y each. Please see example below.. X is of 31 byes and one nibble. Any idea why? ECPublicKey clientEphemeralPublicKey = (ECPublicKey) clientEphemeralKeyPair.getPublic(); EC Ephemeral Public Key X: 834f33bf7e3b8bc8bf5cbb5d375f64ebae4e0d446a2380eb637ff4e0d2cbe11 Y: 2b037a6ec9cdaa0ffcfc90289f4884de3f5a03e11378e7bc1b1e145ee46c4592 – vison Mar 06 '18 at 16:36
  • X and Y only need to be smaller than the order of the curve, they don't need to be statically sized. Same for many other components of keys, e.g. RSA private exponents. In the end X and Y are integers! – Maarten Bodewes Mar 06 '18 at 17:24
  • Sure but could X and Y be odd sized or must be full bytes? – vison Mar 06 '18 at 17:26
  • Odd sized is fine if you're printing the hex representation of an integer, even if it is slightly annoying if you're used to full bytes instead of bytes and a single nibble. Convert to bytes using a function called I2OSP and then print them out using two hex digits at a time to get an even number of hex digits. – Maarten Bodewes Mar 06 '18 at 21:50
  • Here's I2OSP...https://stackoverflow.com/a/18824707/589259, feel free to accept or vote up anything. – Maarten Bodewes Mar 06 '18 at 21:58