What exactly does the response from https://www.googleapis.com/oauth2/v3/certs mean? I am trying to verify a JWT I got via the Google OpenID Connect process using node-jsonwebtokens and the key used to verify the signature must be one of those two. The source code however suggests that node-jsonwebtokens awaits a different key format than available in the response:
[
{
'kty': 'RSA',
'alg': 'RS256',
'use': 'sig',
'kid': 'e53139984bd36d2c230552441608cc0b5179487a',
'n': 'w5F_3au2fyRLapW4K1g0zT6hjF-co8hjHJWniH3aBOKP45xuSRYXnPrpBHkXM6jFkVHs2pCFAOg6o0tl65iRCcf3hOAI6VOIXjMCJqxNap0-j_lJ6Bc6TBKgX3XD96iEI92iaxn_UIVZ_SpPrbPVyRmH0P7B6oDkwFpApviJRtQzv1F6uyh9W_sNnEZrCZDcs5lL5Xa_44-EkhVNz8yGZmAz9d04htNU7xElmXKs8fRdospyv380WeaWFoNJpc-3ojgRus26jvPy8Oc-d4M5yqs9mI72-1G0zbGVFI_PfxZRL8YdFAIZLg44zGzL2M7pFmagJ7Aj46LUb3p_n9V1NQ',
'e': 'AQAB'
},
{
'kty': 'RSA',
'alg': 'RS256',
'use': 'sig',
'kid': 'bc8a31927af20860418f6b2231bbfd7ebcc04665',
'n': 'ucGr4fFCJYGVUwHYWAtBNclebyhMjALOTUmmAXdMrCIOgT8TxBEn5oXCrszWX7RoC37nFqc1GlMorfII19qMwHdC_iskju3Rh-AuHr29zkDpYIuh4lRW0xJ0Xyo2Iw4PlV9qgqPJLfkmE5V-sr5RxZNe0T1jyYaOGIJ5nF3WbDkgYW4GNHXhv-5tOwWLThJRtH_n6wtYqsBwqAdVX-EVbkyZvYeOzbiNiop7bDM5Td6ER1oCBC4NZjvjdmnOh8-_x6vB449jL5IRAOIIv8NW9dLtQd2DescZOw46HZjWO-zwyhjQeYY87R93yM9yivJdfrjQxydgEs8Ckh03NDATmQ',
'e': 'AQAB'
}
]
It doesn't have the classical BEGIN PUBLIC KEY block, is it maybe encoded? Is there an additional step needed?
That code indeed deals with PEM-formatted certificates/keys instead of the JSON Web Key (JWK) formatted key material that is published by Google on the URL that you gave.
There is however a different URL that serves the (same) key material in PEM format here: https://www.googleapis.com/oauth2/v1/certs. You can use that representation in node-jsonwebtoken.
I was able to use n and e successfully with the following code, and then create a public key in Java to decode a JWT which was sent by Google.
String n_str = "...string value of n.... ";
String e_str = "... string value of e...";
byte[] n_bytes = Base64.getUrlDecoder().decode(n_str);
byte[] e_bytes = Base64.getUrlDecoder().decode(e_str);
BigInteger n = new BigInteger(1, n_bytes);
BigInteger e = new BigInteger(1, e_bytes);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n,e);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Related
I am trying to create a JWE Token using the node-jose library's createEncrypt method. The problem is, I want to set the kid to a certain value. But when importing the key using the jose.JWK.asKey method, it's automatically calculating the kid and won't let me change/set it. Here is the sample code:
const { JWK, JWE } = require('node-jose');
encrypt = async (raw, format = 'compact', contentAlg = "A128CBC-HS256", alg = "RSA-OAEP-256") => {
let _publicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxO+O52a1eAkbYatqpPAk
vhTz5VAdNloWhbmAmwPQl9202VKxU+yOCbwZSU8NqwVubHMgnxdycgJw+zGslXgz
zHPpmA5evOY2AVjpcE9avKfp523M5gxOaAnQCxat6KxORIJWLSF84EUtrzLIxgle
bvDyhfoHMGVSYiP89UQPTR+uu6irFRkdu2zFDPOx2/4XdtyAbJlWdj4Fes0v3CcA
/jDO9EmwVEiySCuagLWnrvHvCV0mCDN167JSVjeeKZy4Q36WyF0VqytxmW+mXn+m
IfcLlj5vXSXp81pI1Iyg86KZtW3A6dP8QuRlYwHJU7Z+m7AeIHtC+ol0/eBPYPwk
PQIDAQAB
-----END PUBLIC KEY-----`
let publicKey = await JWK.asKey(_publicKey, "pem");
publicKey.kid = "932ea6bb-2623-4dc3-96b1-c4be61e97569";
console.log(publicKey)
const buffer = Buffer.from(JSON.stringify(raw))
const encrypted = await JWE.createEncrypt({ format: format, contentAlg: contentAlg, fields: { alg: alg, iat: (new Date().getTime()), exp: 30000} }, publicKey)
.update(buffer).final();
return encrypted;
}
let raw = {
"mobileNumber": "1234567890",
"customerId": "000000000",
"sessionId": "3a600342-a7a3-4c66-bbd3-f67de5d7096f",
};
encrypt(raw).then((data)=> {console.log(data)})
Here is the encrypted JWE Token generated:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJpYXQiOjE2NDAyNDU0NzY5ODEsImV4cCI6MzAwMDAsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJraWQiOiI1bUxtdmVHdng0RHVucGlfTnBhajhxZlByRHNDYW9NV29JeWRoM003SzA4In0.SUpO7X0XXbkqQtNGVvLMNo6oGi1GrTzAR1FtXlL8ngg9Uvd91nkLiRqgcmjKBBEE1M330WV_HrUYNs2NVRcXTDcr41fSwvHSu7veK_YDj-m73LoMKlmojeB6GIRUIXIw7oaqgFSOSb_Xgq_zwG9WGa07h2OgOzeFxKNJCvt1J2i_v2Tt61yyet0hdMinT78whDGgf_JW4LUSaXY9wsqsuQSDkKWFLvxHqNmq7nGPLpgEJjm1GPF0slPvdWsARsMEttbPK9VpoMUvMcqy5bWVWSmj2MEGTVw6ua-uFw9fEgyn095wl-s8lEfZFkFaiFN7ps5VwqVV2tihpnYrCVIYAA.jxR4Gw_Gcy9Sexw-wMBKtQ.TzugQZCFgQiolIBc2FAEQ0ZbvNdPFzE2z0m9cFxWQtADEijOCzQjZreVvnsVjHFXdP_w-YcnCbmKXkwalWnFMo7wkjuuJ0fAsTfTOEiBjuIPvMa0k04C97Rc4ZYszzzL7xxwW0RnqoNxiQMkea3H0A.qAqgcg_DLV1vHzb0EIq-9A
If you check this on jwt.io, you can see the kid is already calculated & set. How can I set/change the kid here?
The kid is calculated automatically, when it's not known during the import:
When importing or generating a key that does not have a "kid" defined, a "SHA-256" thumbprint is calculated and used as the "kid".
(see https://github.com/cisco/node-jose#obtaining-a-keys-thumbprint)
But in the call to JWK.asKey, you can pass an additional parameter extras, that sets values for existing fields or contains additional fields for the JWK.
For your use case, you can set a kid as a JSON object
let kid = "932ea6bb-2623-4dc3-96b1-c4be61e97569";
let publicKey = await JWK.asKey(_publicKey, "pem", {"kid":kid});
console.log(publicKey.toJSON())
Output:
{
kty: 'RSA',
kid: '932ea6bb-2623-4dc3-96b1-c4be61e97569',
n: 'xO-O52a1eAkbYatqpPAkvhTz5VAdNloWhbmAmwPQl9202VKxU-yOCbwZSU8NqwVubHMgnxdycgJw-zGslXgzzHPpmA5evOY2AVjpcE9avKfp523M5gxOaAnQCxat6KxORIJWLSF84EUtrzLIxglebvDyhfoHMGVSYiP89UQPTR-uu6irFRkdu2zFDPOx2_4XdtyAbJlWdj4Fes0v3CcA_jDO9EmwVEiySCuagLWnrvHvCV0mCDN167JSVjeeKZy4Q36WyF0VqytxmW-mXn-mIfcLlj5vXSXp81pI1Iyg86KZtW3A6dP8QuRlYwHJU7Z-m7AeIHtC-ol0_eBPYPwkPQ',
e: 'AQAB'
}
import requests
import json
url = "********"
payload = json.dumps({
"username": "*****",
"password": "*****"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
#*** Convert response into a dictionary ***
r = json.loads(response.text)
# *** print the value of they key 'accessToken'
bearerToken = r['accessToken']
print(bearerToken)
Output
{'accessToken': 'e************************************', 'tokenType': 'Bearer'}
What am I trying to achieve?
Grab only the censored code after 'accessToken' and store the new Access Token in a string to use it in HTTP requests.
Note: 'accessToken' is a value of another key called accessToken. So the traditional method of printing the value of the key has already been used in the output shown above.
Complete output:
{
"accessToken" : {
"accessToken" : "e******************",
"tokenType" : "Bearer"
},
"refreshToken" : {
"id" : "6*************",
"lastAccessedTime" : 1***********,
"refreshToken" : "e*************"
}
}
In Python, you can use substrings to accomplish what you want. If the length of the prefix is constant, then all you would need to do is use a slice with constant index,
In your example, it looks like you have an accessToken key, and inside that key is another dictionary holding a key that can change between entries. Assuming that you want the censored portion after e in accessToken, you can access that using:
bearerToken = r['accessToken'][accessToken][1:]
This will give you the access token "******************", which is everything from index 1 onwards. If you just want the entire string, you can omit the [1:] portion.
The solution was to edit the code provided by yeeshue99, the 2nd value to 'accessToken' with the '' marks. the [1:] was also not needed.
Solution
bearerToken = r['accessToken']['accessToken']
i would need to validate a token with jwt.io
To do this I have a token (license)
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6MjcwMzYwLCJwYWNrYWdlc.........."
and a json (certificate)
"keys": [
{
"kid": "1",
"e": "AQAB",
"kty": "RSA",
"alg": "RS256",
"n": "rXYc2Ehtb42R83kLIw56biI/ABOp03lzbYHdXI0caeli.......",
"use": "sig"
}
inside the debugger of the jwt.io site with this information I receive that the "signature is verified".
I'm looking for some scripts on the net that can allow me to do the same thing
Same on the jwt.io site I find that I can use the code reported here https://github.com/PhilJay/JWT which, however, if I understand correctly, does not use the RS256 algorithm but ES256 and therefore should not work.
I'm trying the same but honestly I don't understand what the decoder is asking for in the example
val valid = JWT.verify (tokenString, jwk, decoder)
the first two I believe are the data I have.
can you give me some suggestions or give me some other code to verify the signature?
The problem is here
val nInt = Biginteger(1, Base64.getUrlDecoder().decode(n))
You have to take only the element n of cert and not all cert(json)
val e = obj_cert.asJsonObject["e"].asString
val n = obj_cert.asJsonObject["n"].asString.replace('+', '-').replace('/', '_').replace("=", "")
val eInt = BigInteger(1, Base64.getUrlDecoder().decode(e))
val nInt = BigInteger(1, Base64.getUrlDecoder().decode(n))
val spec = RSAPublicKeySpec(nInt, eInt)
val publicKey = KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey
val algorithm: Algorithm = Algorithm.RSA256(publicKey, null)
val verifier: JWTVerifier = com.auth0.jwt.JWT.require(algorithm).build()
verifier.verify(license)
I'm trying to get the key size from a PEM-encoded certificate.
I've tried to extract it using node-forge (https://www.npmjs.com/package/node-forge), converting it first to a forge certificate and then trying to get the key size from that.
However, I cant find the right information in the forge certificate.
There is this info in the forge certificate under public key, and I think I should be able to get the information from the modulus 'n'
{ n: BigInteger { data: [Array], t: 74, s: 0 },
e: BigInteger { data: [Array], t: 1, s: 0 },
encrypt: [Function],
verify: [Function] },
Here, the array for 'n' is
BigInteger {
data:
[ 108702707,
223366147,
1633698,
222104385,
2057385,
196952745,
204102614,
1342314,
215447298,
167299729,
234087419,
218888278,
143261467,
196197892,
83562517,
50733325,
114027487,
90758946,
9956532,
60800276,
8677133,
7005374,
254551822,
214728639,
42558032,
110792918,
136202203,
78922972,
40753235,
245284543,
194070574,
248422593,
5163396,
151359098,
77422943,
72471134,
181405400,
207346591,
185707006,
185418315,
263158064,
111864582,
186113288,
54738616,
138771291,
249640899,
232181943,
117496275,
231520296,
184509360,
179085501,
215072100,
85449772,
136664237,
71259060,
139830485,
264798471,
266417322,
142764588,
177236257,
17830318,
9879037,
168589759,
121974085,
54883138,
87144585,
7724711,
192243183,
194739694,
159581652,
122617175,
91020203,
117134207,
13 ],
t: 74,
s: 0 },
Can I get the key size from this array? Or is there another easier way to get the keySize from a PEM-encoded certificate?
I think the approach below should work, since n is used as the modulus for the public and private keys, its length in bits is the key length too.
So if (as you say) we can get the size of n in bits we have the key length.
The public key is stored as a big-integer, so we can call the bitLength() function.
The code below is working for me:
const forge = require('node-forge');
const fs = require('fs');
function getCertificatePublicKeyBitLength(pemFile) {
const certificate = forge.pki.certificateFromPem(fs.readFileSync(pemFile));
return certificate.publicKey.n.bitLength();
}
console.log("Bit length: ", getCertificatePublicKeyBitLength("cert.pem"));
I'm using node forge (https://www.npmjs.com/package/node-forge) to create a public and private key pair (nodeforge.pki.rsa.generateKeyPair()) for testing purposes. The modulo (n) and exponent (e) of the public key is represented as an array of decimal values. However, the service that will provide the public key for encryption in production represents modulo and exponent in hex values. My question now is, how can I easily transform these values created by node forge from decimal to hex (I then want to use these values in NodeRSA (https://github.com/rzcoder/node-rsa)):
let pair = nodeforge.pki.rsa.generateKeyPair();
var key = new NodeRSA();
key.importKey({
n: new Buffer(pair.publicKey.n.data, 'hex'),
e: new Buffer(pair.publicKey.e.data, 'hex')
});
The format of the public key provided by node forge is:
"publicKey": {
"n": {
"data": [
95452259,
62292680,
234781115,
46168802,
112578909,
58538647,
44874864,
186853653,
197342112,
104676488,
229032986,
262511790,
101189381,
134141598,
55892282,
145717908,
78729040,
107967002,
62922480,
120440374,
262852613,
65361518,
128040269,
29796715,
171859902,
201093939,
131227011,
165441987,
9273795,
130804401,
240125365,
221986724,
32034899,
180589760,
106981089,
188935555,
21820073,
96309166,
54553752,
100808208,
198611546,
11382488,
139519439,
212193674,
26780810,
225883560,
101310123,
78467199,
181998612,
8435669,
195696345,
41284550,
129888967,
137003722,
74415901,
92893643,
57787310,
143216416,
42433969,
100838107,
34667532,
136300465,
185624737,
67347,
93800454,
226047286,
76852009,
57120535,
266741908,
27775411,
39685199,
4113773,
214629603,
12
],
"t": 74,
"s": 0
},
"e": {
"data": [
65537
],
"t": 1,
"s": 0
}
The reason I want to do that is that I want to test if the encryption facility works correctly (first, encrypt a value with the public key, then decrypt the value with the private key again), because the public key is provided by an external party and else I have no possibility to check, as the private key is not provided.
Thanks a lot in advance!
You can use toString(16) to convert to hexadecimal
var nHex = publicKey.n.toString(16);
var eHex = publicKey.e.toString(16);