Skip to content

Instantly share code, notes, and snippets.

@abramovholvi
Last active September 19, 2022 18:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abramovholvi/ce515f73853e03aa5a59650f66ed57b9 to your computer and use it in GitHub Desktop.
Save abramovholvi/ce515f73853e03aa5a59650f66ed57b9 to your computer and use it in GitHub Desktop.
Example Code for Postman to generate HTTP Signature
// Borrowed from https://developer.nordeaopenbanking.com/app/documentation?api=Business%20Accounts%20API&version=1.0
// With some small adjustments to header names
// Load Forge library
// !!!!!!!!!!!!!! Unsafe: it is better to download and compile forge lib
// from https://github.com/digitalbazaar/forge and put it to service managed
// by you instead of using
// https://raw.githubusercontent.com/loveiset/RSAForPostman/master/forge.js
// or just copy whole script to Postman Pre-Script section
if (!pm.globals.has("forgeJS")) {
pm.sendRequest("https://raw.githubusercontent.com/loveiset/RSAForPostman/master/forge.js", (err, res) => {
if(err) {
console.log(err);
} else {
pm.globals.set("forgeJS", res.text());
}
});
}
eval(postman.getGlobalVariable("forgeJS"));
// Common
function getHeaderValue(headerName) {
const headerValue = request.headers[headerName];
if (headerValue === undefined) {
throw new Error(`Requried header: ${headerName} is not defined`);
}
return resolveVariables(headerValue);
}
function resolveVariables(textWithPossibleVaraibles) {
return textWithPossibleVaraibles.replace (/{{(\w*)}}/,g, (str, key) => {
const value = environment[key];
return value === null ? "" : value;
});
}
// Digest Calculation
function resolveRequestBody() {
const contentType = getHeaderValue("content-type");
if (contentType === "application/x-www-form-urlencoded") {
const data = Object.keys(request.data)
.sort((a, b) => {
if(a < b) { return -1; }
if(a > b) { return 1; }
return 0;
})
.map(key => key + "=" + request.data[key])
.join('&');
return resolveVariables(data);
} else if (Object.entries(request.data).length === 0 && request.data.constructor === Object) {
return "";
}
return resolveVariables(request.data.toString());
}
function calculateDigest() {
const requestData = resolveRequestBody();
console.log(`Request data: ${requestData}`);
const sha256digest = CryptoJS.SHA256(requestData);
const base64sha256 = CryptoJS.enc.Base64.stringify(sha256digest);
const calculatedDigest = 'sha-256=' + base64sha256;
console.log(`Digest header: ${calculatedDigest}`);
pm.environment.set("Digest", calculatedDigest);
return calculatedDigest;
}
// Signature Calculation
const sdk = require("postman-collection");
const moment = require("moment")
const requestWithoutContentHeaders = "(request-target) host date";
const requestWithContentHeaders = "(request-target) host date content-type digest";
function getSignatureBaseOnRequest() {
const url = new sdk.Url(resolveVariables(request.url));
const host = url.getHost().toLowerCase();
const path = url.getPathWithQuery();
const method = request.method.toLowerCase();
const date = moment().utc().format("ddd, DD MMM YYYY HH:mm:ss") + " GMT";
let headers = requestWithoutContentHeaders;
let normalizedString =
`(request-target): ${method} ${path}\n` +
`host: ${host}\n` +
`date: ${date}`;
if (method === "post" || method === "put" || method === "patch") {
const contentType = getHeaderValue("content-type");
const digest = calculateDigest();
normalizedString += `\ncontent-type: ${contentType}\ndigest: ${digest}`
headers = requestWithContentHeaders;
}
return {host, path, method, date, headers, normalizedString};
}
function encryptSignature(normalizedSignatureString) {
const messageDigest = forge.md.sha256.create();
messageDigest.update(normalizedSignatureString, "utf8");
return forge.util.encode64(getPrivateKey().sign(messageDigest));
}
function getPrivateKey() {
let eidasPrivateKey = pm.environment.get("eidasPrivateKey");
if (!eidasPrivateKey.includes('PRIVATE KEY')) {
eidasPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + eidasPrivateKey + "\n" + "-----END RSA PRIVATE KEY-----";
}
console.log(eidasPrivateKey);
return forge.pki.privateKeyFromPem(eidasPrivateKey);
}
const clientId = getHeaderValue("x-holvi-client-id")
const signature = getSignatureBaseOnRequest();
const encryptedSignature = encryptSignature(signature.normalizedString);
const signatureHeader = `keyId="${clientId}",algorithm="rsa-sha256",headers="${signature.headers}",signature="${encryptedSignature}"`;
console.log(`Normalized signature string: ${signature.normalizedString}`);
console.log(`Signature header: ${signatureHeader}`);
pm.environment.set("Signature", signatureHeader);
pm.environment.set("Host", signature.host);
pm.environment.set(Date", signature.date);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment