Can I create a JCE ECPublicKey from a Q value from

2019-08-17 14:24发布

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)

1条回答
Melony?
2楼-- · 2019-08-17 15:04

The base64 portion of your input has the equivalent hex of

00 00 00 13 65 63 64 73 61 2D 73 68 61 32 2D 6E 
69 73 74 70 32 35 36 00 00 00 08 6E 69 73 74 70 
32 35 36 00 00 00 41 04 AF 21 3E D0 7B DB FB 1F 
62 03 70 D5 64 EC 91 AD 83 34 E2 45 C7 E4 21 53 
1B 95 E8 E2 C8 35 20 F6 B7 2F BA 83 17 0C F3 26 
6B BC CA 96 74 C7 8F 19 16 F6 70 68 7C 2F FD 1B 
18 E9 3E EB 82 10 FE 93 

65 63 64 jumped out to me as "ecd" (sure enough, "ecdsa-sha2-nistp256").

So the blob looks to be

  • Big Endian length of a string/payload (19)
  • The string "ecdsa-sha2-nistp256"
  • Big Endian length of a string/payload (8)
  • The string "nistp256"
  • Big Endian length of a payload (0x41 == 65)
  • The encoded ECPoint Q (04 AF 21 ... 10 FE 93)

The encoded ECPoint starts with 04 indicating it is an uncompressed point (the most common encoding). The 04 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

04
Qx:
AF 21 3E D0 7B DB FB 1F 62 03 70 D5 64 EC 91 AD
83 34 E2 45 C7 E4 21 53 1B 95 E8 E2 C8 35 20 F6
Qy:
B7 2F BA 83 17 0C F3 26 6B BC CA 96 74 C7 8F 19
16 F6 70 68 7C 2F FD 1B 18 E9 3E EB 82 10 FE 93

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.

查看更多
登录 后发表回答