bip70: x509 improvements.

This commit is contained in:
Christopher Jeffrey 2016-07-22 23:44:15 -07:00
parent 54cf5ce341
commit 5dbebe14d9
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 127 additions and 53 deletions

View File

@ -418,10 +418,13 @@ asn1.fromPEM = function fromPEM(pem) {
}; };
}; };
asn1.toPEM = function toPEM(der, type) { asn1.toPEM = function toPEM(der, type, suffix) {
var pem = ''; var pem = '';
var i; var i;
if (suffix)
type += ' ' + suffix;
type = type.toUpperCase(); type = type.toUpperCase();
der = der.toString('base64'); der = der.toString('base64');

View File

@ -218,15 +218,15 @@ PaymentRequest.prototype.verify = function verify() {
return x509.verifySubject(alg.hash, msg, sig, chain); return x509.verifySubject(alg.hash, msg, sig, chain);
}; };
PaymentRequest.prototype.verifyChain = function verifyChain(ignoreTime) { PaymentRequest.prototype.verifyChain = function verifyChain() {
if (!this.pkiType || this.pkiType === 'none') if (!this.pkiType || this.pkiType === 'none')
return true; return true;
return x509.verifyChain(this.getChain(), ignoreTime); return x509.verifyChain(this.getChain());
}; };
PaymentRequest.prototype.getCA = function getCA() { PaymentRequest.prototype.getCA = function getCA() {
var chain; var chain, root, ca;
if (!this.pkiType || this.pkiType === 'none') if (!this.pkiType || this.pkiType === 'none')
return; return;
@ -236,7 +236,21 @@ PaymentRequest.prototype.getCA = function getCA() {
if (chain.length === 0) if (chain.length === 0)
return; return;
return x509.getTrusted(chain[chain.length - 1]); root = x509.parse(chain[chain.length - 1]);
if (!root)
return;
ca = x509.getTrusted(root);
if (!ca)
return;
return {
name: ca.name,
fingerprint: ca.fingerprint,
cert: root
};
}; };
function PaymentDetails(options) { function PaymentDetails(options) {

View File

@ -16,19 +16,41 @@ x509.certs = [];
x509.trusted = {}; x509.trusted = {};
x509.getTrusted = function getTrusted(cert) { x509.getTrusted = function getTrusted(cert) {
var hash; var fingerprint = utils.sha256(cert.raw);
var hash = fingerprint.toString('hex');
if (!Buffer.isBuffer(cert))
cert = cert.raw;
hash = utils.hash256(cert).toString('hex');
return x509.trusted[hash]; return x509.trusted[hash];
}; };
x509.getSubjectOID = function getSubjectOID(cert, oid) {
var subject = cert.tbs.subject;
var i, entry;
for (i = 0; i < subject.length; i++) {
entry = subject[i];
if (entry.type === oid)
return entry.value;
}
};
x509.getCAName = function getCAName(cert) {
// This seems to work the best in practice
// for getting a human-readable and
// descriptive name for the CA.
// See:
// http://oid-info.com/get/2.5.4
// Precedence:
// (3) commonName
// (11) organizationUnitName
// (10) organizationName
return x509.getSubjectOID(cert, '2.5.4.3')
|| x509.getSubjectOID(cert, '2.5.4.11')
|| x509.getSubjectOID(cert, '2.5.4.10')
|| 'Unknown';
};
x509.setTrust = function setTrust(certs) { x509.setTrust = function setTrust(certs) {
var keys = Object.keys(certs); var keys = Object.keys(certs);
var i, key, cert, hash, pem; var i, key, cert, pem, fingerprint, hash, trust;
for (i = 0; i < keys.length; i++) { for (i = 0; i < keys.length; i++) {
key = keys[i]; key = keys[i];
@ -42,16 +64,19 @@ x509.setTrust = function setTrust(certs) {
assert(Buffer.isBuffer(cert), 'Certificates must be PEM or DER.'); assert(Buffer.isBuffer(cert), 'Certificates must be PEM or DER.');
hash = utils.hash256(cert).toString('hex'); cert = x509.parse(cert);
assert(cert, 'Could not parse certificate.');
cert = { fingerprint = utils.sha256(cert.raw);
name: key, hash = fingerprint.toString('hex');
fingerprint: hash,
cert: asn1.parseCert(cert) trust = {
name: x509.getCAName(cert),
fingerprint: fingerprint
}; };
x509.certs.push(cert); x509.certs.push(trust);
x509.trusted[hash] = cert; x509.trusted[hash] = trust;
} }
}; };
@ -84,6 +109,12 @@ x509.oid = {
'1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' } '1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' }
}; };
x509.pemTag = {
dsa: 'DSA',
rsa: 'RSA',
ecdsa: 'EC'
};
x509.getKeyAlgorithm = function getKeyAlgorithm(cert) { x509.getKeyAlgorithm = function getKeyAlgorithm(cert) {
var alg = cert.tbs.pubkey.alg.alg; var alg = cert.tbs.pubkey.alg.alg;
return x509.oid[alg]; return x509.oid[alg];
@ -104,18 +135,21 @@ x509.parse = function parse(der) {
x509.getPublicKey = function getPublicKey(cert) { x509.getPublicKey = function getPublicKey(cert) {
var alg = x509.getKeyAlgorithm(cert); var alg = x509.getKeyAlgorithm(cert);
var key, params, pem; var key, params, pem, tag;
if (!alg) if (!alg)
return; return;
key = cert.tbs.pubkey.pubkey; key = cert.tbs.pubkey.pubkey;
params = cert.tbs.pubkey.alg.params; params = cert.tbs.pubkey.alg.params;
tag = x509.pemTag[alg.key];
pem = asn1.toPEM(key, alg.key + ' PUBLIC KEY'); pem = asn1.toPEM(key, tag, 'public key');
// Key parameters, usually present
// if selecting an EC curve.
if (params) if (params)
pem += asn1.toPEM(params, alg.key + ' PARAMETERS'); pem += asn1.toPEM(params, tag, 'parameters');
return pem; return pem;
}; };
@ -127,7 +161,7 @@ x509.verifyTime = function verifyTime(cert) {
}; };
x509.signSubject = function signSubject(hash, msg, key, chain) { x509.signSubject = function signSubject(hash, msg, key, chain) {
var cert, alg; var cert, alg, tag;
assert(chain.length !== 0, 'No chain available.'); assert(chain.length !== 0, 'No chain available.');
@ -137,8 +171,10 @@ x509.signSubject = function signSubject(hash, msg, key, chain) {
alg = x509.getKeyAlgorithm(cert); alg = x509.getKeyAlgorithm(cert);
assert(alg, 'Certificate uses an unknown algorithm.'); assert(alg, 'Certificate uses an unknown algorithm.');
if (Buffer.isBuffer(key)) if (Buffer.isBuffer(key)) {
key = asn1.toPEM(key, alg.key + ' PRIVATE KEY'); tag = x509.pemTag[alg.key];
key = asn1.toPEM(key, tag, 'private key');
}
return x509.sign(alg.key, hash, msg, key); return x509.sign(alg.key, hash, msg, key);
}; };
@ -167,62 +203,83 @@ x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
return x509.verify(alg.key, hash, msg, sig, key); return x509.verify(alg.key, hash, msg, sig, key);
}; };
x509.verifyChain = function verifyChain(chain, ignoreTime) { x509.verifyChain = function verifyChain(chain) {
var i, child, parent, alg, key, sig, msg; var i, child, parent, alg, key, sig, msg;
if (chain.length < 2) chain = chain.slice();
return false;
for (i = 1; i < chain.length; i++) { // Parse certificates and
child = chain[i - 1]; // check validity time.
parent = chain[i]; for (i = 0; i < chain.length; i++) {
child = x509.parse(chain[i]);
child = x509.parse(child);
if (!child) if (!child)
return false; return false;
parent = x509.parse(parent); chain[i] = child;
if (!parent) if (!x509.verifyTime(child))
return false; return false;
}
if (!ignoreTime) { // Verify signatures.
if (!x509.verifyTime(child)) for (i = 1; i < chain.length; i++) {
return false; child = chain[i - 1];
parent = chain[i];
if (!x509.verifyTime(parent))
return false;
}
alg = x509.getSigAlgorithm(child); alg = x509.getSigAlgorithm(child);
msg = child.tbs.raw;
sig = child.sig;
key = x509.getPublicKey(parent);
if (!alg || !alg.hash) if (!alg || !alg.hash)
return false; return false;
key = x509.getPublicKey(parent);
if (!key) if (!key)
return false; return false;
sig = child.sig;
msg = child.tbs.raw;
if (!x509.verify(alg.key, alg.hash, msg, sig, key)) if (!x509.verify(alg.key, alg.hash, msg, sig, key))
return false; return false;
if (x509.getTrusted(parent))
return true;
} }
// If trust hasn't been
// setup, just return.
if (x509.certs.length === 0) if (x509.certs.length === 0)
return true; return true;
// Make sure we trust one
// of the certs in the chain.
for (i = 0; i < chain.length; i++) {
child = chain[i];
// If any certificate in the chain
// is trusted, assume we also trust
// the parent.
if (x509.getTrusted(child))
return true;
}
// No trusted certs present.
return false; return false;
}; };
x509.normalizeAlg = function normalizeAlg(alg, hash) {
var name = alg.toUpperCase() + '-' + hash.toUpperCase();
switch (name) {
case 'ECDSA-SHA1':
name = 'ecdsa-with-SHA1';
break;
case 'ECDSA-SHA256':
name = 'ecdsa-with-SHA256';
break;
}
return name;
};
x509.verify = function verify(alg, hash, msg, sig, key) { x509.verify = function verify(alg, hash, msg, sig, key) {
var algo = alg.toUpperCase() + '-' + hash.toUpperCase(); var algo = x509.normalizeAlg(alg, hash);
var verify; var verify;
try { try {
@ -235,7 +292,7 @@ x509.verify = function verify(alg, hash, msg, sig, key) {
}; };
x509.sign = function sign(alg, hash, msg, key) { x509.sign = function sign(alg, hash, msg, key) {
var algo = alg.toUpperCase() + '-' + hash.toUpperCase(); var algo = x509.normalizeAlg(alg, hash);
var sig = crypto.createSign(algo); var sig = crypto.createSign(algo);
sig.update(msg); sig.update(msg);
return sig.sign(key); return sig.sign(key);