bip70: x509 improvements.
This commit is contained in:
parent
54cf5ce341
commit
5dbebe14d9
@ -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');
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user