Skip to content

Instantly share code, notes, and snippets.

@brianloveswords
Last active December 12, 2015 09:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianloveswords/4751447 to your computer and use it in GitHub Desktop.
Save brianloveswords/4751447 to your computer and use it in GitHub Desktop.
/*
This is a proof-of-concept to show that, despite being undocumented,
node can successfully sign and verify using Elliptic Curve keys and
SHA2 hashing.
I originally thought using sha2 with ecdsa in node wasn't possible
because node only accepts algorithms defined as an openssl message
digest algorithm, and even the latest version of openssl only lists
"ecdsa-with-sha1".
This test aims to prove that it's possible to use ECDSA with SHA2 by
lying to node by specifying "RSA-SHA(bits)" as the algorithm then
passing an Elliptic Curve key.
Usage:
node ec-poc.js
Methodology:
I test interoperability by generating a signature with
`crypto.createSign` and verifying that signature with `openssl dgst -verify`.
Then I do the reverse, generating a signature with `openssl dgst -sign` and
verifying it with `crypto.createVerify`.
Personal Results:
I tested this with positive results against node v0.8.18 and
OpenSSL version 1.0.1c.
*/
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
const INPUT = fs.readFileSync(__filename);
OpenSSL_keygen(function (keys) {
OpenSSL_verify(keys, function (verifyTestPassed) {
OpenSSL_sign(keys, function (signTestPassed) {
console.log('\nresults');
console.log('\tverify test:', verifyTestPassed);
console.log('\tsign test:', signTestPassed);
cleanup(keys);
});
});
});
/**
Generate a signature with `crypto.createSign` and attempt to verify it
with `openssl dgst -verify`.
`callback` should be in the form of `function (success) { }`
`keys` should be a keys object as generated by `OpenSSL_keygen`
*/
function OpenSSL_verify(keys, callback) {
const signer = crypto.createSign('RSA-SHA256');
const signature = (signer.update(INPUT), signer.sign(keys.private.data));
const signatureFilePath = path.join(__dirname, keys.prefix+'.signature.txt');
fs.writeFileSync(signatureFilePath, Buffer(signature, 'binary'));
const verify = spawn('openssl', [
'dgst',
'-sha256',
'-verify', keys.public.file,
'-signature', signatureFilePath,
]);
verify.stdin.end(INPUT);
verify.stderr.pipe(process.stderr, {end: false});
verify.on('exit', function (code) {
if (code === 0)
return callback(true);
return callback(false);
});
}
/**
Generate a signature with `openssl dgst -sign` and attempt to verify
it with `crypto.createVerify`.
`callback` should be in the form of `function (success) { }`
`keys` should be a keys object as generated by `OpenSSL_keygen`
*/
function OpenSSL_sign(keys, callback) {
var signature = Buffer(0);
const sign = spawn('openssl', [
'dgst',
'-sha256',
'-sign', keys.private.file,
]);
sign.stdin.end(INPUT);
sign.stdout.on('data', function (buf) {
signature = Buffer.concat([signature, buf]);
});
sign.stderr.pipe(process.stderr, {end: false});
sign.on('exit', function (code) {
if (code !== 0)
throw Error('openssl error');
const verifier = crypto.createVerify('RSA-SHA256');
const valid = (
verifier.update(INPUT),
verifier.verify(keys.public.data, signature)
);
return callback(valid);
});
}
/**
Generate an EC keypair. Spawns `openssl ecparam` and `openssl ec`
`callback` should be in form of `function(keys) { ... }`
`keys` will contain three properies: `prefix`, `private` and `public`.
`prefix` is the prefix used when generating the key files.
`private` and `public` each contain two properties:
`data` is the raw key data
`file` is the path to the file on disk
*/
function OpenSSL_keygen(callback) {
const prefix = (Math.random() * 0x100000000).toString(32);
const privateKeyPath = path.join(__dirname, prefix+'.private.pem');
const publicKeyPath = path.join(__dirname, prefix+'.public.pem');
var privateKey = Buffer(0);
var publicKey = Buffer(0);
var privgen, pubgen, error;
privgen = spawn('openssl', [
'ecparam',
'-name', 'secp256k1',
'-genkey'
]);
privgen.stderr.pipe(process.stderr, {end: false});
privgen.stdout.on('data', function (buf) {
privateKey = Buffer.concat([privateKey, buf]);
});
privgen.on('exit', function (code) {
if (code !== 0)
throw new Error('openssl error')
error = '';
pubgen = spawn('openssl', [
'ec',
'-pubout'
]);
pubgen.stderr.pipe(process.stderr, {end: false});
pubgen.stdout.on('data', function (buf) {
publicKey = Buffer.concat([publicKey, buf]);
});
pubgen.stdin.end(privateKey);
pubgen.on('exit', function (code) {
if (code !== 0)
throw new Error('openssl error')
fs.writeFileSync(privateKeyPath, privateKey);
fs.writeFileSync(publicKeyPath, publicKey);
return callback({
"prefix": prefix,
"private": {
data: privateKey,
file: privateKeyPath,
},
"public": {
data: publicKey,
file: publicKeyPath
},
});
});
});
}
/**
Remove all the generated files
*/
function cleanup(keys) {
const prefix = keys.prefix;
fs.unlinkSync(path.join(__dirname, prefix+'.private.pem'));
fs.unlinkSync(path.join(__dirname, prefix+'.public.pem'));
fs.unlinkSync(path.join(__dirname, prefix+'.signature.txt'));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment