paypro: major refactor. create rfc5280 extension parsing function.
This commit is contained in:
parent
799388db8e
commit
0bdc8f1586
410
lib/PayPro.js
410
lib/PayPro.js
|
@ -145,270 +145,7 @@ PayPro.prototype.x509Verify = function() {
|
||||||
// Handle Cert Extensions
|
// Handle Cert Extensions
|
||||||
// http://tools.ietf.org/html/rfc5280#section-4.2
|
// http://tools.ietf.org/html/rfc5280#section-4.2
|
||||||
//
|
//
|
||||||
var ext;
|
var extensions = rfc5280.parseExtensions(c.tbsCertificate, { partial: false });
|
||||||
var eid;
|
|
||||||
var extensions = {
|
|
||||||
authorityKeyIdentifier: null,
|
|
||||||
subjectKeyIdentifier: null,
|
|
||||||
keyUsage: null,
|
|
||||||
certificatePolicies: null,
|
|
||||||
policyMappings: null,
|
|
||||||
subjectAlternativeName: null,
|
|
||||||
issuerAlternativeName: null,
|
|
||||||
subjectDirectoryAttributes: null,
|
|
||||||
basicConstraints: null,
|
|
||||||
nameConstraints: null,
|
|
||||||
policyConstraints: null,
|
|
||||||
extendedKeyUsage: null,
|
|
||||||
CRLDistributionPoints: null,
|
|
||||||
inhibitAnyPolicy: null,
|
|
||||||
freshestCRL: null,
|
|
||||||
authorityInformationAccess: null,
|
|
||||||
subjectInformationAccess: null,
|
|
||||||
standardUnknown: [],
|
|
||||||
unknown: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < c.tbsCertificate.extensions.length; i++) {
|
|
||||||
ext = c.tbsCertificate.extensions[i];
|
|
||||||
eid = ext.extnID;
|
|
||||||
|
|
||||||
// id-ce extensions - Standard Extensions
|
|
||||||
if (eid.length === 4 && eid[0] === 2 && eid[1] === 5 && eid[2] === 29) {
|
|
||||||
switch (eid[3]) {
|
|
||||||
// Authority Key Identifier
|
|
||||||
case 35:
|
|
||||||
print('Authority Key Identifier:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.authorityKeyIdentifier = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.authorityKeyIdentifier = rfc5280.AuthorityKeyIdentifier.decode(
|
|
||||||
extensions.authorityKeyIdentifier,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.authorityKeyIdentifier);
|
|
||||||
break;
|
|
||||||
// Subject Key Identifier
|
|
||||||
case 14: // VERY IMPORTANT, especially is cA (basic constraints) is true (it is)
|
|
||||||
print('Subject Key Identifier:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.subjectKeyIdentifier = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.subjectKeyIdentifier = rfc5280.SubjectKeyIdentifier.decode(
|
|
||||||
extensions.subjectKeyIdentifier,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.subjectKeyIdentifier);
|
|
||||||
break;
|
|
||||||
// Key Usage
|
|
||||||
case 15:
|
|
||||||
print('Key Usage:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.keyUsage = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
data = rfc5280.KeyUsage.decode(
|
|
||||||
extensions.keyUsage,
|
|
||||||
'der').data[0];
|
|
||||||
extensions.keyUsage = {
|
|
||||||
digitalSignature: !!((data >> 0) & 1),
|
|
||||||
nonRepudiation: !!((data >> 1) & 1),
|
|
||||||
// nonRepudiation renamed to contentCommitment:
|
|
||||||
contentCommitment: !!((data >> 1) & 1),
|
|
||||||
keyEncipherment: !!((data >> 2) & 1),
|
|
||||||
dataEncipherment: !!((data >> 3) & 1),
|
|
||||||
keyAgreement: !!((data >> 4) & 1),
|
|
||||||
keyCertSign: !!((data >> 5) & 1),
|
|
||||||
cRLSign: !!((data >> 6) & 1),
|
|
||||||
encipherOnly: !!((data >> 7) & 1),
|
|
||||||
decipherOnly: !!((data >> 8) & 1)
|
|
||||||
};
|
|
||||||
print(extensions.keyUsage);
|
|
||||||
break;
|
|
||||||
// Certificate Policies
|
|
||||||
case 32:
|
|
||||||
print('Certificate Policies:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.certificatePolicies = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.certificatePolicies = rfc5280.CertificatePolicies.decode(
|
|
||||||
extensions.certificatePolicies,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.certificatePolicies);
|
|
||||||
break;
|
|
||||||
// Policy Mappings
|
|
||||||
case 33:
|
|
||||||
print('Policy Mappings:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.policyMappings = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.policyMappings = rfc5280.PolicyMappings.decode(
|
|
||||||
extensions.policyMappings,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.policyMappings);
|
|
||||||
break;
|
|
||||||
// Subject Alternative Name
|
|
||||||
case 17:
|
|
||||||
print('Subject Alternative Name:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.subjectAlternativeName = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.subjectAlternativeName = rfc5280.SubjectAlternativeName.decode(
|
|
||||||
extensions.subjectAlternativeName,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.subjectAlternativeName);
|
|
||||||
break;
|
|
||||||
// Issuer Alternative Name
|
|
||||||
case 18:
|
|
||||||
print('Issuer Alternative Name:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.issuerAlternativeName = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.issuerAlternativeName = rfc5280.IssuerAlternativeName.decode(
|
|
||||||
extensions.issuerAlternativeName,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.issuerAlternativeName);
|
|
||||||
break;
|
|
||||||
// Subject Directory Attributes
|
|
||||||
case 9:
|
|
||||||
print('Subject Directory Attributes:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.subjectDirectoryAttributes = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.subjectDirectoryAttributes = rfc5280.SubjectDirectoryAttributes.decode(
|
|
||||||
extensions.subjectDirectoryAttributes,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.subjectDirectoryAttributes);
|
|
||||||
break;
|
|
||||||
// Basic Constraints
|
|
||||||
case 19:
|
|
||||||
print('Basic Constraints:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.basicConstraints = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.basicConstraints = rfc5280.BasicConstraints.decode(
|
|
||||||
extensions.basicConstraints,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.basicConstraints);
|
|
||||||
break;
|
|
||||||
// Name Constraints
|
|
||||||
case 30:
|
|
||||||
print('Name Constraints:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.nameConstraints = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.nameConstraints = rfc5280.NameConstraints.decode(
|
|
||||||
extensions.nameConstraints,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.nameConstraints);
|
|
||||||
break;
|
|
||||||
// Policy Constraints
|
|
||||||
case 36:
|
|
||||||
print('Policy Constraints:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.policyConstraints = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.policyConstraints = rfc5280.PolicyConstraints.decode(
|
|
||||||
extensions.policyConstraints,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.policyConstraints);
|
|
||||||
break;
|
|
||||||
// Extended Key Usage
|
|
||||||
case 37:
|
|
||||||
print('Extended Key Usage');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.extendedKeyUsage = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.extendedKeyUsage = rfc5280.ExtendedKeyUsage.decode(
|
|
||||||
extensions.extendedKeyUsage,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.extendedKeyUsage);
|
|
||||||
break;
|
|
||||||
// CRL Distribution Points
|
|
||||||
case 31:
|
|
||||||
print('CRL Distribution Points:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.CRLDistributionPoints = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.CRLDistributionPoints = rfc5280.CRLDistributionPoints.decode(
|
|
||||||
extensions.CRLDistributionPoints,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.CRLDistributionPoints);
|
|
||||||
break;
|
|
||||||
// Inhibit anyPolicy
|
|
||||||
case 54:
|
|
||||||
print('Inhibit Any Policy:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.inhibitAnyPolicy = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.inhibitAnyPolicy = rfc5280.InhibitAnyPolicy.decode(
|
|
||||||
extensions.inhibitAnyPolicy,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.inhibitAnyPolicy);
|
|
||||||
break;
|
|
||||||
// Freshest CRL
|
|
||||||
case 46:
|
|
||||||
print('Freshest CRL:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.freshestCRL = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.freshestCRL = rfc5280.FreshestCRL.decode(
|
|
||||||
extensions.freshestCRL,
|
|
||||||
'der', { partial: false });
|
|
||||||
print(extensions.freshestCRL);
|
|
||||||
break;
|
|
||||||
// Unknown Extension (not documented anywhere, probably non-standard)
|
|
||||||
default:
|
|
||||||
extensions.unknown.push(ext);
|
|
||||||
extensions.standardUnknown.push(ext);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// id-pe extensions - Private Internet Extensions
|
|
||||||
if (eid.length === 8
|
|
||||||
&& eid[0] === 1
|
|
||||||
&& eid[1] === 3
|
|
||||||
&& eid[2] === 6
|
|
||||||
&& eid[3] === 1
|
|
||||||
&& eid[4] === 5
|
|
||||||
&& eid[5] === 5
|
|
||||||
&& eid[6] === 7) {
|
|
||||||
switch (eid[3]) {
|
|
||||||
// Authority Information Access
|
|
||||||
// id-pe:
|
|
||||||
case 1:
|
|
||||||
print('Authority Information Access:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.authorityInformationAccess = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.authorityInformationAccess = rfc5280.AuthorityInformationAccess.decode(
|
|
||||||
extensions.authorityInformationAccess,
|
|
||||||
'der');
|
|
||||||
print(extensions.freshestCRL);
|
|
||||||
break;
|
|
||||||
// Subject Information Access
|
|
||||||
// id-pe:
|
|
||||||
case 11:
|
|
||||||
print('Subject Information Access:');
|
|
||||||
print(ext.extnValue);
|
|
||||||
extensions.subjectInformationAccess = ext.extnValue;
|
|
||||||
// parse
|
|
||||||
extensions.subjectInformationAccess = rfc5280.SubjectInformationAccess.decode(
|
|
||||||
extensions.subjectInformationAccess,
|
|
||||||
'der');
|
|
||||||
print(extensions.subjectInformationAccess);
|
|
||||||
break;
|
|
||||||
// Unknown Extension (not documented anywhere, probably non-standard)
|
|
||||||
default:
|
|
||||||
extensions.unknown.push(ext);
|
|
||||||
extensions.standardUnknown.push(ext);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions.unknown.push(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
var extensionsVerified = !extensions.unknown.filter(function(ext) {
|
var extensionsVerified = !extensions.unknown.filter(function(ext) {
|
||||||
return ext.critical;
|
return ext.critical;
|
||||||
}).length;
|
}).length;
|
||||||
|
@ -1115,6 +852,151 @@ rfc5280.SubjectInformationAccess = asn1.define('SubjectInformationAccess', funct
|
||||||
this.seqof(AccessDescription);
|
this.seqof(AccessDescription);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
rfc5280.extensions = {
|
||||||
|
standard: {
|
||||||
|
// id-ce extensions - Standard Extensions
|
||||||
|
prefix: [2, 5, 29],
|
||||||
|
35: 'Authority Key Identifier',
|
||||||
|
14: 'Subject Key Identifier',
|
||||||
|
// VERY IMPORTANT, especially is cA (basic constraints) is true (it is)
|
||||||
|
15: {
|
||||||
|
name: 'Key Usage',
|
||||||
|
parse: function(data, decoded, ext) {
|
||||||
|
data = data[0];
|
||||||
|
return {
|
||||||
|
digitalSignature: !!((data >> 0) & 1),
|
||||||
|
nonRepudiation: !!((data >> 1) & 1),
|
||||||
|
// nonRepudiation renamed to contentCommitment:
|
||||||
|
contentCommitment: !!((data >> 1) & 1),
|
||||||
|
keyEncipherment: !!((data >> 2) & 1),
|
||||||
|
dataEncipherment: !!((data >> 3) & 1),
|
||||||
|
keyAgreement: !!((data >> 4) & 1),
|
||||||
|
keyCertSign: !!((data >> 5) & 1),
|
||||||
|
cRLSign: !!((data >> 6) & 1),
|
||||||
|
encipherOnly: !!((data >> 7) & 1),
|
||||||
|
decipherOnly: !!((data >> 8) & 1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
32: 'Certificate Policies',
|
||||||
|
33: 'Policy Mappings',
|
||||||
|
17: 'Subject Alternative Name',
|
||||||
|
18: 'Issuer Alternative Name',
|
||||||
|
9: 'Subject Directory Attributes',
|
||||||
|
19: 'Basic Constraints',
|
||||||
|
30: 'Name Constraints',
|
||||||
|
36: 'Policy Constraints',
|
||||||
|
37: 'Extended Key Usage',
|
||||||
|
31: 'CRL Distribution Points',
|
||||||
|
54: 'Inhibit anyPolicy',
|
||||||
|
46: 'Freshest CRL',
|
||||||
|
// Unknown Extension (not documented anywhere, probably non-standard)
|
||||||
|
_: 'Unknown Extension'
|
||||||
|
},
|
||||||
|
// id-pe extensions - Private Internet Extensions
|
||||||
|
priv: {
|
||||||
|
prefix: [1, 3, 6, 1, 5, 5, 7],
|
||||||
|
1: 'Authority Information Access',
|
||||||
|
11: 'Subject Information Access',
|
||||||
|
// Unknown Extension (not documented anywhere, probably non-standard)
|
||||||
|
_: 'Unknown Extension'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(rfc5280.extensions).forEach(function(typeName) {
|
||||||
|
var type = rfc5280.extensions[typeName];
|
||||||
|
Object.keys(type).forEach(function(suffix) {
|
||||||
|
var id, prop, schemaName, schema, parse;
|
||||||
|
|
||||||
|
if (suffix === 'prefix')
|
||||||
|
return;
|
||||||
|
|
||||||
|
var prefix = type.prefix;
|
||||||
|
var name = type[suffix];
|
||||||
|
|
||||||
|
if (typeof name === 'object') {
|
||||||
|
var obj = name;
|
||||||
|
name = obj.name;
|
||||||
|
parse = obj.parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = prefix.concat(suffix).join('.');
|
||||||
|
|
||||||
|
if (/^[A-Z]+ /.test(name)) {
|
||||||
|
// CRL Distribution Points - > CRLDistributionPoints
|
||||||
|
prop = name.replace(/ /g, '');
|
||||||
|
} else {
|
||||||
|
prop = (name[0].toLowerCase()) + name.substring(1).replace(/ /g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaName = name.replace(/ /g, '');
|
||||||
|
schema = rfc5280[schemaName];
|
||||||
|
|
||||||
|
rfc5280.extensions[id] = {
|
||||||
|
typeName: typeName,
|
||||||
|
prefix: prefix,
|
||||||
|
suffix: suffix,
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
prop: prop,
|
||||||
|
schemaName: schemaName,
|
||||||
|
schema: schema,
|
||||||
|
parse: parse
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
rfc5280.parseExtensions = function(tbsCertificate, options) {
|
||||||
|
var edata, eid, ext, decoded, data;
|
||||||
|
|
||||||
|
var output = {};
|
||||||
|
var unknown = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < tbsCertificate.extensions.length; i++) {
|
||||||
|
edata = tbsCertificate.extensions[i];
|
||||||
|
eid = edata.extnID.join('.');
|
||||||
|
|
||||||
|
if (ext = rfc5280.extensions[eid]) {
|
||||||
|
// Parse Extension
|
||||||
|
decoded = ext.schema.decode(edata.extnValue, 'der', options);
|
||||||
|
|
||||||
|
// If the Extension needs extra parsing (i.e. bitstrs)
|
||||||
|
data = ext.parse
|
||||||
|
? ext.parse(decoded.data, decoded, ext)
|
||||||
|
: decoded;
|
||||||
|
|
||||||
|
// Tack on some useful info
|
||||||
|
|
||||||
|
// Comment for debugging:
|
||||||
|
// data._edata = edata;
|
||||||
|
// data._ext = ext;
|
||||||
|
|
||||||
|
if (ext.parse) {
|
||||||
|
data._decoded = decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our decoded extension to the output
|
||||||
|
output[ext.prop] = data;
|
||||||
|
|
||||||
|
// XXX Debug
|
||||||
|
print('------------');
|
||||||
|
print('%s (%s):', ext.name, ext.id);
|
||||||
|
print('Buffer:');
|
||||||
|
print(edata.extnValue);
|
||||||
|
print('Extension:');
|
||||||
|
print(data);
|
||||||
|
} else {
|
||||||
|
// Add unknown extension:
|
||||||
|
output['Unkown_' + unknown] = edata;
|
||||||
|
|
||||||
|
// XXX Debug
|
||||||
|
print('Unknown extension: %s', eid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug
|
* Debug
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue