Skip to content

Instantly share code, notes, and snippets.

@hinzundcode
Created August 19, 2015 02:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hinzundcode/1450946e0823fc0d4db6 to your computer and use it in GitHub Desktop.
Save hinzundcode/1450946e0823fc0d4db6 to your computer and use it in GitHub Desktop.
nodejs tls hpkp
var tls = require("tls");
var fs = require("fs");
var crypto = require("crypto");
var options = {
host: "test.dev",
port: 8888,
ca: [fs.readFileSync("cert.pem"), fs.readFileSync("cert2.pem")],
};
// cert fingerprint 99:5B:7B:AD:28:F4:7C:72:96:6C:24:9E:6A:63:A4:B0:AF:04:E5:43
// openssl x509 -in cert.pem -outform DER | shasum
// hpkp base64 wh8TAnmj3IABejy1snFQdaqCjkFm73iArCmXKHGJGKc=
// hpkp: openssl x509 -in cert.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
function rsaPublicKeyDer(modulus_b64, exponent_b64) {
function prepadSigned(hexStr) {
msb = hexStr[0]
if (
(msb>='8' && msb<='9') ||
(msb>='a' && msb<='f') ||
(msb>='A'&&msb<='F')) {
return '00'+hexStr;
} else {
return hexStr;
}
}
function toHex(number) {
var nstr = number.toString(16)
if (nstr.length%2==0) return nstr
return '0'+nstr
}
// encode ASN.1 DER length field
// if <=127, short form
// if >=128, long form
function encodeLengthHex(n) {
if (n<=127) return toHex(n)
else {
n_hex = toHex(n)
length_of_length_byte = 128 + n_hex.length/2 // 0x80+numbytes
return toHex(length_of_length_byte)+n_hex
}
}
var modulus = new Buffer(modulus_b64,'base64');
var exponent = new Buffer(exponent_b64, 'base64');
var modulus_hex = modulus.toString('hex')
var exponent_hex = exponent.toString('hex')
modulus_hex = prepadSigned(modulus_hex)
exponent_hex = prepadSigned(exponent_hex)
var modlen = modulus_hex.length/2
var explen = exponent_hex.length/2
var encoded_modlen = encodeLengthHex(modlen)
var encoded_explen = encodeLengthHex(explen)
var encoded_pubkey = '30' +
encodeLengthHex(
modlen +
explen +
encoded_modlen.length/2 +
encoded_explen.length/2 + 2
) +
'02' + encoded_modlen + modulus_hex +
'02' + encoded_explen + exponent_hex;
var seq2 =
'30 0d ' +
'06 09 2a 86 48 86 f7 0d 01 01 01' +
'05 00 ' +
'03' + encodeLengthHex(encoded_pubkey.length/2 + 1) +
'00' + encoded_pubkey;
seq2 = seq2.replace(/ /g,'');
var der_hex = '30' + encodeLengthHex(seq2.length/2) + seq2;
der_hex = der_hex.replace(/ /g, '');
return new Buffer(der_hex, 'hex');
}
function rsaPublicKeyPem(modulus_b64, exponent_b64) {
var der = rsaPublicKeyDer(modulus_b64, exponent_b64);
var der_b64 = der.toString('base64');
var pem = '-----BEGIN PUBLIC KEY-----\n'
+ der_b64.match(/.{1,64}/g).join('\n')
+ '\n-----END PUBLIC KEY-----\n';
return pem
}
function generateHpbkPin(publicKeyDer) {
return crypto.createHash("sha256").update(publicKeyDer).digest("base64");
}
function validateHpbkPin(peerCertificate, pin) {
var publicKey = rsaPublicKeyDer(
new Buffer(peerCertificate.modulus, "hex").toString("base64"),
new Buffer((peerCertificate.exponent.length % 2 ? "0" : "")+peerCertificate.exponent, "hex").toString("base64")
);
var hash = generateHpbkPin(publicKey);
return hash === pin;
}
var socket = tls.connect(options, function (err) {
console.log("connected");
console.log(socket.getPeerCertificate());
console.log(socket.getPeerCertificate().modulus);
console.log(socket.getPeerCertificate().exponent);
console.log(validateHpbkPin(socket.getPeerCertificate(), 'wh8TAnmj3IABejy1snFQdaqCjkFm73iArCmXKHGJGKc='));
process.stdin.pipe(socket);
process.stdin.resume();
});
socket.setEncoding("utf8");
socket.on("data", function (data) {
console.log(data);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment