From c9e083f171fd50ce055081f4c2b12377ac3e930d Mon Sep 17 00:00:00 2001 From: CGantert345 <57003061+CGantert345@users.noreply.github.com> Date: Mon, 23 May 2022 17:05:41 +0200 Subject: encoding of keys different from P-256 --- .../java/org/uic/barcode/utils/ECKeyEncoder.java | 187 ++++++++++------- .../org/uic/barcode/test/ECKeyEncoderTest521.java | 232 +++++++++++++++++++++ 2 files changed, 344 insertions(+), 75 deletions(-) create mode 100644 src/test/java/org/uic/barcode/test/ECKeyEncoderTest521.java diff --git a/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java b/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java index 96038d3..705b178 100644 --- a/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java +++ b/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java @@ -6,6 +6,7 @@ import java.security.KeyFactory; import java.security.Provider; import java.security.PublicKey; import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; @@ -15,17 +16,40 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; +// TODO: Auto-generated Javadoc +/** + * The Class ECKeyEncoder. + */ public class ECKeyEncoder { + /** The Constant X962_UNCOMPRESSED_POINT_INDICATOR. */ private static final byte X962_UNCOMPRESSED_POINT_INDICATOR = 0x04; + + /** The Constant X962_ODD. */ private static final byte X962_ODD = 0x02; + + /** The Constant X962_EVEN. */ private static final byte X962_EVEN = 0x03; + /** The Constant ENCODING_X509. */ public static final String ENCODING_X509 = "X509"; + + /** The Constant ENCODING_X962_UNCOMPESSED. */ public static final String ENCODING_X962_UNCOMPESSED = "X962_UNCOMPRESSED"; + + /** The Constant ENCODING_X962_COMPRESSED. */ public static final String ENCODING_X962_COMPRESSED = "X962_COMPRESSED"; + + /** + * From encoded. + * + * @param keyBytes the encoded key + * @param oid the algorithm OID of the key algorithm + * @param provider the provider of the security implementation + * @return the public key + */ public static PublicKey fromEncoded (byte[] keyBytes, String oid, Provider provider) { PublicKey key = null; @@ -64,12 +88,7 @@ public class ECKeyEncoder { if (key != null) return key; - if (keyBytes[0] == X962_UNCOMPRESSED_POINT_INDICATOR) { - - - - } - + //maybe a compressed X9.62 eliptic key if (keyBytes[0] == X962_ODD || keyBytes[0] == X962_EVEN) { @@ -82,11 +101,10 @@ public class ECKeyEncoder { //get the curve parameters AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider); parameters.init(new ECGenParameterSpec(curveName)); - ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class); - EllipticCurve curve = ecParameters.getCurve(); + ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class); //reconstruct the uncompressed version with the curve - byte[] uncompressed = decompressPubkey(keyBytes, curve); + byte[] uncompressed = decompressPubkey(keyBytes, ecParameters); //decode the uncompressed key return fromUncompressedPoint(uncompressed, ecParameters); @@ -125,7 +143,14 @@ public class ECKeyEncoder { - public static byte[] getEncoded(PublicKey key, String encoding){ + /** + * Gets the encoded. + * + * @param key the public key + * @param encoding the encoding ("X509","X962_UNCOMPRESSED","X962_COMPRESSED") + * @return the encoded key + */ + public static byte[] getEncoded(PublicKey key, String encoding){ if (encoding.equals(ENCODING_X509)) { return key.getEncoded(); @@ -142,12 +167,17 @@ public class ECKeyEncoder { if (key instanceof ECPublicKey) { ECPoint point = ((ECPublicKey) key).getW(); + + int fieldSize = (((ECPublicKey)key).getParams().getCurve().getField().getFieldSize() + 7) / 8; + int keySizeBytes = fieldSize + 1; + final byte[] compressed = new byte[keySizeBytes]; + + byte[] x = toUnsignedBytes(point.getAffineX()); BigInteger y = point.getAffineY(); - byte[] compressed = new byte[x.length + 1]; //compression indicator if (y.testBit(0)) { @@ -155,7 +185,7 @@ public class ECKeyEncoder { } else { compressed[0] = 0x02; } - System.arraycopy(x, 0, compressed, 1, x.length); + System.arraycopy(x, 0, compressed, 1 + fieldSize - x.length, x.length); return compressed; } @@ -167,110 +197,117 @@ public class ECKeyEncoder { } - private static ECPublicKey fromUncompressedPoint( - final byte[] uncompressedPoint, final ECParameterSpec params) + /** + * From uncompressed point. + * + * @param encoded the public key in uncompressed encoding + * @param params the elliptic curve parameters + * @return the EC public key + * @throws Exception the exception + */ + private static ECPublicKey fromUncompressedPoint( + final byte[] encoded, final ECParameterSpec params) throws Exception { int offset = 0; - if (uncompressedPoint[offset++] != X962_UNCOMPRESSED_POINT_INDICATOR) { - throw new IllegalArgumentException( - "Invalid uncompressedPoint encoding, no uncompressed point indicator"); + if (encoded[offset++] != X962_UNCOMPRESSED_POINT_INDICATOR) { + throw new IllegalArgumentException("Invalid uncompressedPoint encoding, no uncompressed point indicator"); } int keySizeBytes = (params.getOrder().bitLength() + Byte.SIZE - 1) / Byte.SIZE; - if (uncompressedPoint.length != 1 + 2 * keySizeBytes) { - throw new IllegalArgumentException( - "Invalid uncompressedPoint encoding, not the correct size"); + if (encoded.length != 1 + 2 * keySizeBytes) { + throw new IllegalArgumentException("Invalid uncompressedPoint encoding, not the correct size"); } - final BigInteger x = new BigInteger(1, Arrays.copyOfRange( - uncompressedPoint, offset, offset + keySizeBytes)); + final BigInteger x = new BigInteger(1, Arrays.copyOfRange(encoded, offset, offset + keySizeBytes)); offset += keySizeBytes; - final BigInteger y = new BigInteger(1, Arrays.copyOfRange( - uncompressedPoint, offset, offset + keySizeBytes)); + final BigInteger y = new BigInteger(1, Arrays.copyOfRange(encoded, offset, offset + keySizeBytes)); final ECPoint w = new ECPoint(x, y); final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(w, params); final KeyFactory keyFactory = KeyFactory.getInstance("EC"); return (ECPublicKey) keyFactory.generatePublic(ecPublicKeySpec); } - private static byte[] toUncompressedPoint(final ECPublicKey publicKey) { - - int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1) - / Byte.SIZE; - - final byte[] uncompressedPoint = new byte[1 + 2 * keySizeBytes]; - int offset = 0; - uncompressedPoint[offset++] = 0x04; - - final byte[] x = publicKey.getW().getAffineX().toByteArray(); - if (x.length <= keySizeBytes) { - System.arraycopy(x, 0, uncompressedPoint, offset + keySizeBytes - - x.length, x.length); - } else if (x.length == keySizeBytes + 1 && x[0] == 0) { - System.arraycopy(x, 1, uncompressedPoint, offset, keySizeBytes); - } else { - throw new IllegalStateException("x value is too large"); - } - offset += keySizeBytes; - - final byte[] y = publicKey.getW().getAffineY().toByteArray(); - if (y.length <= keySizeBytes) { - System.arraycopy(y, 0, uncompressedPoint, offset + keySizeBytes - - y.length, y.length); - } else if (y.length == keySizeBytes + 1 && y[0] == 0) { - System.arraycopy(y, 1, uncompressedPoint, offset, keySizeBytes); - } else { - throw new IllegalStateException("y value is too large"); - } + + /** + * To uncompressed point. + * + * @param publicKey the public key + * @return the encoded public key + */ + private static byte[] toUncompressedPoint(final ECPublicKey publicKey) { + + final byte[] xCoordBytes = toUnsignedBytes(publicKey.getW().getAffineX()); + final byte[] yCoordBytes = toUnsignedBytes(publicKey.getW().getAffineY()); + + int fieldSize = (publicKey.getParams().getCurve().getField().getFieldSize() + 7) / 8; + int keySizeBytes = 2 * fieldSize + 1; + final byte[] uncompressedPoint = new byte[keySizeBytes]; + + System.arraycopy(xCoordBytes, 0, uncompressedPoint, 1 + fieldSize - xCoordBytes.length, xCoordBytes.length); + System.arraycopy(yCoordBytes, 0, uncompressedPoint, 1 + 2 * fieldSize - yCoordBytes.length, yCoordBytes.length); + uncompressedPoint[0] = 0x04; return uncompressedPoint; } - static final BigInteger MODULUS = - new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16); - - static final BigInteger POW = MODULUS.add(BigInteger.ONE).shiftRight(2); - // Given a 33-byte compressed public key, this returns a 65-byte uncompressed key. - private static byte[] decompressPubkey(byte[] compressedKey, EllipticCurve curve ) { + + /** + * Decompress public key. + * + * @param compressedKey the compressed public key + * @param ecParameters the elliptic curve parameters + * @return uncompressed encoded public key + */ + private static byte[] decompressPubkey(byte[] compressedKey, ECParameterSpec ecParameters ) { + EllipticCurve curve = ecParameters.getCurve(); + // Check array length and type indicator byte - if (compressedKey.length != 33 || compressedKey[0] != 2 && compressedKey[0] != 3) + if (compressedKey[0] != 2 && compressedKey[0] != 3) { throw new IllegalArgumentException(); + } final byte[] xCoordBytes = Arrays.copyOfRange(compressedKey, 1, compressedKey.length); final BigInteger xCoord = new BigInteger(1, xCoordBytes); // Range [0, 2^256) + ECFieldFp ecf = (ECFieldFp) curve.getField(); + BigInteger modulus = ecf.getP(); + BigInteger pow = modulus.add(BigInteger.ONE).shiftRight(2); + BigInteger temp = xCoord.pow(2).add(curve.getA()); temp = temp.multiply(xCoord); temp = temp.add(curve.getB()); - temp = temp.modPow(POW, MODULUS); - - //temp = sqrtMod(temp.add(curveParamB)); - + temp = temp.modPow(pow, modulus); boolean tempIsOdd = temp.testBit(0); boolean yShouldBeOdd = compressedKey[0] == 3; - if (tempIsOdd != yShouldBeOdd) - temp = temp.negate().mod(MODULUS); + if (tempIsOdd != yShouldBeOdd) { + temp = temp.negate().mod(modulus); + } final BigInteger yCoord = temp; - - // Copy the x coordinate into the new - // uncompressed key, and change the type byte - byte[] result = Arrays.copyOf(compressedKey, 65); + + final byte[] yCoordBytes = toUnsignedBytes(yCoord); + + int fieldSize = (curve.getField().getFieldSize() + 7) / 8; + byte[] result = new byte[2 * fieldSize + 1]; + System.arraycopy(compressedKey, 0,result, 1 + fieldSize - compressedKey.length, compressedKey.length); + System.arraycopy(yCoordBytes, 0,result, 1 + 2 * fieldSize - yCoordBytes.length, yCoordBytes.length); + // set uncompressed key indicator result[0] = 0x04; - // Carefully copy the y coordinate into uncompressed key - final byte[] yCoordBytes = yCoord.toByteArray(); - for (int i = 0; i < 32 && i < yCoordBytes.length; i++) - result[result.length - 1 - i] = yCoordBytes[yCoordBytes.length - 1 - i]; - return result; } + /** + * To unsigned bytes. + * + * @param i the i + * @return the unsigned bytes of the integer + */ private static byte[] toUnsignedBytes(BigInteger i) { byte[] b = i.abs().toByteArray(); //remove top sign bit diff --git a/src/test/java/org/uic/barcode/test/ECKeyEncoderTest521.java b/src/test/java/org/uic/barcode/test/ECKeyEncoderTest521.java new file mode 100644 index 0000000..d70e38e --- /dev/null +++ b/src/test/java/org/uic/barcode/test/ECKeyEncoderTest521.java @@ -0,0 +1,232 @@ +package org.uic.barcode.test; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.util.Arrays; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Before; +import org.junit.Test; +import org.uic.barcode.utils.AlgorithmNameResolver; +import org.uic.barcode.utils.ECKeyEncoder; + +public class ECKeyEncoderTest521 { + + public static Provider provider = null; + + @Before public void initialize() { + + provider = new BouncyCastleProvider(); + Security.addProvider(provider); + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + } + + @Test public void testEncodeCompressed() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + // some additional encoding tricks + byte[] compressedBC = getCompressed(key,name); + String compressedHexBC = Hex.toHexString(compressedBC); + + byte[] publicKeyBytes = ECKeyEncoder.getEncoded(key, ECKeyEncoder.ENCODING_X962_COMPRESSED); + String compressedHex = Hex.toHexString(publicKeyBytes); + + if (!compressedHexBC.equals(compressedHex)) { + assert(compressedHexBC.equals(compressedHex)); + } + + assert(Arrays.equals(compressedBC, publicKeyBytes)); + + assert(compressedHexBC.equals(compressedHex)); + } + } + + @Test public void testDecodeCompressed() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + // some additional encoding tricks + byte[] compressedBC = getCompressed(key,name); + + PublicKey publicKey = ECKeyEncoder.fromEncoded(compressedBC, "1.3.132.0.35", provider); + + compareKeys((ECPublicKey) publicKey, key); + + } + } + + @Test public void testEncodeUnCompressed() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + byte[] uncompressedBC = getUnCompressed(key,name); + //String uncompressedHex = Hex.toHexString(uncompressedBC); + + byte[] publicKeyUnComp = ECKeyEncoder.getEncoded(key, ECKeyEncoder.ENCODING_X962_UNCOMPESSED); + + if (!Arrays.equals(uncompressedBC, publicKeyUnComp)) { + assert(Arrays.equals(uncompressedBC, publicKeyUnComp)); + } + + assert(Arrays.equals(uncompressedBC, publicKeyUnComp)); + + } + } + + @Test public void testDecodeUnCompressed() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + // some additional encoding tricks + byte[] compressedBC = getUnCompressed(key,name); + //String compressedHex = Hex.toHexString(compressedBC); + + PublicKey publicKey = ECKeyEncoder.fromEncoded(compressedBC, "1.3.132.0.35", provider); + + compareKeys((ECPublicKey) publicKey, key); + + } + } + + @Test public void testEncodeX509() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + byte[] publicKeyBcX509 = key.getEncoded(); + + byte[] publicKeyBytes = ECKeyEncoder.getEncoded(key, ECKeyEncoder.ENCODING_X509); + assert(Arrays.equals(publicKeyBcX509, publicKeyBytes)); + + } + } + + @Test public void testDecodeX509() throws Exception { + + for (int i = 0; i < 10; i++) { + + String name = "secp521r1"; + + AlgorithmNameResolver.addMap(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, "1.3.132.0.35", "EC"); + + ECPublicKey key = createECPublicKey(name); + + // some additional encoding tricks + byte[] compressedBC = key.getEncoded(); + //String compressedHex = Hex.toHexString(compressedBC); + + PublicKey publicKey = ECKeyEncoder.fromEncoded(compressedBC, "1.3.132.0.35", provider); + + compareKeys((ECPublicKey) publicKey, key); + + } + } + + + + + private ECPublicKey createECPublicKey(String name) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + ECKeyPairGenerator gen = new ECKeyPairGenerator(); + SecureRandom secureRandom = new SecureRandom(); + X9ECParameters secnamecurves = SECNamedCurves.getByName(name); + ECDomainParameters ecParams = new ECDomainParameters(secnamecurves.getCurve(), secnamecurves.getG(), + secnamecurves.getN(), secnamecurves.getH()); + ECKeyGenerationParameters keyGenParam = new ECKeyGenerationParameters(ecParams, secureRandom); + gen.init(keyGenParam); + + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC"); + ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("secp521r1"); + kpg.initialize(ecGenParameterSpec); + ECPublicKey key = (ECPublicKey) kpg.generateKeyPair().getPublic(); + return key; + } + + private byte[] getCompressed(ECPublicKey key, String name) { + + byte[] x = key.getW().getAffineX().toByteArray(); + byte[] y = key.getW().getAffineY().toByteArray(); + + // assumes that x and y are (unsigned) big endian encoded + BigInteger xbi = new BigInteger(1, x); + BigInteger ybi = new BigInteger(1, y); + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + ECCurve curve = x9.getCurve(); + ECPoint point = curve.createPoint(xbi, ybi); + + return point.getEncoded(true); + } + + private byte[] getUnCompressed(ECPublicKey key, String name) { + + byte[] x = key.getW().getAffineX().toByteArray(); + byte[] y = key.getW().getAffineY().toByteArray(); + + // assumes that x and y are (unsigned) big endian encoded + BigInteger xbi = new BigInteger(1, x); + BigInteger ybi = new BigInteger(1, y); + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + ECCurve curve = x9.getCurve(); + ECPoint point = curve.createPoint(xbi, ybi); + + return point.getEncoded(false); + + } + + private void compareKeys(ECPublicKey key1, ECPublicKey key2) { + + assert(key1.getW().getAffineX().equals(key2.getW().getAffineX()) ); + assert(key1.getW().getAffineY().equals(key2.getW().getAffineY()) ); + + } + +} -- cgit v1.2.3