Merge pull request #506 from chjj/paypro

Payment Protocol: Improvements.
This commit is contained in:
Ryan X. Charles 2014-09-04 16:12:10 -07:00
commit 90f99fe744
2 changed files with 127 additions and 81 deletions

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
var crypto = require('crypto');
var Message = Message || require('./Message'); var Message = Message || require('./Message');
var RootCerts = require('./common/RootCerts'); var RootCerts = require('./common/RootCerts');
@ -11,18 +13,24 @@ var rfc3280 = require('asn1.js/rfc/3280');
PayPro.prototype.x509Sign = function(key, returnTrust) { PayPro.prototype.x509Sign = function(key, returnTrust) {
var self = this; var self = this;
var crypto = require('crypto');
var pki_type = this.get('pki_type'); var pki_type = this.get('pki_type');
var pki_data = this.get('pki_data'); var pki_data = this.get('pki_data');
pki_data = PayPro.X509Certificates.decode(pki_data); pki_data = PayPro.X509Certificates.decode(pki_data);
pki_data = pki_data.certificate; pki_data = pki_data.certificate;
var details = this.get('serialized_payment_details'); var details = this.get('serialized_payment_details');
var type = pki_type.split('+')[1].toUpperCase(); var type = pki_type !== 'none'
? pki_type.split('+')[1].toUpperCase()
: pki_type;
var signature = crypto.createSign('RSA-' + type); if (type !== 'none') {
var buf = this.serializeForSig(); var signature = crypto.createSign('RSA-' + type);
signature.update(buf); var buf = this.serializeForSig();
var sig = signature.sign(key); signature.update(buf);
var sig = signature.sign(key);
} else {
var buf = this.serializeForSig();
var sig = '';
}
if (returnTrust) { if (returnTrust) {
var cert = pki_data[pki_data.length - 1]; var cert = pki_data[pki_data.length - 1];
@ -49,7 +57,6 @@ PayPro.prototype.x509Sign = function(key, returnTrust) {
PayPro.prototype.x509Verify = function(returnTrust) { PayPro.prototype.x509Verify = function(returnTrust) {
var self = this; var self = this;
var crypto = require('crypto');
var pki_type = this.get('pki_type'); var pki_type = this.get('pki_type');
var sig = this.get('signature'); var sig = this.get('signature');
var pki_data = this.get('pki_data'); var pki_data = this.get('pki_data');
@ -57,15 +64,20 @@ PayPro.prototype.x509Verify = function(returnTrust) {
pki_data = pki_data.certificate; pki_data = pki_data.certificate;
var details = this.get('serialized_payment_details'); var details = this.get('serialized_payment_details');
var buf = this.serializeForSig(); var buf = this.serializeForSig();
var type = pki_type.split('+')[1].toUpperCase(); var type = pki_type !== 'none'
? pki_type.split('+')[1].toUpperCase()
: pki_type;
var verifier = crypto.createVerify('RSA-' + type); if (type !== 'none') {
verifier.update(buf); var verifier = crypto.createVerify('RSA-' + type);
verifier.update(buf);
var signedCert = pki_data[0]; var signedCert = pki_data[0];
var der = signedCert.toString('hex'); var der = signedCert.toString('hex');
var pem = PayPro.DERtoPEM(der, 'CERTIFICATE'); var pem = PayPro.DERtoPEM(der, 'CERTIFICATE');
var verified = verifier.verify(pem, sig); var verified = verifier.verify(pem, sig);
} else {
var verified = true;
}
var chain = pki_data; var chain = pki_data;
@ -107,7 +119,27 @@ PayPro.prototype.x509Verify = function(returnTrust) {
return verified; return verified;
} }
var chainVerified = chain.every(function(cert, i) { var chainVerified = PayPro.verifyCertChain(chain, type);
if (returnTrust) {
return {
selfSigned: 0, // no
isChain: true,
verified: verified,
caTrusted: !!caName,
caName: caName || null,
chainVerified: chainVerified
};
}
return verified && chainVerified;
};
PayPro.verifyCertChain = function(chain, sigHashAlg) {
if (sigHashAlg === 'none') {
return true;
}
return chain.every(function(cert, i) {
var der = cert.toString('hex'); var der = cert.toString('hex');
var pem = PayPro.DERtoPEM(der, 'CERTIFICATE'); var pem = PayPro.DERtoPEM(der, 'CERTIFICATE');
var name = RootCerts.getTrusted(pem); var name = RootCerts.getTrusted(pem);
@ -160,7 +192,7 @@ PayPro.prototype.x509Verify = function(returnTrust) {
// from the DER Certificate: // from the DER Certificate:
var tbs = PayPro.getTBSCertificate(data); var tbs = PayPro.getTBSCertificate(data);
var verifier = crypto.createVerify('RSA-' + sigAlg); var verifier = crypto.createVerify('RSA-' + sigHashAlg);
verifier.update(tbs); verifier.update(tbs);
var sigVerified = verifier.verify(npubKey, sig); var sigVerified = verifier.verify(npubKey, sig);
@ -168,19 +200,6 @@ PayPro.prototype.x509Verify = function(returnTrust) {
&& issuerVerified && issuerVerified
&& sigVerified; && sigVerified;
}); });
if (returnTrust) {
return {
selfSigned: 0, // no
isChain: true,
verified: verified,
caTrusted: !!caName,
caName: caName || null,
chainVerified: chainVerified
};
}
return verified && chainVerified;
}; };
module.exports = PayPro; module.exports = PayPro;

View File

@ -18,23 +18,29 @@ PayPro.prototype.x509Sign = function(key, returnTrust) {
var pki_data = this.get('pki_data'); // contains one or more x509 certs var pki_data = this.get('pki_data'); // contains one or more x509 certs
pki_data = PayPro.X509Certificates.decode(pki_data); pki_data = PayPro.X509Certificates.decode(pki_data);
pki_data = pki_data.certificate; pki_data = pki_data.certificate;
var type = pki_type.split('+')[1].toUpperCase(); var type = pki_type !== 'none'
? pki_type.split('+')[1].toUpperCase()
: pki_type;
var buf = this.serializeForSig(); var buf = this.serializeForSig();
var rsa = new KJUR.RSAKey(); var rsa = new KJUR.RSAKey();
rsa.readPrivateKeyFromPEMString(key.toString()); rsa.readPrivateKeyFromPEMString(key.toString());
key = rsa; key = rsa;
var jsrsaSig = new KJUR.crypto.Signature({ if (type !== 'none') {
alg: type + 'withRSA', var jsrsaSig = new KJUR.crypto.Signature({
prov: 'cryptojs/jsrsa' alg: type + 'withRSA',
}); prov: 'cryptojs/jsrsa'
});
jsrsaSig.init(key); jsrsaSig.init(key);
jsrsaSig.updateHex(buf.toString('hex')); jsrsaSig.updateHex(buf.toString('hex'));
var sig = new Buffer(jsrsaSig.sign(), 'hex'); var sig = new Buffer(jsrsaSig.sign(), 'hex');
} else {
var sig = '';
}
if (returnTrust) { if (returnTrust) {
var cert = pki_data[pki_data.length - 1]; var cert = pki_data[pki_data.length - 1];
@ -66,20 +72,25 @@ PayPro.prototype.x509Verify = function(returnTrust) {
pki_data = PayPro.X509Certificates.decode(pki_data); pki_data = PayPro.X509Certificates.decode(pki_data);
pki_data = pki_data.certificate; pki_data = pki_data.certificate;
var buf = this.serializeForSig(); var buf = this.serializeForSig();
var type = pki_type.split('+')[1].toUpperCase(); var type = pki_type !== 'none'
? pki_type.split('+')[1].toUpperCase()
: pki_type;
var jsrsaSig = new KJUR.crypto.Signature({ if (type !== 'none') {
alg: type + 'withRSA', var jsrsaSig = new KJUR.crypto.Signature({
prov: 'cryptojs/jsrsa' alg: type + 'withRSA',
}); prov: 'cryptojs/jsrsa'
});
var signedCert = pki_data[0]; var signedCert = pki_data[0];
var der = signedCert.toString('hex'); var der = signedCert.toString('hex');
// var pem = self._DERtoPEM(der, 'CERTIFICATE'); // var pem = self._DERtoPEM(der, 'CERTIFICATE');
var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
jsrsaSig.initVerifyByCertificatePEM(pem); jsrsaSig.initVerifyByCertificatePEM(pem);
jsrsaSig.updateHex(buf.toString('hex')); jsrsaSig.updateHex(buf.toString('hex'));
var verified = jsrsaSig.verify(sig.toString('hex')); var verified = jsrsaSig.verify(sig.toString('hex'));
} else {
var verified = true;
}
var chain = pki_data; var chain = pki_data;
@ -122,7 +133,27 @@ PayPro.prototype.x509Verify = function(returnTrust) {
return verified; return verified;
} }
var chainVerified = chain.every(function(cert, i) { var chainVerified = PayPro.verifyCertChain(chain, type);
if (returnTrust) {
return {
selfSigned: 0, // no
isChain: true,
verified: verified,
caTrusted: !!caName,
caName: caName || null,
chainVerified: chainVerified
};
}
return verified && chainVerified;
};
PayPro.verifyCertChain = function(chain, sigHashAlg) {
if (sigHashAlg === 'none') {
return true;
}
return chain.every(function(cert, i) {
var der = cert.toString('hex'); var der = cert.toString('hex');
// var pem = self._DERtoPEM(der, 'CERTIFICATE'); // var pem = self._DERtoPEM(der, 'CERTIFICATE');
var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
@ -146,16 +177,21 @@ PayPro.prototype.x509Verify = function(returnTrust) {
// //
var ndata = new Buffer(nder, 'hex'); var ndata = new Buffer(nder, 'hex');
var nc = rfc3280.Certificate.decode(ndata, 'der'); var nc = rfc3280.Certificate.decode(ndata, 'der');
var npubKeyAlg = PayPro.getAlgorithm(
nc.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm);
// //
// Get Public Key from next certificate (via KJUR because it's a mess): // Get Public Key from next certificate (via KJUR because it's a mess):
// //
var js = new KJUR.crypto.Signature({ if (sigHashAlg !== 'none') {
alg: type + 'withRSA', var js = new KJUR.crypto.Signature({
prov: 'cryptojs/jsrsa' alg: sigHashAlg + 'withRSA',
}); prov: 'cryptojs/jsrsa'
js.initVerifyByCertificatePEM(npem); });
var npubKey = js.pubKey; js.initVerifyByCertificatePEM(npem);
var npubKey = js.pubKey;
}
// XXX Somehow change the pubKey format to npubKeyAlg.
// //
// Get Signature Value from current certificate: // Get Signature Value from current certificate:
@ -179,37 +215,28 @@ PayPro.prototype.x509Verify = function(returnTrust) {
// Verify current Certificate signature // Verify current Certificate signature
// //
var jsrsaSig = new KJUR.crypto.Signature({ if (sigHashAlg !== 'none') {
alg: type + 'withRSA', var jsrsaSig = new KJUR.crypto.Signature({
prov: 'cryptojs/jsrsa' alg: sigHashAlg + 'withRSA',
}); prov: 'cryptojs/jsrsa'
jsrsaSig.initVerifyByPublicKey(npubKey); });
jsrsaSig.initVerifyByPublicKey(npubKey);
// Get the raw DER TBSCertificate // Get the raw DER TBSCertificate
// from the DER Certificate: // from the DER Certificate:
var tbs = PayPro.getTBSCertificate(data); var tbs = PayPro.getTBSCertificate(data);
jsrsaSig.updateHex(tbs.toString('hex')); jsrsaSig.updateHex(tbs.toString('hex'));
var sigVerified = jsrsaSig.verify(sig.toString('hex')); var sigVerified = jsrsaSig.verify(sig.toString('hex'));
} else {
var sigVerified = true;
}
return validityVerified return validityVerified
&& issuerVerified && issuerVerified
&& sigVerified; && sigVerified;
}); });
if (returnTrust) {
return {
selfSigned: 0, // no
isChain: true,
verified: verified,
caTrusted: !!caName,
caName: caName || null,
chainVerified: chainVerified
};
}
return verified && chainVerified;
}; };
module.exports = PayPro; module.exports = PayPro;