How to extract key size in NodeJS - node.js

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"));

Related

How can I overwrite the automatically calculated kid when importing a JWK from PEM

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'
}

How to resolve Enums as String values with Node grpc?

Using grpc with Node, Enums in responses to my queries are resolving as integer values. However, when I make the same queries with BloomRPC, the Enums resolve as Integer values.
Is there a parameter or option to force these Enums to be resolved as String using Node grpc?
In our project, we use enum to help us ensure the integrity of a finite set of possibilities by eliminating human error. Why should we need to remember what the string value is when we have the protocol buffer enum so handy? Thus, we use the .proto as the source of truth; that's our rule.
To do that, follow these steps, which are written for ES6+ code.
Define your gRPC/Protobuf enum in a .proto file.
// life.proto
syntax = 'proto3';
package life;
enum Choices
{
EAT = 0;
DRINK = 1;
SLEEP = 2;
CODE = 3;
SKI = 4;
}
Install #grpc/proto-loader and #grpc/grpc-js.
$ npm i -s #grpc/proto-loader #grpc/grpc-js
Import the modules that pay the bills, so to speak. Load the .proto file directly into memory (don't compile).
// myNodeApp.js
import * as grpc from '#grpc/grpc-js'
import * as protoLoader from '#grpc/proto-loader'
import path from 'path'
// these options help make definitions usable in our code
const protoOptions = {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
}
// this allows us to prepare the path to the current dir
const dir = path.dirname(new URL(import.meta.url).pathname)
// this loads the .proto file
const lifeDef = protoLoader.loadSync(path.join(dir, 'life.proto'), protoOptions)
// this loads the package 'life' from the .proto file
const life = grpc.loadPackageDefinition(lifeDef).life
Take a peek at the enum Choices definition (in the same file).
// myNodeApp.js (cont'd)
console.log(life.Choices)
/* stdout */
{
format: 'Protocol Buffer 3 EnumDescriptorProto',
type: {
value: [ [Object], [Object], [Object], [Object], [Object] ],
name: 'Choices',
options: null
},
fileDescriptorProtos: [
<Buffer 0a ... 328 more bytes>
]
}
...look deeper...
console.log(life.Choices.value)
/* stdout */
{
value: [
{ name: 'EAT', number: 0, options: null },
{ name: 'DRINK', number: 1, options: null },
{ name: 'SLEEP', number: 2, options: null },
{ name: 'CODE', number: 3, options: null },
{ name: 'SKI', number: 4, options: null }
],
name: 'Choices',
options: null
}
Use the enum.
// myNodeApp.js
const myDay = { // plain JSON (or define a gRPC message, same same)
dawn: life.Choices.type.value[1].name,
morning: life.Choices.type.value[0].name,
afternoon: life.Choices.type.value[4].name,
evening: life.Choices.type.value[3].name,
night: life.Choices.type.value[2].name
}
You could write an accessor or utility function to manage the key lookup (by passing the imported grpc enum and index), like so:
export const getEnumByName = function (protoEnum, needle) {
return protoEnum.type.value.find(p => {
return p.name === needle
})
}
export const getEnumByNum = function (protoEnum, needle) {
return protoEnum.type.value.filter(p => {
return p.number = needle
})
}
export const getEnumKeys = function (protoEnum, key = 'name') {
return protoEnum.type.value.map(p => {
return p[key]
})
}
Inverting and assigning a value to a Message is what's already covered in other answers, just set the enum field to the string value using, you guessed it, the string that represents the enum name, which you access using the code above.
This is along the lines of how we do it. Clean and simple, just a touch obscure until you look "under the hood" one day.
Learn more about #grpc/proto-loader and #grpc/grpc-js. Hope this helps someone out there in the wild. :)
If you are using the #grpc/proto-loader library, you can set the option enums to the value String (not the string "String", the constructor function String). Then all enum values will be represented by their name strings.

Node-Forge: how can I convert the public key's modulo and exponent from decimal to hex?

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);

Google OpenID Connect Public Keys

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);

transforming strings in a golang struct

I've got a json file of AES encrypted secrets. The structure is:
{
"username": "asdf123ASLdf3",
"password": "elisjdvo4etQW"
}
And a struct to hold these values
type Secrets struct {
Username string `json:"username"`
Password string `json:"password"`
}
It's easy to load the encrypted json values into the struct, but what I really want is a struct with the unencrypted values.
So, for each value, I'd like to run it though a function:
aesDecrypt(key string, value string) string
I'm happy to have this done on the first load, or to move everything over into a new struct.
I would like to avoid repeating the json keys or the field names.
What's the best way to do this?
(Also open to other ways to manage encrypted secrets in Go)
One option is to define a custom JSON Unmarshaler. Another is, as you mention, copy it to another struct.
Implementing the Unmarshaler interface
The key insight is knowing that you can override json.Unmarshal's
behaviour by implementing the Unmarshaler interface. In our
case, that means defining a function func (ss *Secrets)
UnmarshalJSON(bb []byte) error that will do the AES Decryption when
you try to unmarshal any JSON to a Secrets.
package main
import "fmt"
import "encoding/json"
type Secrets struct {
Username string `json:"username"`
Password string `json:"password"`
}
func main() {
jj := []byte(`{
"username": "asdf123ASLdf3",
"password": "elisjdvo4etQW"
}`)
var ss Secrets
json.Unmarshal(jj, &ss)
fmt.Println(ss)
}
func aesDecrypt(key, value string) string {
return fmt.Sprintf("'%s' decrypted with key '%s'", value, key)
}
func (ss *Secrets) UnmarshalJSON(bb []byte) error {
var objmap map[string]*string
err := json.Unmarshal(bb, &objmap)
ss.Username = aesDecrypt("my key", *objmap["password"])
ss.Password = aesDecrypt("my key", *objmap["username"])
return err
}
This outputs a Secrets struct:
{'elisjdvo4etQW' decrypted with key 'my key'
'asdf123ASLdf3' decrypted with key 'my key'}
See it in action at the Go Playground.
Copying to another struct
You could simply make a new Secrets struct every time you need to
decrypt the JSON. This could be tedious if you do it alot, or if you
have no need for the intermediate state.
package main
import "fmt"
import "encoding/json"
type Secrets struct {
Username string `json:"username"`
Password string `json:"password"`
}
func main() {
jj := []byte(`{
"username": "asdf123ASLdf3",
"password": "elisjdvo4etQW"
}`)
var ss Secrets
json.Unmarshal(jj, &ss)
decoded := Secrets{
aesDecrypt(ss.Username, "my key"),
aesDecrypt(ss.Password, "my key")}
fmt.Println(decoded)
}
func aesDecrypt(key, value string) string {
return fmt.Sprintf("'%s' decrypted with key '%s'", value, key)
}
Check it out at Go Playground.
This has the same output as above:
{'elisjdvo4etQW' decrypted with key 'my key'
'asdf123ASLdf3' decrypted with key 'my key'}
Obviously, you would use a different version of aesDecrypt, mine's
just a dummy. And, as always, you should actually be checking the
returned errors in your own code.

Resources