Trying to generate an uncompressed representation in java almost killed me! Wish I would have found this (especially Maarten Bodewes' excellent answer) earlier. I'd like to point out an issue in the answer and offer an improvement:
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");
}
This ugly bit is necessary because of the way BigInteger
spits out byte array representations: "The array will contain the minimum number of bytes required to represent this BigInteger
, including at least one sign bit" (toByteArray javadoc). This means a.) if the highest bit of x
or y
is set, a 0x00
will be prepended to the array and b.) leading 0x00
's will be trimmed. The first branch deals with the trimmed 0x00
's and the second one with the prepended 0x00
.
The "trimmed leading zero's" lead to an issue in the code determining the expected length of x
and y
:
int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)
/ Byte.SIZE;
If the order
of the curve has a leading 0x00
it gets truncated and is not considered by bitLength
. The resulting key length is too short. The incredibly convoluted (but proper?) way to get at the bitlength of p
would be:
int keySizeBits = publicKey.getParams().getCurve().getField().getFieldSize();
int keySizeBytes = (keySizeBits + 7) >>> 3;
(The +7
is to compensate for bit lengths which are not powers of 2.)
This issue affects at least one curve delivered with the standard JCA (X9_62_c2tnb431r1
) which has an order with a leading zero:
000340340340340 34034034034034034
034034034034034 0340340340323c313
fab50589703b5ec 68d3587fec60d161c
c149c1ad4a91