diff --git a/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java b/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java index 769121a..f1830b6 100644 --- a/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java +++ b/net-security/src/main/java/org/xbib/net/security/PrivateKeyReader.java @@ -108,8 +108,7 @@ public class PrivateKeyReader { return getECKeySpec(keyBytes); } 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[] sk = Arrays.copyOfRange(keyBytes, 0, 32); - return getEdECKeySpec(sk); + return getEdECKeySpec(keyBytes); } 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( "secp256r1 [NIST P-256, X9.62 prime256v1]", "1.2.840.10045.3.1.7", @@ -384,6 +378,12 @@ public class PrivateKeyReader { 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) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException { @@ -424,37 +424,49 @@ public class PrivateKeyReader { } private static ECPublicKey getECPublicKey(KeyFactory keyFactory, PrivateKey key) - throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException { + throws InvalidKeyException, InvalidKeySpecException { if (!(key instanceof ECPrivateKey ecPrivateKey)) { throw new InvalidKeyException("Private key is not EC private key"); } ECParameterSpec ecParameterSpec = ecPrivateKey.getParams(); - BigInteger x = ecParameterSpec.getGenerator().getAffineX(); - BigInteger y = ecParameterSpec.getGenerator().getAffineX(); - ECPoint ecPoint = new ECPoint(x, y); + ECPoint ecPoint = scalarPointMultiplication(ecParameterSpec.getCurve(), + ecParameterSpec.getOrder(), ecParameterSpec.getGenerator(), ecPrivateKey.getS()); ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParameterSpec); return ECPublicKey.class.cast(keyFactory.generatePublic(spec)); } private static EdECPublicKey getEdECPublicKey(KeyFactory keyFactory, PrivateKey key) - throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException { - if (!(key instanceof EdECPrivateKey)) { + throws InvalidKeyException, InvalidKeySpecException { + if (!(key instanceof EdECPrivateKey edECPrivateKey)) { throw new InvalidKeyException("Private key is not EdEC private key"); } - byte[] pk = key.getEncoded(); - boolean xisodd = false; - int lastbyteInt = pk[pk.length - 1]; - if ((lastbyteInt & 255) >> 7 == 1) { - xisodd = true; - } - pk[pk.length - 1] &= 127; - BigInteger y = new BigInteger(1, pk); - NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519"); - EdECPoint ep = new EdECPoint(xisodd, y); - EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(paramSpec, ep); + byte[] arr = edECPrivateKey.getEncoded(); + byte msb = arr[arr.length - 1]; + boolean xOdd = (msb & 0x80) != 0; + arr[arr.length - 1] &= (byte) 0x7F; + reverse(arr); + BigInteger y = new BigInteger(1, arr); + EdECPoint edECPoint = new EdECPoint(xOdd, y); + EdECPublicKeySpec publicKeySpec = new EdECPublicKeySpec(edECPrivateKey.getParams(), edECPoint); 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) { if (target.length == 0) { return 0; @@ -504,4 +516,80 @@ public class PrivateKeyReader { 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); + } diff --git a/net-security/src/test/java/org/xbib/net/security/test/Ed25519KeyTest.java b/net-security/src/test/java/org/xbib/net/security/test/Ed25519KeyTest.java index 40fda31..d763127 100644 --- a/net-security/src/test/java/org/xbib/net/security/test/Ed25519KeyTest.java +++ b/net-security/src/test/java/org/xbib/net/security/test/Ed25519KeyTest.java @@ -5,6 +5,7 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Signature; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; public class Ed25519KeyTest { @@ -16,5 +17,8 @@ public class Ed25519KeyTest { sig.initSign(kp.getPrivate()); sig.update("Hello Jörg".getBytes(StandardCharsets.UTF_8)); byte[] s = sig.sign(); + sig.initVerify(kp.getPublic()); + sig.update("Hello Jörg".getBytes(StandardCharsets.UTF_8)); + assertTrue(sig.verify(s)); } } diff --git a/net-security/src/test/java/org/xbib/net/security/test/PrivateKeyReaderTest.java b/net-security/src/test/java/org/xbib/net/security/test/PrivateKeyReaderTest.java index 0b56a07..0d51eff 100644 --- a/net-security/src/test/java/org/xbib/net/security/test/PrivateKeyReaderTest.java +++ b/net-security/src/test/java/org/xbib/net/security/test/PrivateKeyReaderTest.java @@ -79,7 +79,7 @@ public class PrivateKeyReaderTest { PublicKey publicKey = keyPair.getPublic(); assertNotNull(publicKey); assertEquals("EdDSA", publicKey.getAlgorithm()); - match("Ed25519", privateKey, publicKey); + match("EdDSA", privateKey, publicKey); } } @@ -137,7 +137,6 @@ public class PrivateKeyReaderTest { signature.initSign(privateKey); signature.update(message.getBytes(StandardCharsets.UTF_8)); byte[] payload = signature.sign(); - signature.initVerify(publicKey); signature.update(message.getBytes(StandardCharsets.UTF_8)); assertTrue(signature.verify(payload));