Improper base64url - node.js

The JWT guide here - https://scotch.io/tutorials/the-anatomy-of-a-json-web-token#header - says they run base64url on this:
{
"typ": "JWT",
"alg": "HS256"
}
And they end up with this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
I try this code:
var b64u = require("base64url")
var rez = b64u(JSON.stringify({
"typ": "JWT",
"alg": "HS256"
}));
var shouldbe = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
console.log(rez);
console.log(shouldbe);
console.log(rez == shouldbe);
as seen here in online test:
https://tonicdev.com/56c5484d7a4ea10d0002c623/5733af59234d9d1200d8c818
however they are not matching.
Does anyone see any simple issue?

The Base64 output is dependent on which order of keys you receive in the string from the JSON.stringify call.
For reference, here is a working example using pre-built JSON strings.
let expected = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
let str1 = '{"alg":"HS256","typ":"JWT"}';
let str2 = '{"typ":"JWT","alg":"HS256"}';
// note that you don't need a library to Base64 encode strings in node
let base64str1 = new Buffer(str1).toString('base64');
let base64str2 = new Buffer(str2).toString('base64');
console.log(base64str1); // 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
console.log(base64str2); // 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
console.log('base64str1 equals expected?', base64str1 === expected); // true
console.log('base64str2 equals expected?', base64str2 === expected); // false

Related

separate GET request path from query variables

I have the url: http://www.localhost:8080/api/numbers/prime?amount=13
via GET request
I want to get api/numbers/prime
is there any way to do that in node.js vanilla?
You can use JavaScript split and slice methods on Strings.
let url = "http://www.localhost:8080/api/numbers/prime?amount=13";
let urlWithoutQuery = url.split("?")[0];
let path = urlWithoutQuery.slice(25); // To get the value after http://www.localhost:8080
You can use URL:
const url = new URL('http://www.localhost:8080/api/numbers/prime?amount=13')
then:
console.log(url.pathname);
// outputs:
// /api/numbers/prime
console.log(url.searchParams);
// outputs:
// URLSearchParams { 'amount' => '13' }
console.log(url.search);
// outputs:
// ?amount=13

How to unpack an google.protobuf.Any type in gRPC nodejs client?

My protobuf file is like this:
syntax = "proto3"; import "google/protobuf/any.proto";
service RoomService {
getTestAny (Hotelid) returns (google.protobuf.Any); }
message Hotelid {
string hotelid = 1;
}
message HtlInDate {
Hotelid hotelid = 1;
string date = 2;
}
My java-gRPC-server code is like that:
#Override
public void getTestAny(Roomservice.Hotelid request, StreamObserver<Any> responseObserver) {
Roomservice.Hotelid hotelid = Roomservice.Hotelid.newBuilder()
.setHotelid("This is Hotelid")
.build();
Roomservice.HtlInDate htlDate = Roomservice.HtlInDate.newBuilder()
.setHotelid(hotelid)
.setDate("This is Data")
.build();
responseObserver.onNext(Any.pack(htlDate));
responseObserver.onCompleted();
}
And I make a request from a nodejs-gRPC-client, which code is like that:
function () {
var client = new services.RoomServiceClient('localhost:6565',
grpc.credentials.createInsecure());
var request = new messages.Hotelid();
var hotelid = "ignore";
request.setHotelid(hotelid);
var call = client.getTestAny(request, function (err, response) {
var obj = response.toObject();
console.log(obj);
});
}
The response in nodejs-gRPC-client is a type of Any. And it contains a data array:
array:["type.googleapis.com/HtlInDate", Uint8Array[10,17,10...]]
I try to use response.toObject() to get HtlInDate instance but I just get like this:
obj:{
typeUrl:"type.googleapis.com/HtlInDate",
value:"ChEKD1RoaXMgaXMgSG90ZWxpZBIMVGhpcyBpcyBEYXRh"
}
So how can I unpack the Any type response and get the HtlInDate instance exactly? Thanks a lot if you have any idea about this!
Currently, the google.protobuf.Any type is not supported in Node.js, either in Protobuf.js, which gRPC uses by default, or by google-protobuf, which is the official first party protobuf implementation.
From documentation:
https://developers.google.com/protocol-buffers/docs/reference/javascript-generated#message
// Storing an arbitrary message type in Any.
const status = new proto.foo.ErrorStatus();
const any = new Any();
const binarySerialized = ...;
any.pack(binarySerialized, 'foo.Bar');
console.log(any.getTypeName()); // foo.Bar
// Reading an arbitrary message from Any.
const bar = any.unpack(proto.foo.Bar.deserializeBinary, 'foo.Bar');
Please take a note that for browser support you need to use webpack(probably with babel loader) or browserify
As found in google-protobuf tests Any is bundled with pack and unpack functions.
Your code could be unpacked like this:
function () {
var client = new services.RoomServiceClient('localhost:6565',
grpc.credentials.createInsecure());
var request = new messages.Hotelid();
var hotelid = "ignore";
request.setHotelid(hotelid);
var call = client.getTestAny(request, function (err, response) {
var obj = response.toObject();
console.log('Any content', obj);
var date = response.unpack(messages.HtlInDate.deserializeBinary, response.getTypeName());
console.log('HtlInDate', date.toObject());
});
}
This will deserialize the bytes received in the Any object.
You could also build some Any using pack function for wrapping TypeUrl and Value:
var someAny = new Any();
someAny.pack(date.serializeBinary(), 'HtlInDate')

