Search code examples
javacryptographyjce

How do I create a PrivateKey object from private key bytes?


Say I have a seed: this is a seed that i might have lying around somewhere
I then hash the seed to produce a byte array of size 32
From what I understand (from this article), this is now my private key bytes. How would I go around turning these bytes into a PrivateKey object?

From what I've seen, it is something like:

KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));

But for this to work, the private key bytes should be in the PKCS #8 format. And since I generated my private key bytes by hashing a seed, they obviously aren't.

So my question is: how do I encode my private key bytes into PKCS #8, OR alternatively, is there another class to do this besides PKCS8EncodedKeySpec?

I am using ECDSA
and
I am using secp256k1


Solution

  • ... do I encode my private key bytes into PKCS #8, OR alternatively, is there another class to do this besides PKCS8EncodedKeySpec?

    Yes and yes.

    Option A: constructing a DER encoding like PKCS8 can be complex in general, but for the specific case of PKCS8-unencrypted containing an EC key in 'named' (OID) form, which is what everyone (and in particular Java crypto) uses nowadays, is simpler and for a given curve consists of a fixed prefix followed by the private key bytes. See my answer to this Q which is mostly about the computation of publickey from privatekey, but my example uses the PKCS8-ized privatekey in the process of answering something else.

    Note in java 9 up (released after that answer) java.xml.bind.DatatypeConverter is no longer available by default in JavaSE, so you either need to make it a dependency or substitute something functionally equivalent -- there are many Qs about this.

    Option B: instead of using PKSC8 (encoded) representation, you can use ECPrivateKeySpec. As also noted in that answer, standard (Oracle/Open) Java doesn't provide an easy way to get the parameters alone, so the best I've found is to generate a dummy key and discard it:

    /* workaround is unnecessary
    KeyPairGenerator kg = KeyPairGenerator.getInstance("EC");
    kg.init(new ECGenParameterSpec("secp256k1"));
    ECParameterSpec param = ((ECPublicKey)kg.generateKeyPair().getPublic()).getParams();
    --- instead do: */
    AlgorithmParameters a = AlgorithmParameters.getInstance("EC");
    a.init(new ECGenParameterSpec("secp256k1"));
    ECParameterSpec p = a.getParameterSpec(ECParameterSpec.class);
    
    BigInteger s = new BigInteger(1, /*byte[32] privatekey value*/);
    KeyFactory kf = KeyFactory.getInstance("EC");
    PrivateKey kp = kf.generatePrivate(new ECPrivateKeySpec(s, param));
    // castable to ECPrivateKey if desired/needed
    

    If you instead use BouncyCastle's direct (non-JCA) API it does provide EC parameters by name; I can find some dupes for that if you want.

    Caveat: if 'lying around somewhere' is NOT the output of a correctly implemented random process using sufficent entropy, and in particular the 'seed' is either chosen or remembered by a human, doing this is insecure. And if you use this for Bitcoin -- which you didn't say, but is the use case of most ignorant, untrained people who suddenly want to do ECC because they think it will make them sooper-kewl, l33t, rich, sexy and famous -- you will probably lose your money; see https://en.bitcoin.it/wiki/Brainwallet . But that's offtopic for StackOverflow.