The value returned from Java PrivateKey.getEncoded()
is an encoded privatekey, as the name says. Specifically as documented in the superclass Key
it is an ASN.1 (DER) encoding of PKCS#8 = Public-Key Cryptography Standard #8, Private Key Information Syntax. PKCS#8 handles privatekeys for a wide variety of different public-key algorithms and contains metadata in addition to the actual key, namely an 'AlgorithmIdentifier' which identifies the algorithm and any parameters of the algorithm; for the ECC algorithm(s) (ECDSA, ECDH, ECMQV and more share one key format) these parameters specify the elliptic curve group used, and although there are several options for this specification, in practice everyone, including here Java, uses the 'namedCurve' option which defines the curve group by an ASN.1 OID aka Object Identifier.
The PKCS#8 structure then contains the actual privatekey data, 'wrapped' in an OCTET STRING, in a format that varies depending on the algorithm. For ECC, this format is defined (AFAICT) in SEC1 by SECG/Certicom and contains the actual privatekey value (a number, expressed as an OCTET STRING) plus optionally the curve spec and the public key.
Your first code uses BouncyCastle, and Bouncy generates the encoding with the wrapped value including the optional curve spec and public key, making it longer.
Your second code uses by default the Oracle/Sun provider (SunEC), which generates the encoding without those options, but still including the required AlgorithmIdentifier making it longer than the actual privatekey value. It also uses a different curve: initializing a SunEC generator with the integer 256 chooses secp256r1 (aka P-256, prime256v1) NOT secp256k1. If you change this to use new ECGenParameterSpec("secp256k1")
as the parameter then SunEC will also generate secp256k1, but without the options, giving a 64-byte encoding.
In both cases, if you want only the privatekey number, cast to java.security.interfaces.ECPrivateKey
and use getS()
. If you want the result in a byte/octet array, as is conventional, note BigInteger.toByteArray()
returns a variable-length result and you often need to left zero trim or pad it.
And if you did want the publickey, it has a similar scheme using 'X.509' encoding which contains both an AlgorithmIdentifier and a BIT STRING wrapping the actual publickey value, and thus is longer than the raw publickey value. However in this case interfaces.ECPublicKey
and spec.ECPoint
won't construct the encoding for you; using the Bouncy-only types can be more convenient.