Skip to content

Commit

Permalink
Fix #466: Fix public key conversion from point (#467)
Browse files Browse the repository at this point in the history
  • Loading branch information
petrdvorak committed Apr 13, 2023
1 parent e94a04d commit c92d359
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,38 @@ public PublicKey convertBytesToPublicKey(byte[] keyBytes) throws InvalidKeySpecE
* Converts provided byte array representing X coordinate of an EC public key.
*
* @param xBytes X coordinate.
* @param yBytes Y coordinate.
* @return Public key with provided coordinates.
* @throws InvalidKeySpecException When provided bytes are not a correct key representation.
* @throws CryptoProviderException When crypto provider is incorrectly initialized.
* @throws GenericCryptoException When public key is invalid.
*/
public PublicKey convertPointBytesToPublicKey(byte[] xBytes) throws InvalidKeySpecException, CryptoProviderException, GenericCryptoException {
final ByteBuffer compressedBytes = ByteBuffer.allocate(xBytes.length + 1);
compressedBytes.put((byte) 0x03);
compressedBytes.put(xBytes);
return convertBytesToPublicKey(compressedBytes.array());
public PublicKey convertPointBytesToPublicKey(byte[] xBytes, byte[] yBytes) throws InvalidKeySpecException, CryptoProviderException, GenericCryptoException {
try {
// Make sure the values are interpreted as positive integer.
final BigInteger x = new BigInteger(1, xBytes);
final BigInteger y = new BigInteger(1, yBytes);

// Validate the point is correct
final ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
if (ecSpec == null) { // can happen with incorrectly initialized crypto provider.
throw new CryptoProviderException("Crypto provider does not support the secp256r1 curve");
}
final org.bouncycastle.math.ec.ECPoint point = ecSpec.getCurve().createPoint(x, y);
publicKeyValidator.validate(ecSpec.getCurve(), point);

// Generate public key using Java security API
final AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", PowerAuthConfiguration.CRYPTO_PROVIDER_NAME);
parameters.init(new ECGenParameterSpec("secp256r1"));
final ECParameterSpec ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);

final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(new ECPoint(x, y), ecParameterSpec);
return KeyFactory.getInstance("EC", PowerAuthConfiguration.CRYPTO_PROVIDER_NAME).generatePublic(ecPublicKeySpec);
} catch (NoSuchAlgorithmException | InvalidParameterSpecException | NoSuchProviderException ex) {
throw new CryptoProviderException(ex.getMessage(), ex);
} catch (IllegalArgumentException ex) {
throw new GenericCryptoException(ex.getMessage(), ex);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public void validate(ECCurve curve, ECPoint point) throws GenericCryptoException
throw new GenericCryptoException("Invalid public key with point equal to the point at infinity");
}

BigInteger n = curve.getOrder();
ECPoint calculatedPoint = ECAlgorithms.referenceMultiply(point, n);
final BigInteger n = curve.getOrder();
final ECPoint calculatedPoint = ECAlgorithms.referenceMultiply(point, n);
if (!calculatedPoint.isInfinity()) {
throw new GenericCryptoException("Point order does not match the order defined in EC curve");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -41,6 +42,8 @@
*/
public class KeyConversionUtilsTest {

private KeyGenerator keyGenerator = new KeyGenerator();

/**
* Default constructor
*/
Expand Down Expand Up @@ -108,21 +111,42 @@ public void testConvertPoint() throws Exception {
final List<byte[]> testVector = Arrays.asList(
// Public Key 1
Base64.getDecoder().decode("c+aNszGLNA/CfexMq3lUXLY5xS2Pqougl+OmTX3toZc="),
Base64.getDecoder().decode("OqrmEXN5Ou7zAmDD5V0Uq4SdgRUmbtfLw2fg+anUOxU="),
Base64.getDecoder().decode("BHPmjbMxizQPwn3sTKt5VFy2OcUtj6qLoJfjpk197aGXOqrmEXN5Ou7zAmDD5V0Uq4SdgRUmbtfLw2fg+anUOxU="),

// Public Key 2
Base64.getDecoder().decode("TUh1vYPq7OXRXGEacVO+pUQr8vj8elfytOV9+ebaBvo="),
Base64.getDecoder().decode("BE1Idb2D6uzl0VxhGnFTvqVEK/L4/HpX8rTlffnm2gb6CGvImC7L9jiStTUSRF7Z1wgdzBcuvOkUwNH7LGzlWd0=")
Base64.getDecoder().decode("CGvImC7L9jiStTUSRF7Z1wgdzBcuvOkUwNH7LGzlWd0="),
Base64.getDecoder().decode("BE1Idb2D6uzl0VxhGnFTvqVEK/L4/HpX8rTlffnm2gb6CGvImC7L9jiStTUSRF7Z1wgdzBcuvOkUwNH7LGzlWd0="),

// Public Key 3
Base64.getDecoder().decode("CKi+0FTAiBzE+8p8luu+o0EefMUwSUQ7qEmHwzM3KvY="),
Base64.getDecoder().decode("ANv71ruj5P2RD1a3jkFukNwhJsD/bj6fUvWD+4142RA2"),
Base64.getDecoder().decode("BAiovtBUwIgcxPvKfJbrvqNBHnzFMElEO6hJh8MzNyr22/vWu6Pk/ZEPVreOQW6Q3CEmwP9uPp9S9YP7jXjZEDY=")
);

for (int i = 0; i < testVector.size() / 2; i++) {
final int keyIndex = i * 2;
for (int i = 0; i < testVector.size() / 3; i++) {
final int keyIndex = i * 3;
final byte[] x = testVector.get(keyIndex);
final byte[] encoded = testVector.get(keyIndex + 1);
final PublicKey publicKey = instance.convertPointBytesToPublicKey(x);
final byte[] y = testVector.get(keyIndex + 1);
final byte[] encoded = testVector.get(keyIndex + 2);
final PublicKey publicKey = instance.convertPointBytesToPublicKey(x, y);
final PublicKey publicKeyExpected = instance.convertBytesToPublicKey(encoded);
assertEquals(publicKeyExpected, publicKey);
}

// random key test
for (int i = 0; i < 100; i++) {
final KeyPair keyPair = keyGenerator.generateKeyPair();
final ECPublicKey publicKeyOrig = (ECPublicKey) keyPair.getPublic();
final byte[] bytes = instance.convertPublicKeyToBytes(publicKeyOrig);
final byte[] x = publicKeyOrig.getW().getAffineX().toByteArray();
final byte[] y = publicKeyOrig.getW().getAffineY().toByteArray();
final PublicKey publicKey = instance.convertPointBytesToPublicKey(x, y);
final PublicKey publicKeyExpected = instance.convertBytesToPublicKey(bytes);
assertEquals(publicKeyExpected, publicKey);
}

}

}

0 comments on commit c92d359

Please sign in to comment.