Closed andrewparks closed 7 years ago
@andrewparks sorry, but can you give full example of Java source which can be pasted to compilejava.net (I not familiar with Java unfortunately)
Thanks, unfortunately there is a dependency on a jar file, so it won't work at compilejava.net. If you have the JDK installed, you can run it by doing the following:
First, place this full source code in a file called ECDHKeygen.java
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
public class ECDHKeygen {
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b: data) sb.append(String.format("%02x", b&0xff));
return sb.toString();
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
ECParameterSpec ecsp = ECNamedCurveTable.getParameterSpec("curve25519");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", "BC");
kpg.initialize(ecsp, new SecureRandom());
KeyPair processorKeyPair = kpg.genKeyPair();
System.out.println("private key: " + ((ECPrivateKey) processorKeyPair.getPrivate()).getS().toString(16));
System.out.println("public key: " + toHex(((ECPublicKey) processorKeyPair.getPublic()).getQ().getEncoded(true)));
}
}
Then, to execute it run:
wget https://www.bouncycastle.org/download/bcprov-jdk15on-156.jar
javac ECDHKeygen.java -extdirs .
java -cp "bcprov-jdk15on-156.jar:." ECDHKeygen
By the way, as a sanity check, here is Java code that derives the public key from the private key, to check that the public key really is G multiplied by the private key:
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.ECPrivateKey;
public class ECDHPrivateToPublicKey {
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b: data) sb.append(String.format("%02x", b&0xff));
return sb.toString();
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
ECParameterSpec ecsp = ECNamedCurveTable.getParameterSpec("curve25519");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", "BC");
kpg.initialize(ecsp, new SecureRandom());
KeyPair processorKeyPair = kpg.genKeyPair();
System.out.println("private key: " + ((ECPrivateKey) processorKeyPair.getPrivate()).getS().toString(16));
System.out.println("public key: " + toHex(((ECPublicKey) processorKeyPair.getPublic()).getQ().getEncoded(true)));
ECPoint publicKeyPoint = ecsp.getG().multiply(new BigInteger(((ECPrivateKey) processorKeyPair.getPrivate()).getS().toByteArray()));
System.out.println("recreated public key: " + toHex(publicKeyPoint.getEncoded(true)));
}
}
To run this, place the source in a file called ECDHPrivateToPublicKey.java and then do:
wget https://www.bouncycastle.org/download/bcprov-jdk15on-156.jar
javac ECDHPrivateToPublicKey.java -extdirs .
java -cp "bcprov-jdk15on-156.jar:." ECDHPrivateToPublicKey
Example output is:
private key: a8a677e2ebc857cf36e8634ca1731977737bc993b123e2ee68a6fb82e276bda
public key: 034b8acc13b97df5aec3d7a84959f1c4d6c6d19c3029cf6c62d28122c24eb5d537
recreated public key: 034b8acc13b97df5aec3d7a84959f1c4d6c6d19c3029cf6c62d28122c24eb5d537
I also tried printing out G in Java, and I get this:
(2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad245a,20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9,1,2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa984914a144)
In contrast, in Javascript, when I run the code:
console.log(ec.g.x.toString(16));
console.log(ec.g.z.toString(16));
I get the result:
9
1
This is the source code for the Java Curve25519 implementation:
/*
* NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
* involves substitution of variables, so the base-point x coordinate is 9 + (486662 / 3).
*
* The Curve25519 paper doesn't say which of the two possible y values the base
* point has. The choice here is guided by language in the Ed25519 paper.
*
* (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
*/
X9ECPoint G = new X9ECPoint(curve, Hex.decode("04"
+ "2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A"
+ "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"));
This may also be of interest:
this.a = fromBigInteger(new BigInteger(1,
Hex.decode("2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144")));
this.b = fromBigInteger(new BigInteger(1,
Hex.decode("7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864")));
this.order = new BigInteger(1, Hex.decode("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"));
this.cofactor = BigInteger.valueOf(8);
If it's helpful, this code can be used to test changing the curve parameters:
(Put in a file called ECDHKeygenAlternativeCurve.java)
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.ECPrivateKey;
public class ECDHKeygenAlternativeCurve {
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b: data) sb.append(String.format("%02x", b&0xff));
return sb.toString();
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
ECCurve curve = new ECCurve.Fp(
new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
ECParameterSpec ecsp = new ECParameterSpec(
curve,
curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
g.initialize(ecsp, new SecureRandom());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", "BC");
kpg.initialize(ecsp, new SecureRandom());
KeyPair processorKeyPair = kpg.genKeyPair();
System.out.println("private key: " + ((ECPrivateKey) processorKeyPair.getPrivate()).getS().toString(16));
System.out.println("public key: " + toHex(((ECPublicKey) processorKeyPair.getPublic()).getQ().getEncoded(true)));
}
}
Problem solved. I just saw this pull request which fixes everything: https://github.com/indutny/elliptic/pull/113/commits (i've tested it and it works)
Doh, problem not actually solved. The shared secrets between Java and Javascript implementations differ. Can you think of why this might be?
@andrewparks sad to hear this, may I ask you to post private/public keys and expected/actual shared secret?
I've rewritten the code and performed some more tests, and I've now confirmed the problem was actually fully solved by the pull request mentioned above. Everything is working perfectly with the shared secret, thanks for this fantastic project.
Glad it worked out in the end!
To reproduce:
In Java, generate Curve25519 ECDH keypair:
The output produced is:
Then, run the following code in Javascript:
The output produced is:
Note that the public key generated in Java is not the same as the public key generated from Javascript