Generating ECDSA signature with Node.js/crypto

I have code that generates a concatenated (r-s) signature for the ECDSA signature using jsrsasign and a key in JWK format:
const sig = new Signature({ alg: 'SHA256withECDSA' });
sig.init(KEYUTIL.getKey(key));
sig.updateHex(dataBuffer.toString('hex'));
const asn1hexSig = sig.sign();
const concatSig = ECDSA.asn1SigToConcatSig(asn1hexSig);
return new Buffer(concatSig, 'hex');
Seems to work. I also have code that uses SubtleCrypto to achieve the same thing:
importEcdsaKey(key, 'sign') // importKey JWK -> raw
.then((privateKey) => subtle.sign(
{ name: 'ECDSA', hash: {name: 'SHA-256'} },
privateKey,
dataBuffer
))
These both return 128-byte buffers; and they cross-verify (i.e. I can verify jsrsasign signatures with SubtleCrypto and vice versa). However, when I use the Sign class in the Node.js crypto module, I seem to get something quite different.
key = require('jwk-to-pem')(key, {'private': true});
const sign = require('crypto').createSign('sha256');
sign.update(dataBuffer);
return sign.sign(key);
Here I get a buffer of variable length, roughly 70 bytes; it does not cross-verify with jsrsa (which bails complaining about an invalid length for an r-s signature).
How can I get an r-s signature, as generated by jsrsasign and SubtleCrypto, using Node crypto?
The answer turns out to be that the Node crypto module generates ASN.1/DER signatures, while other APIs like jsrsasign and SubtleCrypto produce a “concatenated” signature. In both cases, the signature is a concatenation of (r, s). The difference is that ASN.1 does so with the minimum number of bytes, plus some payload length data; while the P1363 format uses two 32-bit hex encoded integers, zero-padding them if necessary.
The below solution assumes that the “canonical” format is the concatenated style used by SubtleCrypto.
const asn1 = require('asn1.js');
const BN = require('bn.js');
const crypto = require('crypto');
const EcdsaDerSig = asn1.define('ECPrivateKey', function() {
return this.seq().obj(
this.key('r').int(),
this.key('s').int()
);
});
function asn1SigSigToConcatSig(asn1SigBuffer) {
const rsSig = EcdsaDerSig.decode(asn1SigBuffer, 'der');
return Buffer.concat([
rsSig.r.toArrayLike(Buffer, 'be', 32),
rsSig.s.toArrayLike(Buffer, 'be', 32)
]);
}
function concatSigToAsn1SigSig(concatSigBuffer) {
const r = new BN(concatSigBuffer.slice(0, 32).toString('hex'), 16, 'be');
const s = new BN(concatSigBuffer.slice(32).toString('hex'), 16, 'be');
return EcdsaDerSig.encode({r, s}, 'der');
}
function ecdsaSign(hashBuffer, key) {
const sign = crypto.createSign('sha256');
sign.update(asBuffer(hashBuffer));
const asn1SigBuffer = sign.sign(key, 'buffer');
return asn1SigSigToConcatSig(asn1SigBuffer);
}
function ecdsaVerify(data, signature, key) {
const verify = crypto.createVerify('SHA256');
verify.update(data);
const asn1sig = concatSigToAsn1Sig(signature);
return verify.verify(key, new Buffer(asn1sig, 'hex'));
}
Figured it out thanks to
https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1
ECDSA signatures between Node.js and WebCrypto appear to be incompatible?

Using SHA-256 with NodeJS Crypto

I'm trying to hash a variable in NodeJS like so:
var crypto = require('crypto');
var hash = crypto.createHash('sha256');
var code = 'bacon';
code = hash.update(code);
code = hash.digest(code);
console.log(code);
But looks like I have misunderstood the docs as the console.log doesn't log a hashed version of bacon but just some information about SlowBuffer.
What's the correct way to do this?
base64:
var crypto = require('crypto');
const hash = crypto.createHash('sha256').update(input).digest('base64');
hex:
var crypto = require('crypto')
const hash = crypto.createHash('sha256').update(input).digest('hex');
nodejs (8) ref
const crypto = require('crypto');
const hash = crypto.createHash('sha256');
hash.on('readable', () => {
const data = hash.read();
if (data) {
console.log(data.toString('hex'));
// Prints:
// 6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
}
});
hash.write('some data to hash');
hash.end();
you can use, like this, in here create a reset token (resetToken), this token is used to create a hex version.in database, you can store hex version.
// Generate token
const resetToken = crypto.randomBytes(20).toString('hex');
// Hash token and set to resetPasswordToken field
this.resetPasswordToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');
console.log(resetToken )
Similar to the answers above, but this shows how to do multiple writes; for example if you read line-by-line from a file and then add each line to the hash computation as a separate operation.
In my example, I also trim newlines / skip empty lines (optional):
const {createHash} = require('crypto');
// lines: array of strings
function computeSHA256(lines) {
const hash = createHash('sha256');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim(); // remove leading/trailing whitespace
if (line === '') continue; // skip empty lines
hash.write(line); // write a single line to the buffer
}
return hash.digest('base64'); // returns hash as string
}
I use this code ensure generated lines of a file aren't edited by someone manually. To do this, I write the lines out, append a line like sha256:<hash> with the sha265-sum, and then, upon next run, verify the hash of those lines matches said sha265-sum.

get hash from strings, like hashids

Using the package hashids, I can obtain hashes (with encode and decode) from numbers.
var Hashids = require("hashids"),
hashids = new Hashids("this is my salt", 8);
var id = hashids.encode(1);
Is there a similar package to obtain hashes from strings?
(with encode and decode)
var Hashids = require("hashids");
var hashids = new Hashids("this is my salt");
var hex = Buffer.from('Hello World', 'utf8').toString('hex');
console.log (hex); // '48656c6c6f20576f726c64'
var encoded = hashids.encodeHex(hex);
console.log (encoded); // 'rZ4pPgYxegCarB3eXbg'
var decodedHex = hashids.decodeHex('rZ4pPgYxegCarB3eXbg');
console.log (decodedHex); // '48656c6c6f20576f726c64'
var string = Buffer.from('48656c6c6f20576f726c64', 'hex').toString('utf8');
console.log (string); // 'Hello World'
Getting hex without Node's Buffer.from ( to use with hashids.decodeHex)
const toHex = (str: string): string => str.split("")
.reduce((hex, c) => hex += c.charCodeAt(0).toString(16).padStart(2, "0"), "")
const toUTF8 = (num: string): string =>
num.match(/.{1,2}/g)
.reduce((acc, char) => acc + String.fromCharCode(parseInt(char, 16)),"");

Resources