Reputation: 7450
I am trying to use SECP-256K1 (Bitcoin's ECC curve) to do a SHA256withECDSA signature on some data with the code posted below. The codes do not always work properly and can be rather erratic and unstable at times.
I would like help stabilizing the code below.
Code for generating ECC keypair on Signer's end:
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
parameters.init(new ECGenParameterSpec("secp256k1"));
ECParameterSpec ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(ecParameterSpec);
KeyPair ecKeyPair = keyGen.generateKeyPair();
ECPublicKey deviceEcPub = (ECPublicKey) ecKeyPair.getPublic();
ECPrivateKey deviceEcPriv = (ECPrivateKey) ecKeyPair.getPrivate();
Convert the ECC public key into 04<Public X Axis><Public Y Axis>
format as per Bitcoin specifications of public keys:
int cursor = 0;
byte[] devicePubKey = new byte[65];
devicePubKey[cursor] = (byte) 0x04;
cursor++;
System.arraycopy(deviceEcPub.getW().getAffineX().toByteArray(), 0, devicePubKey, cursor, 32);
cursor += 32;
System.arraycopy(deviceEcPub.getW().getAffineY().toByteArray(), 0, devicePubKey, cursor, 32);
Sign some data on the Signer's end:
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(deviceEcPriv);
sig.update(data, 0, data.length);
byte[] signature = sig.sign();
Verifier's end receive the 04<Public X Axis><Public Y Axis>
encoded public key and converts from a X,Y coordinates into an actual ECC Public Key for use:
byte[] x = new byte[32];
byte[] y = new byte[32];
// Skips the first byte containing the 0x04 byte and copies the rest
System.arraycopy(devicePubKey, 1, x, 0, 32);
System.arraycopy(devicePubKey, 33, y, 0, 32);
Verifier uses the X and Y coordinates and reconstructs the ECPublicKey:
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
parameters.init(new ECGenParameterSpec("secp256k1"));
ECParameterSpec ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(x), new BigInteger(y)), ecParameterSpec);
ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(ecPublicKeySpec);
Executes the verification by the Verifier on the data and signature supplied:
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initVerify(publicKey);
sig.update(data, offset, length);
return sig.verify(signature);
The result of verification is sometimes True
and sometimes False
with seemingly random looking occurrences.
I would like advise on the above code to ensure that it doesn't have erratic behaviours.
Upvotes: 1
Views: 157
Reputation: 7450
Ok, I noticed that if the 1st byte starts with 0x00, the X or Y will be 33 bytes long and thus I am not properly copying X or Y bytes.
To mitigate the above where there can be 0x00 as the first byte for either or both X and Y, I would read the first byte of X and Y and if the first byte is 0x00, I will skip copying the first byte and read as much bytes until 32 bytes for each of X and Y are fully copied.
There are also cases where X and/or Y may fall short of 32 bytes (i.e. only 31 bytes long)
Another way is to measure the length. If the length is more than 32, skip bytes until last 32 bytes. If the length is less than 32, copy the bytes and put 0x00 bytes to the front of the byte array.
My re-implementation of extracting the public X and Y values are shown in the codes below.
int cursor = 0;
int copy = 0;
// Get X
byte[] X = deviceEcPub.getW().getAffineX().toByteArray();
// Evaluate length
if (X.length > 32) {
copy = 1;
System.arraycopy(X, copy, devicePubKey, cursor, 32);
} else if (X.length < 32) {
System.arraycopy(X, copy, devicePubKey, cursor + (32 - X.length), X.length);
} else {
System.arraycopy(X, copy, devicePubKey, cursor, 32);
}
// Increment cursor
cursor += 32;
// Reset copy offset status
copy = 0;
// Get Y
byte[] Y = deviceEcPub.getW().getAffineY().toByteArray();
// Evaluate length
if (Y.length > 32) {
copy = 1;
System.arraycopy(Y, copy, devicePubKey, cursor, 32);
} else if (Y.length < 32) {
System.arraycopy(Y, copy, devicePubKey, cursor + (32 - Y.length), Y.length);
} else {
System.arraycopy(Y, copy, devicePubKey, cursor, 32);
}
Result is the signature verification becomes stabilized.
Upvotes: 1