I'm reading openssh format elliptic curve public keys (RFC 5656, section 3.1) and would like to get from a BigInteger Q value to an ECPublicKey
instance using JCE (rather than say BouncyCastle). I want to do this to verify JWT signatures.
e.g. https://api.github.com/users/davidcarboni/keys:
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK8hPtB72/sfYgNw1WTska2DNOJFx+QhUxuV6OLINSD2ty+6gxcM8yZrvMqWdMePGRb2cGh8L/0bGOk+64IQ/pM=
It looks like I can use ECPublicKeySpec
. This takes two parameters. An ECPoint
and an ECParameterSpec
. I'm able to get the parameter spec using the following JCE code (and the openssh identifier from the key data, say "nistp256"):
ECParameterSpec getECParameterSpec(String identifier) {
try {
AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
String name = identifier.replace("nist", "sec") + "r1";
parameters.init(new ECGenParameterSpec(name));
return parameters.getParameterSpec(ECParameterSpec.class);
} catch (InvalidParameterSpecException | NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Unable to get parameter spec for identifier " + identifier, e);
}
}
I've successfully parsed the Q value from the key data. RFC 5656 tells me that "Q is the public key encoded from an elliptic curve point into an octet string") however the constructor of JCE's ECPoint
class takes two parameters, X and Y.
Can I get to X and Y from Q, or do I need to take a different approach?
(NB I quite rightly don't have access to the private key)
The base64 portion of your input has the equivalent hex of
65 63 64 jumped out to me as "ecd" (sure enough, "ecdsa-sha2-nistp256").
So the blob looks to be
04 AF 21 ... 10 FE 93
)The encoded ECPoint starts with
04
indicating it is an uncompressed point (the most common encoding). The04
encoding rules (from http://www.secg.org/sec1-v2.pdf, 2.3.5, step 3) say that the remaining payload is X and Y, each left-padded with zeros to the encoding size of the curve field.So your ECPoint looks like
In C# you'd need to a) reverse the bytes of each of Qx and Qy (because the .NET BigInteger expects Little Endian, and these are Big Endian) and b) put a padding 0x00 byte as more significant than the 0xAF and 0xB7 bytes, since they have their high bits set (and would be interpreted as negative numbers). Don't know if Java has either of those quirks.