Reputation: 47
I am trying to create an access token for line API
below is my private key
{
"alg": "RS256",
"d": "TC4Ij......wqSQIQ",
"dp": "nJg12KiRo...nc02GdK-d8",
"dq": "UzRtJ.....HA70",
"e": "AQAB",
"ext": true,
"key_ops": [
"sign"
],
"kty": "RSA",
"n": "rj_fXZ.....kuNDvOOHimw",
"p": "70k5HA_wTnmAEm.....z4pG79DZ5U",
"qi": "7j7gzQq....NfS7B8HRiC4"
}
I wanted to create a token by the the key with payload and header using the code below
String token = null;
byte[] privateKey = ResourceUtil.readStr("line_key/privatekey-dev.json", StandardCharsets.UTF_8).getBytes();
// ペイロードの設定
Map<String, Object> payloadClaims = new HashMap<>();
// line channel id
payloadClaims.put("iss", "XXXXXXX");
payloadClaims.put("sub", "XXXXXXX");
payloadClaims.put("aud", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
payloadClaims.put("exp", new Date(System.currentTimeMillis() + 60 * 30));
payloadClaims.put("token_exp", 60 * 60 * 24 * 30);
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privateKey);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);
//Algorithm algorithm = Algorithm.RSA256(null, privKey);
token = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "RS256")
.setHeaderParam("kid", "XXXXXXX").setClaims(payloadClaims)
.signWith(SignatureAlgorithm.RS256, privKey)
.compact();
} catch (Exception e) {
log.error(e.getMessage());
}
Since the private the not encoded so I didnt use Base64 to decode it.
But it always showed
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
Even if I use Base64 to decode the private key read from the json file it didnt work.
byte[] keyContentAsBytes = Base64.getMimeDecoder().decode(privateKey);
Can any body tell me how to solve this issue.
Thanks
PS: jwt can be generated by python with
key = RSAAlgorithm.from_jwk(privateKey)
JWT = jwt.encode(payload, key, algorithm="RS256", headers=headers, json_encoder=None)
Upvotes: 0
Views: 2538
Reputation: 269847
First, because you are re-inventing the date-arithmetic wheel, your token will only be valid for 1.8 seconds. I doubt that's what you intended. Use higher level APIs from java.time
instead of magic numbers and obsolete types from java.util
so that your code states the intent clearly.
You don't have a PKCS-#8–encoded key spec, so you can't use PKCS8EncodedKeySpec
. For some reason I can't fathom, JWK ignored existing standards like PKCS #8 and invented a new format with a bloated yet still opaque JSON encoding.
If you really need to convert the private key yourself, you can get the parameters from the JWK and create a RSAPrivateCrtKeySpec
. I say "really," because I'd be surprised if the library you are using doesn't have some means to load a PrivateKey
from a JWK representation.
Here is the mapping between JWK RSA private key members and Java's RSAPrivateCrtKeySpec
properties.
JWK member | RSAPrivateCrtKeySpec property |
---|---|
n | modulus |
e | publicExponent |
d | privateExponent |
p | primeP |
q | primeQ |
dp | primeExponentP |
dq | primeExponentQ |
qi | crtCoefficient |
Base-64 decode each value in the JWK, then create a BigInteger
with the resulting magnitude, specifying the sign is positive: new BigInteger(+1, magnitude)
. Instantiate a RSAPrivateCrtKeySpec
and use that with your KeyFactory
in place of the PCKS8EncodedKeySpec
.
Upvotes: 3