7

The BouncyCastle cryptography APIs allow for creating and verifying digital signatures using the regular java.security package objects, such as java.security.PublicKey, java.security.PrivateKey and their container java.security.KeyPair.

Suppose I use OpenSSL to create a .pem (or, if easier, a .der file) containing the elliptic curve private key I want to use in my application. For example, it looks like this:

-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK
oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun
GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==
-----END EC PRIVATE KEY-----

How do I use the BouncyCastle APIs to obtain a java.security.KeyPair containing both this private key and a corresponding public key?

Please note I want to use the APIs available in BouncyCastle 1.50 (which is current at the time of writing) and no deprecated APIs. This unfortunately excludes the PEMReader class used in other SO answers. Furthermore, this question is specific to the format of elliptic curves; they contain additional parameters when compared RSA or DSA key files.

DCKing
  • 4,253
  • 2
  • 28
  • 43
  • The `EC PARAMETERS` block in your _file_ is an accident of the way `openssl ecparam -genkey` works by default; it is not needed or used as part of the actual key and you can omit it by specifying `-noout` which is admittedly somewhat unobvious. The actual key structure ('hidden' in the base64/DER data) for EC(DSA/DH) _does_ contain some parameter info which RSA doesn't but DSA _does_. – dave_thompson_085 Jan 30 '17 at 23:11

3 Answers3

18

In addition to the standard JCE approach shown by divanov as long as you give it the correct input (see my comment thereto), or just using JCE in the first place like your selfanswer, BouncyCastle 1.48 up DOES still contain the old PEMReader functionality just organized a bit differently and for this case you can use something like:

static void SO22963581BCPEMPrivateEC () throws Exception {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    Reader rdr = new StringReader ("-----BEGIN EC PRIVATE KEY-----\n"
            +"MHQCAQEEIDzESrZFmTaOozu2NyiS8LMZGqkHfpSOoI/qA9Lw+d4NoAcGBSuBBAAK\n"
            +"oUQDQgAE7kIqoSQzC/UUXdFdQ9Xvu1Lri7pFfd7xDbQWhSqHaDtj+XY36Z1Cznun\n"
            +"GDxlA0AavdVDuoGXxNQPIed3FxPE3Q==\n"+"-----END EC PRIVATE KEY-----\n");
    Object parsed = new org.bouncycastle.openssl.PEMParser(rdr).readObject();
    KeyPair pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair)parsed);
    System.out.println (pair.getPrivate().getAlgorithm());
}
Community
  • 1
  • 1
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • I had the padding : `-----BEGIN EC PARAMETERS-----`. Had to strip off the EC Params and then read it using PEMParser. Then got the KeyPair object correctly. Otherwise PEMParser reads it as a ASN1Object if there are EC Params present in the key. I hope I am not losing the objective by stripping off the EC Parameters ? – AnirbanDebnath Dec 03 '19 at 09:48
  • 1
    Anirban: yes you don't need the parameters block. See my comment on the _question_ approx. 3 years ago. – dave_thompson_085 Dec 03 '19 at 21:28
  • Ha ha. --3 years ago-- I believe software industry is a small world !! – AnirbanDebnath Dec 05 '19 at 09:30
12

In Java this will be pretty much the same code. After striping guarding strings away and decoding Base64 data give it to this utility method:

public static PrivateKey keyToValue(byte[] pkcs8key)
    throws GeneralSecurityException {

    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8key);
    KeyFactory factory = KeyFactory.getInstance("ECDSA");
    PrivateKey privateKey = factory.generatePrivate(spec);
    return privateKey;
}
JaredHatfield
  • 6,381
  • 2
  • 29
  • 32
divanov
  • 6,173
  • 3
  • 32
  • 51
  • I've marked this answer as correct as it presents the answer in the form originally asked. It should be clearer for the general audience, as well. – DCKing Apr 30 '14 at 10:31
  • 4
    Looking at the [JCA Documentation](http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyFactory) makes me believe the argument to `KeyFactory.getInstance()` is supposed to be `EC` rather than `ECDSA`. Am I wrong? – Adam Mackler May 21 '14 at 20:52
  • 2
    It depends on provider used. For BouncyCastle it could be both EC and ECDSA. For Sun Provider it could be EC only, I don't use it much. – divanov May 21 '14 at 21:20
  • hi,what is guarding strings ?when i strip begin**----,i get Input byte array has incorrect ending byte exception – zhouxiang Dec 29 '15 at 09:12
  • Guarding strings are the ones starting and ending with `-----`. Ask a question with example data and exception trace. – divanov Dec 29 '15 at 09:18
  • 15
    This won't work for (the relevant part of) the data in this Q, which is in OpenSSL's 'legacy' format NOT **PKCS8 format as used by JCE**; notice PEM headers `BEGIN/END EC PRIVATE KEY` _not_ `BEGIN/END PRIVATE KEY` as used for unencrypted PKCS8. You can convert it using `openssl pkey -in oldfile -out newfile` in modern versions (1.0.0 up) or `openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile` in all versions, then un-base64 _that_ into `PKCS8EncodedKeySpec`. – dave_thompson_085 Jan 30 '17 at 23:18
  • This format is defined for EC keys in RFC 5915 and is PKCS#8 compatible. – divanov Jan 30 '17 at 23:39
4

Since I only need this for a quick and dirty demo, I solved it in the following way (in Scala). First, I generate a public private key pair in the REPL and print out its data:

Security.addProvider(new BouncyCastleProvider)

val SignatureScheme = "some signature scheme, eg ECDSA"
val RandomAlgorithm = "some random algorithm, eg SHA1PRNG"

val keygen = KeyPairGenerator.getInstance(SignatureScheme)
val rng = SecureRandom.getInstance(RandomAlgorithm)
rng.setSeed(seed)
keygen.initialize(KeySize, rng)

val kp = keygen.generateKeyPair()
println(kp.getPublic.getEncoded.toSeq) // toSeq so that Scala actually prints it
println(kp.getPrivate.getEncoded.toSeq)

Then using the generated data,

val hardcodedPublic = Array[Byte]( /* data */ )
val hardcodedPrivate = Array[Byte]( /* data */ )

val factory = KeyFactory.getInstance(SignatureScheme)

val publicSpec = new X509EncodedKeySpec(hardcodedPublic)
val publicKey = factory.generatePublic(publicSpec)

val privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate)
val privateKey = factory.generatePrivate(privateSpec)

The key thing you need to know here is that by default public key data uses X509 encoding and private key data uses PKCS8 encoding. It should be possible to get OpenSSL to output these formats and parse them manually, but I did not check how.

I used information from this blog post about SpongyCastle (which is Android's BouncyCastle alias) quite helpful. It is unfortunate that documentation is fragmented like this, and that BouncyCastle's wiki was down at the time of this question.

Update: the BouncyCastle wiki is up, and you can find the documentation here.

DCKing
  • 4,253
  • 2
  • 28
  • 43