Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DH->Curve25519->ServerKeyExchange->Bouncy Castle is not working #251

Closed
plmn22 opened this issue Nov 10, 2017 · 6 comments
Closed

DH->Curve25519->ServerKeyExchange->Bouncy Castle is not working #251

plmn22 opened this issue Nov 10, 2017 · 6 comments
Assignees

Comments

@plmn22
Copy link

plmn22 commented Nov 10, 2017

I need to create a shared secret for the DH (Diffie–Hellman Key Exchange), using my private key and a public key that I receive from Apache Server. The code is written in Java + Bouncy Castle 1.57.

I have attached a screen shot from OpenSSL:

I have used openSSL in order to connect to a server, that implement, Curve25519. I have taken the public key, that have returned in the response and use it, as byte array, in the following code:

// **** START OF BOUNCY CASTLE CODE

byte[] publicKey = new byte[]{(byte)0xF1, (byte)0x6D, (byte)0x48, (byte)0x25, (byte)0x0C, (byte)0xE2, (byte)0xA2, (byte)0xA4, (byte)0xFD, (byte)0x4D, (byte)0x9B, (byte)0x08, (byte)0x57, (byte)0x7B, (byte)0x2D, (byte)0x3F, (byte)0x92, (byte)0xC6, (byte)0x4D, (byte)0x09, (byte)0x3C, (byte)0xD9, (byte)0x68, (byte)0xE6, (byte)0xC7, (byte)0x32, (byte)0x5E, (byte)0x40, (byte)0x30, (byte)0xB7, (byte)0xF2, (byte)0x06 };

ECParameterSpec ecP = ECNamedCurveTable.getParameterSpec(this.namedCarved);

ECPublicKeySpec pubKey = new
ECPublicKeySpec(ecP.getCurve().decodePoint(publicKey), ecP);

KeyFactory kf = KeyFactory.getInstance("ECDH", "BC");
return kf.generatePublic(pubKey);

// **** END OF BOUNCY CASTLE CODE

The problem it that the function ecP.getCurve().decodePoint(publicKey) throws an exception:
"java.lang.IllegalArgumentException: Invalid point encoding 0xF1"

@peterdettman
Copy link
Collaborator

peterdettman commented Nov 11, 2017

This is a point format and/or curve form mismatch. Skip to the last paragraph for the easy solution, or read on if you need to make things work via the provider code above.

X25519 uses the Montgomery curve "Curve25519", and specifies the public key format as the (exactly) 32-byte X coordinate (little-endian).

On the other hand, when you get an implementation of "Curve25519" (or any curve) from ECNamedCurveTable, it will be for a short-Weierstrass (SW) curve, and the expected public key format is from the SEC standards, so that it includes a format byte at the start, followed by the 32-byte X coordinate, and possibly the Y coordinate, both in big-endian order.

This can be made to work by converting the input as follows:

  • parse the input Montgomery X coordinate ("publicKey") as a BigInteger (byte-reversed)
  • convert the Montgomery X coordinate to a Weierstrass X coordinate via the point map:
    Xw = Xm + D mod P, where P == 2^255 - 19, D == 486662 / 3 mod P
  • build a SEC compressed point encoding for the Weierstrass X coordinate
  • ...which can then be passed to decodePoint

Sample code:

    static BigInteger P = BigInteger.ONE.shiftLeft(255).subtract(BigInteger.valueOf(19));
    static BigInteger D = BigInteger.valueOf(3).modInverse(P).multiply(BigInteger.valueOf(486662)).mod(P);

    byte[] convertInput(byte[] montPubKey)
    {
        BigInteger Xm = new BigInteger(1, Arrays.reverse(montPubKey));
        BigInteger Xw = Xm.add(D).mod(P);

        byte[] weierPubKey = BigIntegers.asUnsignedByteArray(33, Xw);
        weierPubKey[0] = 0x02;

        return weierPubKey;
    }

If you want to also send a public key in X25519 format, you'll need to do a similar conversion (Xm = Xw - D mod P) from the point encoding you get from the Weierstrass curve.

However I should point out that we have just committed a proper implementation of X25519 (1f559bb). We have more work to do on trying to present that in the provider and through the usual interfaces, but if you just want to do ECDH with X25519, you could use that class directly (copy it for now, or wait for the next release - or beta).

@peterdettman peterdettman self-assigned this Nov 11, 2017
@plmn22
Copy link
Author

plmn22 commented Nov 19, 2017

Thanks for your answer. It was very accurate and also the explanation was good.

But, I have another question:

Now i need to generate X25519 public key. I'm generating that public key, using BC library. When i check the public key that i get from BC, then i can see that it is 64 bytes. According to your explanation I think that BC output it in short-Weierstrass format, while i need it in Montgomery curve format.

How can i convert it to 32 bytes?

This is my java code:

X9ECParameters ecP = CustomNamedCurves.getByName("Curve25519");
this.ECCPointCompressed = true;

ECParameterSpec ecSpec=new ECParameterSpec(ecP.getCurve(), ecP.getG(),ecP.getN(), ecP.getH(), ecP.getSeed());

KeyPairGenerator kpgen;
kpgen = KeyPairGenerator.getInstance("ECDH", "BC");
kpgen.initialize(ecSpec, new SecureRandom());
pairA = kpgen.generateKeyPair();
ECPublicKey eckey = (ECPublicKey)pairA.getPublic();
var public_key = eckey.getQ().getEncoded(true);

The "public_key" returned here is 64 bytes!

Please Advice!
Thanks,
Asi Fefer.

@peterdettman
Copy link
Collaborator

If you need to do the full X25519 ECDH, then I am going to strongly recommend that you use the classes added here: 1f559bb . It should be clear how to use it by referring to the X25519Test.testECDH method.

The way that private keys are generated and/or used in X25519 have some subtle differences compared to JCE providers' behaviour for generic SW curves. Also, X25519 permits public keys on the twist of the curve, which will instead cause exceptions in SW implementations. So it's not clear to me that it can be made to work in the general case, without stepping outside of JCE and doing parts of the operations yourself.

In which case, you may as well use the new code, which is a direct implementation of X25519, so requires no complicated adapters, and performs quite a bit faster besides. If you need any advice on using these classes would you please post further questions to the dev-crypto mailing list (http://bouncycastle.org/mailing_lists.html).

@hamirshekhawat
Copy link

hamirshekhawat commented Feb 23, 2021

@peterdettman I found this issue while trying to figure out how to create compatible keys between pointycastle and bouncycastle.
Following is the java code

        kpg = KeyPairGenerator.getInstance("EC", "BC");
        X9ECParameters ecP = CustomNamedCurves.getByName("Curve25519");
        ECParameterSpec ecSpec = EC5Util.convertToSpec(ecP);
        kpg.initialize(ecSpec);

        final KeyPair kp = kpg.genKeyPair();

Is it possible to do the same in PointyCastle? If not can you suggest a way to do this using OpenSSL or Libsodium?

@peterdettman
Copy link
Collaborator

@hamirshekhawat I assume you should be using KeyPairGenerator.getInstance("X25519", "BC"). I don't think PointyCastle has support for X25519 though.

@hamirshekhawat
Copy link

Thanks @peterdettman for confirmation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants