Last active
March 30, 2023 21:08
-
-
Save DinoChiesa/36ff5491a3d9ceda55665bead3819918 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// createKeyPair.js | |
// ------------------------------------------------------------------ | |
// | |
// created: Thu Feb 28 15:49:15 2019 | |
// last saved: <2023-March-29 14:19:28> | |
/* jshint esversion:9, node: true */ | |
/* global process, console, Buffer, require */ | |
const crypto = require('crypto'); | |
crypto.generateKeyPair('rsa', { // requires node@10 | |
modulusLength: 2048, //4096 | |
publicKeyEncoding: { | |
type: 'spki', | |
format: 'pem' | |
}, | |
privateKeyEncoding: { | |
type: 'pkcs8', | |
format: 'pem' | |
//cipher: 'aes-256-cbc', | |
//passphrase: 'top secret' | |
} | |
}, (e, publicKey, privateKey) => { | |
// Handle errors and use the generated key pair. | |
console.log('\n\npublic:\n' + publicKey); | |
console.log('\n\nprivate:\n' + privateKey); | |
let fs = require('fs'); | |
fs.writeFileSync('public-rsa.pem', publicKey, {encoding:'utf8'}); | |
fs.writeFileSync('private-rsa.pem', privateKey, {encoding:'utf8'}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// sign.js | |
// ------------------------------------------------------------------ | |
// | |
// It seems that https://www.npmjs.com/package/http-signature is out of date with the current | |
// HTTP Signature specification (https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures). | |
// | |
// This example shows how to compute an RSA-signed signature for hs2019, wuth created/expires and digest. | |
// | |
// Run the accompanying createKeyPair.js script to get a keypair, if you need one. | |
// | |
// created: Wed Mar 29 13:14:19 2023 | |
// last saved: <2023-March-29 14:15:26> | |
/* jshint esversion:9, node:true, strict:implied */ | |
/* global process, console, Buffer, URL, require */ | |
const crypto = require('crypto'); | |
const SIG_LIFETIME = 90; // in seconds | |
function toBase64Url(s) { | |
return s.replace(/=/g, "") | |
.replace(/\+/g, "-") | |
.replace(/\//g, "_"); | |
} | |
function getRsaPrivateKey() { | |
return getRsaKey('./private-rsa.pem'); | |
} | |
function getRsaPublicKey() { | |
return getRsaKey('./public-rsa.pem'); | |
} | |
function getRsaKey(filepath) { | |
let fs = require('fs'); | |
let privatePem = fs.readFileSync(filepath); | |
let key = privatePem.toString('utf-8'); | |
return key; | |
} | |
/* | |
* signs according to https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures | |
* | |
* method: get, post, etc | |
* url: a string specifying the target URL. | |
* inboundHeaders: a hash of all headers to send. | |
* namesOfHeadersToSign: an array of header names to include in signature. This can include the | |
* so-called "special" header name of "(request-target)", as well as "created" and "expires". | |
* An example of a value for this parameter might be: | |
* [ "(request-target)", "host", "date", "digest", "created", "expires" ] | |
* keyInfo: hash of { key, keyId} | |
**/ | |
function sign(method, url, inboundHeaders, namesOfHeadersToSign, keyInfo) { | |
let now = Math.floor((new Date()).valueOf() / 1000); // secconds since epoch | |
let signatureBase = | |
namesOfHeadersToSign.map(name => name .toLowerCase()) | |
.map( name => { | |
if (name == '(request-target)') { | |
let parsedUrl = new URL(url); | |
return "(request-target): " + method.toLowerCase() + ' ' + parsedUrl.path + parsedUrl.search; | |
} | |
if (name == 'created') { return 'created: ' + now; } | |
if (name == 'expires') { return 'expires: ' + (now + SIG_LIFETIME); } | |
return `${name}: ${inboundHeaders[name]}`; | |
}) | |
.join('\n'); | |
// example signatureBase: | |
// (request-target): get /foo | |
// host: example.org | |
// date: Tue, 07 Jun 2014 20:51:35 GMT | |
let signer = crypto.createSign('sha256'); | |
signer.update(signatureBase); | |
let computedSignature = toBase64Url(signer.sign(keyInfo.key, 'base64')); | |
let sigHeaderItems = [ | |
`keyId="${keyInfo.keyId}"`, | |
`headers="${namesOfHeadersToSign.join(' ')}"` | |
]; | |
if (namesOfHeadersToSign.indexOf('created') >= 0) { | |
sigHeaderItems.push(`created=${now}`); | |
} | |
if (namesOfHeadersToSign.indexOf('expires') >= 0) { | |
sigHeaderItems.push(`expires=${(now+SIG_LIFETIME)}`); | |
} | |
sigHeaderItems.push(`signature="${computedSignature}"`); | |
let updatedHeaders = {...inboundHeaders, signature: sigHeaderItems.join(', ')}; | |
return updatedHeaders; | |
} | |
// ==================================================================== | |
console.log('\nGET request, signing date and host.'); | |
let method1 = 'GET'; | |
let url = "https://mastodon.example.net/users/username/inbox"; | |
let preparedHeaders = { | |
date: (new Date()).toUTCString(), | |
host: (new URL(url)).host }; | |
let keyInfo = { | |
key: getRsaPrivateKey(), | |
keyId: 'https://my-key-site.com/abcdefg' | |
}; | |
let resultHeaders = | |
sign(method1, url, preparedHeaders, | |
['(request-target)', 'date', 'host', 'digest'], | |
keyInfo | |
); | |
console.log(JSON.stringify(resultHeaders, null, 2)); | |
// ==================================================================== | |
console.log('\nPOST with a payload, signing date, host, and digest. as well as created/expires.'); | |
let method2 = 'POST'; | |
let payloadJson = { | |
request: "hello" | |
}; | |
let payloadTxt = JSON.stringify(payloadJson, null, 2); | |
let hash = crypto.createHash('sha256'); | |
hash.update(payloadTxt); | |
preparedHeaders.digest = 'SHA-256=' + hash.digest('base64'); | |
resultHeaders = | |
sign(method2, url, preparedHeaders, | |
['(request-target)', 'date', 'host', 'digest', 'created', 'expires'], | |
keyInfo | |
); | |
console.log(JSON.stringify(resultHeaders, null, 2)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment