Reputation: 950
Trying to generate signed JWT token with EDCSA private jkey but getting errored out
Generate public and private key using link
https://techdocs.akamai.com/iot-token-access-control/docs/generate-jwt-ecdsa-keys
private String doGenerateToken(Map<String, Object> claims, String subject)
throws NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(new BouncyCastleProvider());
String EC_PRIVATE_KEY_STR = "-----BEGIN EC PRIVATE KEY-----\n"
+ "MHQCAQEEIBuSmY4MFZ938j0sno1nOICb0ScfIebC1O7DXkvf6UDMoAcGBSuBBAAK\n"
+ "oUQDQgAELAWORZuUv+lpO34bVoYHv6T3Gey+GtuHFB+TH1+l0tRKfKELHcmHlDOK\n"
+ "ebiIegDVhHd6jYx2yT1nOBddjDHCVw==\n"
+ "-----END EC PRIVATE KEY-----\n";
final KeyFactory keyPairGenerator = KeyFactory.getInstance("EC"); // EC is ECDSA in Java
ECPrivateKey EC_PRIVATE_KEY = (ECPrivateKey) keyPairGenerator.generatePrivate(
new PKCS8EncodedKeySpec(
Base64.decodeBase64(removeEncapsulationBoundaries(EC_PRIVATE_KEY_STR))));
var currentDateTime = new Date(System.currentTimeMillis());
final String jwt = Jwts.builder()
.setHeaderParam("kid", "any")
.signWith(SignatureAlgorithm.ES256, EC_PRIVATE_KEY)
.compact();
return jwt;
}
Exception :
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 01
Upvotes: 1
Views: 1745
Reputation: 38771
Java PKCS8EncodedKeySpec
requires a key in PKCS8 format (and specifically PKCS8-clear); that's why the name says PKCS8. That's not a PKCS8-format key so it fails.
To read the key:
If you're generating the key with OpenSSL, as per the website you link (though on the previous page), the easiest way is to convert with OpenSSL:
$ cat ecprivate-sec1
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBuSmY4MFZ938j0sno1nOICb0ScfIebC1O7DXkvf6UDMoAcGBSuBBAAK
oUQDQgAELAWORZuUv+lpO34bVoYHv6T3Gey+GtuHFB+TH1+l0tRKfKELHcmHlDOK
ebiIegDVhHd6jYx2yT1nOBddjDHCVw==
-----END EC PRIVATE KEY-----
$ openssl pkey <ecprivate-sec1 >ecprivate-pkcs8
$ cat ecprivate-pkcs8
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgG5KZjgwVn3fyPSyejWc4
gJvRJx8h5sLU7sNeS9/pQMyhRANCAAQsBY5Fm5S/6Wk7fhtWhge/pPcZ7L4a24cU
H5MfX6XS1Ep8oQsdyYeUM4p5uIh6ANWEd3qNjHbJPWc4F12MMcJX
-----END PRIVATE KEY-----
If you have an ancient (0.9.x) version of OpenSSL, instead do openssl pkcs8 -topk8 -nocrypt
.
That value, with the BEGIN/END lines removed and de-base64'ed, will be accepted by Java.
Or OpenSSL can generate this format directly; instead of ecparam -name secp256k1 -genkey -noout ...
use:
openssl genpkey -algorithm ec -pkeyopt ec_paramgen_curve:secp256k1 >ecprivate-pkcs8
# but see below
(Also you can use either ec -pubout
or pkey -pubout
to get the public key, and the format OpenSSL uses for public key is, after de-PEM-ing, the 'X.509' format Java wants.)
Alternatively, if BouncyCastle is available including bcpkix, that can handle the 'traditional' SEC1/rfc5915 format in PEM directly. With your above EC_PRIVATE_KEY_STR value just do:
PrivateKey privkey = new JcaPEMKeyConverter().getKeyPair (
(PEMKeyPair) new PEMParser(new StringReader(EC_PRIVATE_KEY_STR)).readObject()
).getPrivate();
(you can declare, and cast to, ECPrivateKey
if you want but don't need to).
But that's still wrong.
I don't if anyone did quality control on that website, but JWS ES256 uses ECDSA with P-256, the curve also known as secp256r1 and prime256v1, NOT secp256k1 which is an entirely different and wrong curve. Instead when generating with OpenSSL you should specify either P-256
or prime256v1
.
The example they give using command-line openssl
to sign doesn't catch this, because OpenSSL is a general purpose tool used for many things not just JOSE/JWS, and for some (other) things secp256k1 is correct. More surprising to me, jjwt -- at least the slightly old version I have -- also doesn't catch this. Nevertheless the result does not comply to the standard and will not be reliably accepted by other systems.
Upvotes: 2