From 569e60065aa15e7b8c20a85c29393d1fb3fea18c Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 21 Aug 2014 16:13:34 -0700 Subject: [PATCH] paypro: verify the certificate chain. --- lib/PayPro.js | 72 ++++++++++++++++++++++++++++++++++----- lib/browser/PayPro.js | 78 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 130 insertions(+), 20 deletions(-) diff --git a/lib/PayPro.js b/lib/PayPro.js index 0d9abc5..61d4893 100644 --- a/lib/PayPro.js +++ b/lib/PayPro.js @@ -6,6 +6,8 @@ var RootCerts = require('./common/RootCerts'); var PayPro = require('./common/PayPro'); +var KJUR = require('jsrsasign'); + PayPro.prototype.x509Sign = function(key) { var self = this; var crypto = require('crypto'); @@ -53,20 +55,74 @@ PayPro.prototype.x509Verify = function() { var verifier = crypto.createVerify('RSA-' + type); verifier.update(buf); - return pki_data.every(function(cert) { + var signedCert = pki_data[0]; + var der = signedCert.toString('hex'); + var pem = this._DERtoPEM(der, 'CERTIFICATE'); + var verified = verifier.verify(pem, sig); + + var chain = pki_data; + + // Verifying the cert chain: + // 1. Extract public key from next certificate. + // 2. Extract signature from current certificate. + // 3. If current cert is not trusted, verify that the current cert is signed + // by NEXT by the certificate. + // 4. XXX What to do when the certificate is revoked? + + var blen = +type.replace(/[^\d]+/g, ''); + if (blen === 1) blen = 20; + if (blen === 256) blen = 32; + + chain.forEach(function(cert, i) { var der = cert.toString('hex'); var pem = self._DERtoPEM(der, 'CERTIFICATE'); - var name = RootCerts.getTrusted(pem); - // XXX Figure out what to do here - if (!name) { - // throw new Error('Unstrusted certificate.'); - } else { - // console.log('Certificate: %s', name); + + var ncert = chain[i + 1]; + // The root cert, check if it's trusted: + if (!ncert || name) { + if (!name) { + // console.log('Untrusted certificate.'); + } else { + // console.log('Certificate: %s', name); + } + return; } + var nder = ncert.toString('hex'); + var npem = self._DERtoPEM(nder, 'CERTIFICATE'); - return verifier.verify(pem, sig); + // get sig from current cert - BAD + var sig = new Buffer(der.slice(-(blen * 2)), 'hex'); + + // Should work but doesn't: + // get sig from current cert + // var o = new KJUR.asn1.cms.SignerInfo(); + // o.setSignerIdentifier(pem); + // var sig = o.getEncodedHex(); + + // get public key from next cert + var js = new KJUR.crypto.Signature({ + alg: type + 'withRSA', + prov: 'cryptojs/jsrsa' + }); + js.initVerifyByCertificatePEM(npem); + var npubKey = KJUR.KEYUTIL.getPEM(js.pubKey); + + var verifier = crypto.createVerify('RSA-' + type); + + // NOTE: We need to slice off the signatureAlgorithm and signatureValue. + // consult the x509 spec: + // https://www.ietf.org/rfc/rfc2459 + verifier.update(new Buffer(der, 'hex')); + + var v = verifier.verify(npubKey, sig); + if (!v) { + // console.log(i + ' not verified.'); + verified = false; + } }); + + return verified; }; module.exports = PayPro; diff --git a/lib/browser/PayPro.js b/lib/browser/PayPro.js index b9ab061..9269308 100644 --- a/lib/browser/PayPro.js +++ b/lib/browser/PayPro.js @@ -64,24 +64,78 @@ PayPro.prototype.x509Verify = function(key) { prov: 'cryptojs/jsrsa' }); - return pki_data.every(function(cert) { + var signedCert = pki_data[0]; + var der = signedCert.toString('hex'); + var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); + jsrsaSig.initVerifyByCertificatePEM(pem); + jsrsaSig.updateHex(buf.toString('hex')); + var verified = jsrsaSig.verify(sig.toString('hex')); + + var chain = pki_data; + + // Verifying the cert chain: + // 1. Extract public key from next certificate. + // 2. Extract signature from current certificate. + // 3. If current cert is not trusted, verify that the current cert is signed + // by NEXT by the certificate. + // 4. XXX What to do when the certificate is revoked? + + var blen = +type.replace(/[^\d]+/g, ''); + if (blen === 1) blen = 20; + if (blen === 256) blen = 32; + + chain.forEach(function(cert, i) { var der = cert.toString('hex'); var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); - - // XXX Figure out what to do here var name = RootCerts.getTrusted(pem); - if (!name) { - // throw new Error('Unstrusted certificate.'); - } else { - // console.log('Certificate: %s', name); - } - - jsrsaSig.initVerifyByCertificatePEM(pem); - jsrsaSig.updateHex(buf.toString('hex')); + var ncert = chain[i + 1]; + // The root cert, check if it's trusted: + if (!ncert || name) { + if (!name) { + // console.log('Untrusted certificate.'); + } else { + // console.log('Certificate: %s', name); + } + return; + } + var nder = ncert.toString('hex'); + var npem = KJUR.asn1.ASN1Util.getPEMStringFromHex(nder, 'CERTIFICATE'); + + // get sig from current cert - BAD + var sig = der.slice(-(blen * 2)); + + // Should work but doesn't: + // get sig from current cert + // var o = new KJUR.asn1.cms.SignerInfo(); + // o.setSignerIdentifier(pem); + // var sig = new Buffer(o.getEncodedHex(), 'hex'); + + // get public key from next cert + var js = new KJUR.crypto.Signature({ + alg: type + 'withRSA', + prov: 'cryptojs/jsrsa' + }); + js.initVerifyByCertificatePEM(npem); + var npubKey = KJUR.KEYUTIL.getPEM(js.pubKey); - return jsrsaSig.verify(sig.toString('hex')); + var jsrsaSig = new KJUR.crypto.Signature({ + alg: type + 'withRSA', + prov: 'cryptojs/jsrsa' + }); + jsrsaSig.initVerifyByPublicKey(npubKey); + // NOTE: We need to slice off the signatureAlgorithm and signatureValue - + // consult the x509 spec: + // https://www.ietf.org/rfc/rfc2459 + jsrsaSig.updateHex(der); + var v = jsrsaSig.verify(sig); + if (!v) { + // console.log(i + ' not verified.'); + verified = false; + } }); + + return verified; }; module.exports = PayPro;