6

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?

nsayer
  • 16,925
  • 3
  • 33
  • 51

2 Answers2

14

It turns out that there is, in fact, another way to do this. The AlgorithmParameters class can apparently be used to translate an ECGenParameterSpec, with a named curve, into an ECParameterSpec object that you can use with a KeyFactory to generate a PublicKey object:

            ECPoint pubPoint = new ECPoint(new BigInteger(1, x),new BigInteger(1, y));
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", "SunEC");
            parameters.init(new ECGenParameterSpec("secp256r1"));
            ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
            ECPublicKeySpec pubSpec = new ECPublicKeySpec(pubPoint, ecParameters);
            KeyFactory kf = KeyFactory.getInstance("EC");
            return (ECPublicKey)kf.generatePublic(pubSpec);
nsayer
  • 16,925
  • 3
  • 33
  • 51
9

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.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • If the getEncoded() form of the resulting public key isn't the form that has the OID of the curve, then that won't work for me. I need to use the resulting PublicKey in a certificate request. BouncyCastle can do it, and I don't mind using BouncyCastle. It's just that I can't figure out how because their example code is b0rk3d so far as I can tell. – nsayer Mar 26 '14 at 14:10
  • Actually, never mind. I tried it, and it does indeed result in a getEncoded() that shows the right stuff when fed into openssl asn1parse. Yay! – nsayer Mar 26 '14 at 14:29
  • Assuming you mean the JCE one (not Bouncy) you're right. I debugged and sun.security.ec.ECNamedCurve does keep the curve name/OID and encode it, even though it isn't available on the API. – dave_thompson_085 Mar 28 '14 at 07:11
  • `ECParameterSpec` does not seem to be serializable in itself. – Maarten Bodewes Aug 24 '14 at 23:29
  • You're right. You could still do it with reflection -- I'm sure without trying because you can do *anything* with reflection -- but that's a sledgehammer. Debugging some more, I find `static public sun.security.ec.NamedCurve.getECParameterSpec (String name_or_OID_as_string)` -- this isn't a documented class, but since this whole feature isn't (yet?) in the documented API, that seems the least bad workaround. – dave_thompson_085 Aug 27 '14 at 07:45