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
... 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.