fix EC public key derive
This commit is contained in:
parent
25fb5a0bb9
commit
dea43eee45
3 changed files with 117 additions and 26 deletions
|
@ -108,8 +108,7 @@ public class PrivateKeyReader {
|
||||||
return getECKeySpec(keyBytes);
|
return getECKeySpec(keyBytes);
|
||||||
} else if (indexOf(key, BEGIN_OPENSSH_PRIVATE_KEY,0, key.length) >= 0) {
|
} else if (indexOf(key, BEGIN_OPENSSH_PRIVATE_KEY,0, key.length) >= 0) {
|
||||||
byte[] keyBytes = extract(key, BEGIN_OPENSSH_PRIVATE_KEY, END_OPENSSH_PRIVATE_KEY);
|
byte[] keyBytes = extract(key, BEGIN_OPENSSH_PRIVATE_KEY, END_OPENSSH_PRIVATE_KEY);
|
||||||
byte[] sk = Arrays.copyOfRange(keyBytes, 0, 32);
|
return getEdECKeySpec(keyBytes);
|
||||||
return getEdECKeySpec(sk);
|
|
||||||
}
|
}
|
||||||
throw new IOException("invalid PEM input stream");
|
throw new IOException("invalid PEM input stream");
|
||||||
}
|
}
|
||||||
|
@ -312,11 +311,6 @@ public class PrivateKeyReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private EdECPrivateKeySpec getEdECKeySpec(byte[] keySpec) {
|
|
||||||
NamedParameterSpec params = NamedParameterSpec.ED25519;
|
|
||||||
return new EdECPrivateKeySpec(params, keySpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Curve SECP256R1 = initializeCurve(
|
private static final Curve SECP256R1 = initializeCurve(
|
||||||
"secp256r1 [NIST P-256, X9.62 prime256v1]",
|
"secp256r1 [NIST P-256, X9.62 prime256v1]",
|
||||||
"1.2.840.10045.3.1.7",
|
"1.2.840.10045.3.1.7",
|
||||||
|
@ -384,6 +378,12 @@ public class PrivateKeyReader {
|
||||||
return new BigInteger(s, 16);
|
return new BigInteger(s, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EdECPrivateKeySpec getEdECKeySpec(byte[] keySpec) {
|
||||||
|
NamedParameterSpec params = NamedParameterSpec.ED25519;
|
||||||
|
byte[] sk = Arrays.copyOfRange(keySpec, 0, 32);
|
||||||
|
return new EdECPrivateKeySpec(params, sk);
|
||||||
|
}
|
||||||
|
|
||||||
private static PKCS8EncodedKeySpec getKeySpec(byte[] key, char[] password)
|
private static PKCS8EncodedKeySpec getKeySpec(byte[] key, char[] password)
|
||||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
|
@ -424,37 +424,49 @@ public class PrivateKeyReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ECPublicKey getECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
private static ECPublicKey getECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||||
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
|
throws InvalidKeyException, InvalidKeySpecException {
|
||||||
if (!(key instanceof ECPrivateKey ecPrivateKey)) {
|
if (!(key instanceof ECPrivateKey ecPrivateKey)) {
|
||||||
throw new InvalidKeyException("Private key is not EC private key");
|
throw new InvalidKeyException("Private key is not EC private key");
|
||||||
}
|
}
|
||||||
ECParameterSpec ecParameterSpec = ecPrivateKey.getParams();
|
ECParameterSpec ecParameterSpec = ecPrivateKey.getParams();
|
||||||
BigInteger x = ecParameterSpec.getGenerator().getAffineX();
|
ECPoint ecPoint = scalarPointMultiplication(ecParameterSpec.getCurve(),
|
||||||
BigInteger y = ecParameterSpec.getGenerator().getAffineX();
|
ecParameterSpec.getOrder(), ecParameterSpec.getGenerator(), ecPrivateKey.getS());
|
||||||
ECPoint ecPoint = new ECPoint(x, y);
|
|
||||||
ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
|
ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
|
||||||
return ECPublicKey.class.cast(keyFactory.generatePublic(spec));
|
return ECPublicKey.class.cast(keyFactory.generatePublic(spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EdECPublicKey getEdECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
private static EdECPublicKey getEdECPublicKey(KeyFactory keyFactory, PrivateKey key)
|
||||||
throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
|
throws InvalidKeyException, InvalidKeySpecException {
|
||||||
if (!(key instanceof EdECPrivateKey)) {
|
if (!(key instanceof EdECPrivateKey edECPrivateKey)) {
|
||||||
throw new InvalidKeyException("Private key is not EdEC private key");
|
throw new InvalidKeyException("Private key is not EdEC private key");
|
||||||
}
|
}
|
||||||
byte[] pk = key.getEncoded();
|
byte[] arr = edECPrivateKey.getEncoded();
|
||||||
boolean xisodd = false;
|
byte msb = arr[arr.length - 1];
|
||||||
int lastbyteInt = pk[pk.length - 1];
|
boolean xOdd = (msb & 0x80) != 0;
|
||||||
if ((lastbyteInt & 255) >> 7 == 1) {
|
arr[arr.length - 1] &= (byte) 0x7F;
|
||||||
xisodd = true;
|
reverse(arr);
|
||||||
}
|
BigInteger y = new BigInteger(1, arr);
|
||||||
pk[pk.length - 1] &= 127;
|
EdECPoint edECPoint = new EdECPoint(xOdd, y);
|
||||||
BigInteger y = new BigInteger(1, pk);
|
EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(edECPrivateKey.getParams(), edECPoint);
|
||||||
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
|
|
||||||
EdECPoint ep = new EdECPoint(xisodd, y);
|
|
||||||
EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(paramSpec, ep);
|
|
||||||
return EdECPublicKey.class.cast(keyFactory.generatePublic(publicKeySpec));
|
return EdECPublicKey.class.cast(keyFactory.generatePublic(publicKeySpec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void reverse(byte [] arr) {
|
||||||
|
int i = 0;
|
||||||
|
int j = arr.length - 1;
|
||||||
|
while (i < j) {
|
||||||
|
swap(arr, i, j);
|
||||||
|
i++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void swap(byte[] arr, int i, int j) {
|
||||||
|
byte tmp = arr[i];
|
||||||
|
arr[i] = arr[j];
|
||||||
|
arr[j] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
private static int indexOf(byte[] array, byte[] target, int start, int end) {
|
private static int indexOf(byte[] array, byte[] target, int start, int end) {
|
||||||
if (target.length == 0) {
|
if (target.length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -504,4 +516,80 @@ public class PrivateKeyReader {
|
||||||
return Base64.getMimeDecoder().decode(m.group(1));
|
return Base64.getMimeDecoder().decode(m.group(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ECPoint scalarPointMultiplication(EllipticCurve curve,
|
||||||
|
BigInteger order,
|
||||||
|
ECPoint ecPointP,
|
||||||
|
BigInteger scalar) {
|
||||||
|
return scalarPointMultiplication(curve, ecPointP, scalar.mod(order));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECPoint scalarPointMultiplication(EllipticCurve curve,
|
||||||
|
ECPoint ecPointP,
|
||||||
|
BigInteger scalar) {
|
||||||
|
if (ecPointP.equals(ECPoint.POINT_INFINITY)) {
|
||||||
|
return ecPointP;
|
||||||
|
}
|
||||||
|
ECPoint ecPointR = ECPoint.POINT_INFINITY;
|
||||||
|
for (int i = (scalar.bitLength()) - 1; i >= 0; i--) {
|
||||||
|
ecPointR = doublePoint(curve, ecPointR);
|
||||||
|
if (scalar.testBit(i)) {
|
||||||
|
ecPointR = addPoint(curve, ecPointR, ecPointP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ecPointR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECPoint doublePoint(EllipticCurve curve, ECPoint ecPointP) {
|
||||||
|
if (ecPointP.equals(ECPoint.POINT_INFINITY)) {
|
||||||
|
return ecPointP;
|
||||||
|
}
|
||||||
|
BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||||
|
BigInteger a = curve.getA();
|
||||||
|
BigInteger xp = ecPointP.getAffineX();
|
||||||
|
BigInteger yp = ecPointP.getAffineY();
|
||||||
|
BigInteger lambda = ((((xp.pow(2)).multiply(THREE)).add(a)).multiply((yp.multiply(TWO)).modInverse(p))).mod(p);
|
||||||
|
BigInteger xr = computeXr(p, lambda, xp);
|
||||||
|
BigInteger yr = computeYr(p, lambda, xp, yp, xr);
|
||||||
|
return new ECPoint(xr, yr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECPoint addPoint(EllipticCurve curve, ECPoint ecPointQ, ECPoint ecPointP) {
|
||||||
|
if (ecPointQ.equals(ecPointP)) {
|
||||||
|
return doublePoint(curve, ecPointQ);
|
||||||
|
}
|
||||||
|
if (ecPointQ.equals(ECPoint.POINT_INFINITY)) {
|
||||||
|
return ecPointP;
|
||||||
|
}
|
||||||
|
if (ecPointP.equals(ECPoint.POINT_INFINITY)) {
|
||||||
|
return ecPointQ;
|
||||||
|
}
|
||||||
|
BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||||
|
BigInteger xq = ecPointQ.getAffineX();
|
||||||
|
BigInteger yq = ecPointQ.getAffineY();
|
||||||
|
BigInteger xp = ecPointP.getAffineX();
|
||||||
|
BigInteger yp = ecPointP.getAffineY();
|
||||||
|
BigInteger lambda = ((yq.subtract(yp)).multiply(xq.subtract(xp).modInverse(p))).mod(p);
|
||||||
|
BigInteger xr = computeXr(p, lambda, xp, xq);
|
||||||
|
BigInteger yr = computeYr(p, lambda, xp, yp, xr);
|
||||||
|
return new ECPoint(xr, yr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigInteger computeXr(BigInteger p, BigInteger lambda, BigInteger xp) {
|
||||||
|
return computeXr(p, lambda, xp, xp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigInteger computeXr(BigInteger p, BigInteger lambda, BigInteger xp, BigInteger xq) {
|
||||||
|
return (((lambda.modPow(TWO, p).subtract(xq))).subtract(xp)).mod(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigInteger computeYr(BigInteger p, BigInteger lambda, BigInteger xp, BigInteger yp, BigInteger xr) {
|
||||||
|
return ((lambda.multiply(xp.subtract(xr)).mod(p)).subtract(yp)).mod(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final BigInteger ZERO = BigInteger.ZERO;
|
||||||
|
private static final BigInteger ONE = BigInteger.ONE;
|
||||||
|
private static final BigInteger TWO = ONE.add(ONE);
|
||||||
|
private static final BigInteger THREE = TWO.add(ONE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class Ed25519KeyTest {
|
public class Ed25519KeyTest {
|
||||||
|
|
||||||
|
@ -16,5 +17,8 @@ public class Ed25519KeyTest {
|
||||||
sig.initSign(kp.getPrivate());
|
sig.initSign(kp.getPrivate());
|
||||||
sig.update("Hello Jörg".getBytes(StandardCharsets.UTF_8));
|
sig.update("Hello Jörg".getBytes(StandardCharsets.UTF_8));
|
||||||
byte[] s = sig.sign();
|
byte[] s = sig.sign();
|
||||||
|
sig.initVerify(kp.getPublic());
|
||||||
|
sig.update("Hello Jörg".getBytes(StandardCharsets.UTF_8));
|
||||||
|
assertTrue(sig.verify(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class PrivateKeyReaderTest {
|
||||||
PublicKey publicKey = keyPair.getPublic();
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
assertNotNull(publicKey);
|
assertNotNull(publicKey);
|
||||||
assertEquals("EdDSA", publicKey.getAlgorithm());
|
assertEquals("EdDSA", publicKey.getAlgorithm());
|
||||||
match("Ed25519", privateKey, publicKey);
|
match("EdDSA", privateKey, publicKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,6 @@ public class PrivateKeyReaderTest {
|
||||||
signature.initSign(privateKey);
|
signature.initSign(privateKey);
|
||||||
signature.update(message.getBytes(StandardCharsets.UTF_8));
|
signature.update(message.getBytes(StandardCharsets.UTF_8));
|
||||||
byte[] payload = signature.sign();
|
byte[] payload = signature.sign();
|
||||||
|
|
||||||
signature.initVerify(publicKey);
|
signature.initVerify(publicKey);
|
||||||
signature.update(message.getBytes(StandardCharsets.UTF_8));
|
signature.update(message.getBytes(StandardCharsets.UTF_8));
|
||||||
assertTrue(signature.verify(payload));
|
assertTrue(signature.verify(payload));
|
||||||
|
|
Loading…
Reference in a new issue