How does one convert a public EC code point and curve name into a PublicKey?

nsayer picture nsayer · Mar 25, 2014 · Viewed 8.1k times · Source

I have two 32 byte long byte arrays representing the X and Y values for an EC Public Key. I know that the curve is the named curve "prime256v1".

How can I turn that into a Java PublicKey object?

The JCE appears to provide no facilities whatsoever to use named curves.

Bouncycastle's example code does not appear to compile with any version of bouncycastle I can find.

WTF?

Answer

dave_thompson_085 picture dave_thompson_085 · Mar 26, 2014

I don't see any way in JCE to use a named curve directly for a key, but it can be used for key generation, and the parameters can then be extracted from that key:

    // generate bogus keypair(!) with named-curve params
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
    ECGenParameterSpec gps = new ECGenParameterSpec ("secp256r1"); // NIST P-256 
    kpg.initialize(gps); 
    KeyPair apair = kpg.generateKeyPair(); 
    ECPublicKey apub  = (ECPublicKey)apair.getPublic();
    ECParameterSpec aspec = apub.getParams();
    // could serialize aspec for later use (in compatible JRE)
    //
    // for test only reuse bogus pubkey, for real substitute values 
    ECPoint apoint = apub.getW();
    BigInteger x = apoint.getAffineX(), y = apoint.getAffineY();
    // construct point plus params to pubkey
    ECPoint bpoint = new ECPoint (x,y); 
    ECPublicKeySpec bpubs = new ECPublicKeySpec (bpoint, aspec);
    KeyFactory kfa = KeyFactory.getInstance ("EC");
    ECPublicKey bpub = (ECPublicKey) kfa.generatePublic(bpubs);
    //
    // for test sign with original key, verify with reconstructed key
    Signature sig = Signature.getInstance ("SHA256withECDSA");
    byte [] data = "test".getBytes();
    sig.initSign(apair.getPrivate());
    sig.update (data);
    byte[] dsig = sig.sign();
    sig.initVerify(bpub);
    sig.update(data);
    System.out.println (sig.verify(dsig));

You do get the parameters, but apparently no longer linked to the OID, which might make a difference. In particular it may be treated as "arbitrary" or "explicit" in TLS and not work even though the TLS parties support that same curve by name.

Note that openssl uses the name prime256v1 but not everyone does. Java (sun.) uses secp256r1, or the OID. If you are actually getting this pubkey from openssl, note that JCE can directly read the X.509 SubjectPublicKeyInfo format, which openssl calls PUBKEY, including the named (OID) form.