6
votes

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?

2

2 Answers

11
votes

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);
9
votes

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.