Reputation: 688
I have the following keys created with openssl (the algorithms etc. are specified by the counterpart):
openssl req -newkey rsa-pss -new -nodes -x509 -days 3650 -pkeyopt rsa_keygen_bits:4096 -sigopt rsa_pss_saltlen:32 -keyout key.pem -out cert.pem
openssl x509 -pubkey -noout -in cert.pem > pubkey.pem
I have the following working Java code:
private static String createJwtToken() {
try {
Path privateKeyPath = Path.of("/Users/abc/bzstcerts/key.pem");
Path publicKeyPath = Path.of("/Users/abc/bzstcerts/pubkey.pem");
RSAPrivateKey privateKey = (RSAPrivateKey)readPrivateKey(privateKeyPath);
RSAPublicKey publicKey =(RSAPublicKey) readPublicKey(publicKeyPath);
Algorithm algorithm = Algorithm.RSA256( publicKey, privateKey);
return JWT.create()
.withIssuer("iss")
.withSubject("subject")
.withAudience("audience")
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + (5 * 60 * 1000L)))
.withJWTId(UUID.randomUUID().toString())
.withNotBefore(new Date(System.currentTimeMillis() - 60 * 1000L))
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static PrivateKey readPrivateKey(Path privateKeyPath) throws Exception {
byte[] keyBytes = Files.readAllBytes(privateKeyPath);
keyBytes = Base64.getDecoder().decode(keyBytes);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSASSA-PSS");
return keyFactory.generatePrivate(spec);
}
private static PublicKey readPublicKey(Path publicKeyPath) throws Exception {
byte[] keyBytes = Files.readAllBytes(publicKeyPath);
keyBytes = Base64.getDecoder().decode(keyBytes);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSASSA-PSS");
return keyFactory.generatePublic(spec);
}
Here the private and public keys are loaded using RSASSA-PSS and casted to RSAPrivateKey and RSAPublicKey respectively.
I try to achieve the same with jose in node but I have no success.
import * as fs from 'fs'
import * as jose from 'jose'
import * as uuid from 'uuid'
import path from 'node:path'
const jwtUuid = uuid.v4()
const currentTime = Math.floor(Date.now() / 1000)
const expirationTime = currentTime + 300
const notValidBeforeTime = currentTime - 60
const clientId = process.env.CLIENT_ID
const audienceUrl = process.env.AUDIENCE_URL
const getSignedJWTToken = async () => {
const signOptions: jose.JWTPayload = {
iss: clientId,
sub: clientId,
aud: audienceUrl,
iat: currentTime,
exp: expirationTime,
jti: jwtUuid,
nbf: notValidBeforeTime,
}
const privateKey = await getPrivateKey()
const signedJwt = await new jose.SignJWT(signOptions)
.setProtectedHeader({ alg: 'PS256' })
.setIssuer(clientId)
.setSubject(clientId)
.setAudience(audienceUrl)
.setIssuedAt(currentTime)
.setExpirationTime(expirationTime)
.setJti(jwtUuid)
.setNotBefore(notValidBeforeTime)
.sign(privateKey)
console.log(`\nsignedJwt in generateRequestToken:\n${signedJwt}`)
return signedJwt
}
const certPath = path.resolve('./assets/cert')
const privateKeyName = 'key.pem'
const getPrivateKey = async () => {
const privateKeyPath = `${certPath}/${privateKeyName}`
const privateKeyString = fs.readFileSync(privateKeyPath, 'utf-8')
return await jose.importPKCS8(privateKeyString, 'PS256')
}
const publicKeyName = 'pubkey.pem'
const getPublicKey = async () => {
const publicKeyPath = `${certPath}/${publicKeyName}`
const publicKeyString = fs.readFileSync(publicKeyPath, 'utf-8')
console.log(`\npublicKeyString from readFileSync:\n${publicKeyString}`)
return await jose.importSPKI(publicKeyString, 'PS256')
}
I can load the keys and sign the jwt using PS256 algorithm, but I can't load the keys with PS256 algorithm, convert them to RS256 algorithm and sign the jwt. How can I achieve this? Thanks a lot for your help in advance!
Upvotes: 0
Views: 774
Reputation: 1
i am working on a very similar problem. The German Ministry of Finance has the requirement that the certificate has to have rsa-pss. But every implementation of generating JWT has a problem using such a key generated with rsa-pss to sign. It may validate locally but it never does on jwt.io or at the API. Generally you can sign with openssl using a command like: openssl dgst -sha256 -binary -sign /path/to/key.pem And it works if you use a certificate that wasn't created with "rsa-pss", jwt.io says they are valid, but not having rsa-pss is not an option here.
Upvotes: 0
Reputation: 211
I can load the keys and sign the jwt using PS256 algorithm, but I can't load the keys with PS256 algorithm, convert them to RS256 algorithm and sign the jwt. How can I achieve this? Thanks a lot for your help in advance!
Here the private and public keys are loaded using RSASSA-PSS and casted to RSAPrivateKey and RSAPublicKey respectively.
You've generated an RSASSA-PSS key, it cannot be used for RS256, at least not in Node.js, there's no manipulation in the crypto module you could do to drop/change the key's parameters.
If you want a key that's usable for both RS256 and PS256 in Node.js use this
openssl req -newkey rsa -new -nodes -x509 -days 3650 -pkeyopt rsa_keygen_bits:4096 -keyout key.pem -out cert.pem
openssl x509 -pubkey -noout -in cert.pem > pubkey.pem
Upvotes: 1